Skip to content

Escape language name in codehilite non-Pygments output - #1667

Open
naaz234 wants to merge 1 commit into
Python-Markdown:masterfrom
naaz234:codehilite-escape-lang-class
Open

naaz234 wants to merge 1 commit into
Python-Markdown:masterfrom
naaz234:codehilite-escape-lang-class

Conversation

@naaz234

@naaz234 naaz234 commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Description

The codehilite extension's non-Pygments output builds the <code> element's class attribute from the language name without escaping it. In CodeHilite.hilite, the fallback branch (reached when Pygments is not installed or use_pygments is False) does classes.append('{}{}'.format(self.lang_prefix, self.lang)) and then ' class="{}"'.

When codehilite is used together with attr_list, the language is taken from a fenced-block class token. The attr_list scanner allows any character in a class token except space, = and }, which includes ", < and >. So an input such as:

```{.a"><script>alert(1)</script>}
code
```

produces class="language-a"><script>alert(1)</script>", which closes the attribute and injects a live <script> element (stored XSS for anything rendering untrusted Markdown in that configuration). The language can also be set directly, e.g. CodeHilite('x', lang='"><script>', use_pygments=False).

The fenced_code extension already escapes the language in its own non-Pygments branch with _escape_attrib_html; this applies the same escaping at the one site in codehilite that missed it. Valid language names are unchanged. Added a regression test (runs without Pygments via use_pygments=False) and a changelog entry.

AI Assistance Disclosure

  • No AI tools were used in preparing this PR.

  • AI tools were used and I have disclosed which ones, and have fully reviewed and verified their output.

    AI tools were use to assist with the following:

    • Code
    • Code Comments
    • Documentation
    • Changelog Entry
    • Commit Message
    • The Description Above
    • Other: ______________

    Claude (Anthropic) was used to help locate the issue, draft the patch, the test, the changelog entry, the commit message, and this description. I reviewed and verified all of it, and can explain the change.

Checklist

When Pygments is unavailable (or use_pygments is False), CodeHilite.hilite built the code element's class attribute from the language name without escaping it. With attr_list enabled the language comes from a fenced-block class token, whose scanner permits ", < and >, so a value could close the class attribute and inject markup. fenced_code already escapes the language in its own non-Pygments branch; apply the same escaping in codehilite's fallback.
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