T09 · Insecure Skill Coding Practices
Warning
- Location
- assets/index.html:408
- Finding
- DOM-Based Cross-Site Scripting Through Chat Input<![CDATA[ ## Vulnerability Details **File Location**: `assets/index.html`, lines 408-424 **Vulnerability Type**: DOM-based cross-site scripting **Risk Level**: Medium ### Vulnerable Code ```javascript function appendMsg(text, role) { const box = document.getElementById("chatBox"); const div = document.createElement("div"); div.className = `msg ${role}`; if (role === "sasa") { div.innerHTML = ` <img src="sasa_avatar.png" class="msg-avatar" alt="Assistant"> <div class="bubble">${text.replace(/\n/g, "<br>")}</div>`; } else { div.innerHTML = ` <div class="msg-avatar user-avatar">👤</div> <div class="bubble">${text}</div>`; } box.appendChild(div); box.scrollTop = box.scrollHeight; } ``` ### Technical Analysis The `appendMsg` function inserts the `text` parameter into an HTML template and assigns the result to `innerHTML`. No HTML escaping, sanitization, or element allowlisting is performed before this assignment. The value originates from the text input or the browser speech-recognition transcript. An attacker-controlled string containing HTML with event handlers can therefore create executable DOM nodes. For example, the following input can execute JavaScript when the browser attempts to load the invalid image: ```html <img src="invalid" onerror="alert(document.domain)"> ``` Assistant responses are also rendered through `innerHTML`. Several responses incorporate portions of the user's input, such as a navigation destination or search query. Consequently, removing only the vulnerable user-message branch would not fully address the problem. This is DOM-based XSS because the injection and execution occur entirely in client-side JavaScript without requiring a server response. ### Attack Path 1. A user opens `assets/index.html` in a browser. 2. An attacker causes malicious HTML to be entered into the chat field. This could occur through direct input, pasted content, or a transcript produced after the user acti ...[truncated 1310 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Replace `innerHTML` with DOM construction and `textContent` for every user-controlled value: ```javascript function appendMsg(text, role) { const box = document.getElementById("chatBox"); const message = document.createElement("div"); message.className = `msg ${role}`; if (role === "sasa") { const avatar = document.createElement("img"); avatar.src = "sasa_avatar.png"; avatar.className = "msg-avatar"; avatar.alt = "Assistant"; const bubble = document.createElement("div"); bubble.className = "bubble"; bubble.textContent = text; message.append(avatar, bubble); } else { const avatar = document.createElement("div"); avatar.className = "msg-avatar user-avatar"; avatar.textContent = "👤"; const bubble = document.createElement("div"); bubble.className = "bubble"; bubble.textContent = text; message.append(avatar, bubble); } box.appendChild(message); box.scrollTop = box.scrollHeight; } ``` 2. Preserve line breaks through CSS rather than converting them to HTML: ```css .bubble { white-space: pre-wrap; } ``` 3. If rich-text output is genuinely required, sanitize it with a maintained allowlist-based sanitizer such as DOMPurify. Disable event attributes, script-capable URL schemes, embedded frames, and active SVG or MathML content. 4. Apply a restrictive Content Security Policy as defense in depth. Avoid allowing inline scripts and inline event handlers. 5. Add regression tests covering HTML elements, event-handler attributes, encoded payloads, malformed markup, SVG payloads, and user-controlled values reflected through assistant responses. ]]>
