Skip to content

fix(security): add rehype-sanitize to prevent stored XSS in chat messages#4115

Open
0xcucumbersalad wants to merge 1 commit into
mainfrom
fix/markdown-xss-rehype-sanitize
Open

fix(security): add rehype-sanitize to prevent stored XSS in chat messages#4115
0xcucumbersalad wants to merge 1 commit into
mainfrom
fix/markdown-xss-rehype-sanitize

Conversation

@0xcucumbersalad

@0xcucumbersalad 0xcucumbersalad commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Critical: Stored XSS via chat message markdown — session hijacking.

The chat markdown renderer (markdown.tsx:132) used rehype-raw to parse inline HTML but had no sanitizer. An attacker could store an <iframe srcdoc="<script>..."> payload in a thread message's part.text field. When any org member viewed the thread:

  1. ReactMarkdown + rehypeRaw parses the raw HTML into DOM elements
  2. <iframe srcdoc="..."> is rendered — no component override blocks it
  3. srcdoc iframes inherit the parent's origin
  4. Embedded script calls /api/auth/get-session with credentials → raw session token
  5. Full account takeover

Fix

Add rehype-sanitize (GitHub's default schema) after rehype-raw in the plugin pipeline:

- const rehypePluginsMemo = [rehypeRaw];
+ const rehypePluginsMemo = [rehypeRaw, rehypeSanitize];

The default schema strips iframe, script, object, embed, form, and dangerous attributes (srcdoc, on*) while preserving safe HTML that LLMs produce (tables, <details>, <summary>, inline formatting).

Test plan

  • bun run fmt — passes
  • bun run lint — passes (0 errors)
  • bun run check — passes (pre-existing NATS type errors only)
  • Verify LLM-generated HTML tables still render correctly
  • Verify <details>/<summary> blocks still work
  • Verify <iframe srcdoc> payloads are stripped from rendered messages
  • Verify <script> tags in messages are stripped
  • Verify inline on* event handlers in messages are stripped

🤖 Generated with Claude Code


Summary by cubic

Fix stored XSS in chat message rendering by sanitizing inline HTML. Adds rehype-sanitize after rehype-raw to block dangerous tags/attributes and prevent session hijacking.

  • Bug Fixes
    • Sanitize chat markdown using the GitHub schema; strips iframe, script, object, embed, form, srcdoc, and on* handlers.
    • Keeps safe HTML like tables and details/summary working.

Written for commit 230ea86. Summary will update on new commits.

Review in cubic

…ages

The chat markdown renderer used rehype-raw to parse inline HTML in
messages but had no sanitizer. An attacker could inject an
<iframe srcdoc="<script>..."> payload into a thread message's
part.text field. Since srcdoc iframes inherit the parent origin,
the embedded script could call /api/auth/get-session with credentials
and exfiltrate the victim's session token — full account takeover.

Add rehype-sanitize (GitHub's default schema) after rehype-raw in the
plugin pipeline. This strips dangerous elements (iframe, script, object,
embed, form) and attributes (srcdoc, on*) while preserving safe HTML
that LLMs commonly produce (tables, details, summary, code blocks).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant