Skip to content

markdown-it-myst: target renderer interpolates label into HTML unescaped #60

@mmcky

Description

@mmcky

Found by Copilot review on sync PR #59; the code is upstream's (vendored into markdown-it-myst by jupyter-book#2953, previously in markdown-it-myst-extras). Per our sync-PR policy this is tracked as an issue rather than patched in the merge.

Problem

render_myst_target in packages/markdown-it-myst/src/block.ts interpolates the target label directly into HTML:

const label = token.content;
const target = `<a href="#${label}">(${label})=</a>`;

while TARGET_PATTERN explicitly admits < and > in labels:

const TARGET_PATTERN = /^\((?<label>[a-zA-Z0-9|@<>*./_\-+:]{1,100})\)=\s*$/;

So a target like (<b>x</b>)= injects markup into the rendered output. The file's own escapeHtml helper is used by render_myst_line_comment ten lines above but not here.

Severity

Low: quotes are excluded from the label pattern (no attribute breakout), and this markdown-it HTML render path is used for previews/demos — the main mystmd pipeline goes through tokensToMyst, which is unaffected. Markup injection is possible; full XSS is constrained by the limited character class. Still, the fix is one line (escapeHtml(label) for the text, plus encoding for the fragment), and the helper already exists.

Action

Good first upstream report/PR to jupyter-book/mystmd — not fork-blocking. If we touch markdown-it-myst again for fork work, fix it in passing and add it to the cite-code-span upstream candidate's orbit in UPSTREAM-PRS.yml.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions