Skip to content

fix(comark): prevent HTML attribute injection in the stringifier - #372

Open
atinux wants to merge 4 commits into
mainfrom
fix/html-attribute-escaping
Open

fix(comark): prevent HTML attribute injection in the stringifier#372
atinux wants to merge 4 commits into
mainfrom
fix/html-attribute-escaping

Conversation

@atinux

@atinux atinux commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

What

Escapes &, ", < and > in every attribute value emitted by htmlAttributes(), drops attribute names outside the safe HTML charset, looks up node handlers with Object.hasOwn, sanitizes footnote labels used in href/id, and stops code-fence languages at quotes/angle brackets.

Why

A security scan found that any attribute value containing a double quote (raw HTML, component props, fence info strings, image alt text, footnote labels) broke out of key="value" at serialization time and injected attacker-chosen attributes such as onmouseover into the rendered HTML — even with comark/plugins/security enabled, because that plugin sanitizes the AST but cannot fix quote breakout at the string sink. Output encoding belongs in the serializer (per-renderer), since the same AST feeds the DOM-based Vue/React/Svelte/Angular renderers where escaping in the tree would double-encode; a plugin-side-only fix was considered and rejected for that reason. Also fixes a prototype-chain lookup where a ::constructor component resolved Object.prototype.constructor as its handler (XSS, and __proto__/valueOf aborted the render).


🤖 Prepared by an AI agent (OpenCode) from a security-audit findings list; commits are signed by the repository owner's key.

atinux added 4 commits August 20, 2026 19:47
Attribute values were interpolated into key="value" with no escaping,
so a double quote in any attribute value (raw HTML, component props,
fence info strings, image alt text) terminated the attribute early and
injected attacker-chosen attributes such as onmouseover handlers into
the rendered HTML. This bypassed comark/plugins/security, which
sanitizes the AST but cannot prevent quote breakout at serialization.

- Escape &, ", < and > in all attribute values emitted by
  htmlAttributes()
- Serialize object values as entity-escaped JSON (backslash escaping is
  not valid inside HTML attribute values)
- Drop attribute names outside the safe HTML name charset
- Update SPEC expectations that encoded the broken output
Handler maps are plain object literals, so a markdown component named
`constructor` resolved Object.prototype.constructor through the
prototype chain and was invoked as the node handler, emitting child
text without escaping. Names like `__proto__` or `valueOf` instead
threw and aborted the whole render.

Look up user and default handlers with Object.hasOwn so only real
handlers match.
Footnote labels were interpolated raw into href="#fn-<label>" and
id="fnref-<label>". A label containing a double quote broke out of
the attribute when rendered to HTML, injecting event-handler
attributes.

Encode any character outside [a-zA-Z0-9_-] as -<hex>- so references
and definitions still match and the emitted values stay inert.
The fence info language regex admitted quotes and angle brackets even
though the value lands in the language attribute and language-* class
of the rendered HTML. Stop the language at those characters; any
remainder still parses as meta.
@atinux
atinux requested a review from farnabaz as a code owner August 20, 2026 18:21
@vercel

vercel Bot commented Aug 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
comark Ready Ready Preview Aug 20, 2026 6:25pm
comark-json-render Building Building Preview Aug 20, 2026 6:25pm
comark-nextjs Ready Ready Preview Aug 20, 2026 6:25pm
comark-nuxt Ready Ready Preview Aug 20, 2026 6:25pm
comark-svelte Building Building Preview Aug 20, 2026 6:25pm
comark-sveltekit Ready Ready Preview Aug 20, 2026 6:25pm
comark-twoslash Ready Ready Preview Aug 20, 2026 6:25pm
comark-vue Building Building Preview Aug 20, 2026 6:25pm

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