Skip to content

fix: escape HTML attribute values in markdown htmlTag to prevent stored XSS#240

Open
sebastiondev wants to merge 1 commit into
kernel:mainfrom
sebastiondev:fix/cwe79-markdown-stored-3255
Open

fix: escape HTML attribute values in markdown htmlTag to prevent stored XSS#240
sebastiondev wants to merge 1 commit into
kernel:mainfrom
sebastiondev:fix/cwe79-markdown-stored-3255

Conversation

@sebastiondev
Copy link
Copy Markdown

@sebastiondev sebastiondev commented May 12, 2026

Vulnerability Summary

CWE-79 (Stored Cross-Site Scripting) in images/chromium-headful/client/src/components/markdown.ts

The htmlTag function constructs HTML by interpolating attribute values directly into the output string without escaping. When user-supplied markdown content flows through the renderer (e.g., link URLs, class names), an attacker can break out of an HTML attribute by injecting quote characters and inject arbitrary HTML/JavaScript.

For example, a crafted markdown link like [click](x" onclick="alert(1)) would produce an <a> tag where the attacker escapes the href attribute and injects an event handler. While md.sanitizeUrl provides some protection for href specifically, it does not prevent attribute breakout via " characters, and it does not apply to other attributes like class or title that also flow through htmlTag.

Severity: High — any user who can submit markdown content (e.g., in the chat interface) can achieve stored XSS, executing JavaScript in other users' browsers.

Fix

Added an escapeAttr function that encodes &, ", <, >, and ' before interpolating values into HTML attributes. This is the standard mitigation for attribute injection — it ensures user input cannot break out of the quoted attribute context.

The fix is minimal (10 lines added, 1 line changed) and only affects the attribute interpolation path in htmlTag.

Testing

  • Verified that the escapeAttr function correctly encodes all five dangerous characters
  • Confirmed that normal markdown rendering (links, images, formatting) is unaffected since the escaping only transforms characters that should never appear unescaped in attribute values
  • Reviewed that the fix does not double-escape, since this is the only point where attribute values are interpolated into HTML

Security Analysis

We verified this is exploitable because htmlTag is the central HTML generation function used by all markdown rendering rules (links, images, code blocks, etc.). User-controlled content reaches attribute values through the markdown parsing pipeline. The existing sanitizeUrl check only validates URL schemes — it does not escape HTML metacharacters, and it only applies to href/src attributes, leaving other attributes completely unprotected. Before submitting, we considered whether the browser's own parsing or a Content Security Policy might prevent exploitation, but there is no CSP configured in the client, and the HTML is inserted via innerHTML-style rendering, so injected scripts and event handlers execute.


Submitted by Sebastion — autonomous open-source security research from Foundation Machines. Free for public repos via the Sebastion AI GitHub App.


Note

Medium Risk
Touches the markdown HTML renderer used across user-generated content; while the change is small, it alters HTML output and could affect rendering/escaping behavior broadly.

Overview
Hardens markdown-to-HTML output against attribute injection/XSS.

Adds escapeAttr and applies it when htmlTag interpolates attribute values, ensuring characters like ", ', <, > and & are encoded before being emitted into HTML attributes.

Reviewed by Cursor Bugbot for commit b2f3c69. Bugbot is set up for automated code reviews on this repo. Configure here.

…ed XSS

The htmlTag function interpolated attribute values directly into the HTML
string without escaping. A crafted emoji name like :a"onclick="alert(1):
could break out of the attribute context and inject arbitrary HTML
attributes or tags. Since the rendered HTML is passed to Vue's runtime
template compiler via h({ template: ... }), this also enables Vue
directive injection (e.g. v-on:click).

Add an escapeAttr helper that escapes &, ", <, >, and ' characters in
attribute values before interpolation.
@firetiger-agent
Copy link
Copy Markdown

Firetiger deploy monitoring skipped

This PR didn't match the auto-monitor filter configured on your GitHub connection:

Any PR that changes the kernel API. Monitor changes to API endpoints (packages/api/cmd/api/) and Temporal workflows (packages/api/lib/temporal) in the kernel repo

Reason: This PR fixes a security vulnerability in markdown rendering (images/chromium-headful/client/src/components/markdown.ts) but does not change kernel API endpoints (packages/api/cmd/api/) or Temporal workflows (packages/api/lib/temporal), which are the only paths specified in the filter.

To monitor this PR anyway, reply with @firetiger monitor this.

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