T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/browser_modal.py:108
- Finding
- DOM-based cross-site scripting through unsanitized modal messages<![CDATA[ ## Vulnerability Details **File Location**: `scripts/browser_modal.py:108-133` **Vulnerability Type**: DOM-based cross-site scripting through unsafe `innerHTML` assignment **Risk Level**: High ### Vulnerable Code ```python # Escape JavaScript strings title_js = title.replace("\\", "\\\\").replace("'", "\\'").replace("\n", "\\n") message_js = message.replace("\\", "\\\\").replace("'", "\\'").replace("\n", "\\n") default_js = default_value.replace("\\", "\\\\").replace("'", "\\'") js_code = f''' (function() {{ # ... modal.querySelector('#openclaw-modal-title').textContent = '{title_js}'; modal.querySelector('#openclaw-modal-content').innerHTML = '{message_js}'.replace(/\\n/g, '<br>'); # ... }})(); ''' ``` ### Technical Analysis The modal message is derived from the command-line-controlled `message` argument. The implementation escapes selected JavaScript string characters, but it does not apply HTML escaping or sanitization before assigning the value to `innerHTML`. JavaScript string escaping and HTML sanitization address different parsing contexts. Consequently, an input containing HTML elements and event handlers can be parsed as active markup when the generated JavaScript is injected into a browser page. For example, a malicious message can contain an image element with an error handler. When the browser parses that message through `innerHTML`, the handler may execute in the security context of the active page. The title uses `textContent` and is not affected by this specific flaw. The message should use the same safe rendering approach. Exploitation requires the generated JavaScript to be executed in a browser page, as intended by the Skill's browser mode. Content Security Policy and the browser injection mechanism may constrain individual payloads, but they do not make the unsafe sink secure. ### Attack Path 1. An attacker causes untrusted content to be used as the modal `--message`, such as content copied from a ...[truncated 1502 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Replace the unsafe `innerHTML` assignment with `textContent`: ```javascript var content = modal.querySelector('#openclaw-modal-content'); content.textContent = message; content.style.whiteSpace = 'pre-wrap'; ``` 2. Preserve line breaks with CSS such as `white-space: pre-wrap` rather than converting newlines into `<br>` tags through HTML parsing. 3. If formatted HTML is an explicit product requirement, sanitize it with a well-maintained HTML sanitizer and a strict allowlist. Disallow scripts, event-handler attributes, dangerous URL schemes, embedded frames, and active SVG or MathML content. 4. Serialize Python values into JavaScript with `json.dumps()` rather than implementing partial JavaScript-string escaping manually. This protects the JavaScript-string context, although HTML sanitization or `textContent` is still required separately. 5. Treat modal titles, messages, defaults, and options as untrusted data regardless of whether they originate from command-line arguments, websites, files, or Agent-generated content. 6. Add security tests using payloads containing event handlers, closing tags, SVG constructs, quotes, backticks, Unicode line separators, and multiline content. Verify that all payloads are displayed literally and never interpreted as markup. ]]>
