Advisories for Pypi/Mistune package

2026

Mistune: XSS via unescaped class option in Admonition directive

In src/mistune/directives/admonition.py, the render_admonition() function concatenates the :class: option directly into the HTML class attribute without escaping (lines 63-68). This allows attribute injection and XSS even when HTMLRenderer(escape=True) is used. The directive name parameter is safe (validated against whitelist), but the class option comes from raw user input.

Mistune toc / TableOfContents directive: heading IDs use predictable `toc_N` numbering with no slugification, allowing collision with attacker-controlled `id="toc_N"` content

Type: Predictable identifier generation. The toc plugin and TableOfContents directive both default to generating heading IDs of the form toc_1, toc_2, toc_3, … with no input-derived component. An attacker who can place a heading anywhere in the document can predict which toc_N ID it will receive, and can inject HTML elsewhere (in a non-heading context) that uses the same id="toc_N" to either (a) shadow the legitimate heading anchor, breaking same-page …

Mistune renderers/html.safe_url: HARMFUL_PROTOCOLS list misses legacy and chained schemes that historically chain to `javascript:` execution

Type: URL-scheme allowlist gap. The safe_url filter only blocks the four schemes javascript:, vbscript:, file:, data:. Several other schemes are accepted into rendered <a href="…"> and <img src="…"> tags despite being known XSS vectors in legacy or chain-handling browsers. The same gap applies to direct links, reference links, and autolinks. File: src/mistune/renderers/html.py, line 11-23 (HARMFUL_PROTOCOLS list). Root cause: the HARMFUL_PROTOCOLS tuple is a hardcoded, opt-out denylist of four entries. Browsers …

Mistune plugins/formatting: quadratic-time parsing on long runs of `~~x~~`, `==x==`, and `^^x^^` markers (strikethrough / mark / insert)

Type: Algorithmic-complexity denial of service. A run of N closed pairs x~~~~x… (or the analogous ==x== for mark, ^^x^^ for insert) causes O(N²) work in the formatting parser. With the strikethrough, mark, or insert plugin enabled, an 8 KB input pegs the CPU for ~4 seconds; 16 KB → ~17 seconds. File: src/mistune/plugins/formatting.py, lines 13-15 (the _STRIKE_END / _MARK_END / _INSERT_END patterns and their per-position scan). Root cause: for each …

Mistune inline_parser: quadratic-time parsing on long runs of `**x**` and `***x***` emphasis pairs

Type: Algorithmic-complexity DoS in core emphasis parsing. A long sequence of well-formed x (strong) or x (strong-emphasis combined) pairs causes O(N²) parser work. Distinct from the bracket-bomb DoS ([ repetition) and from the formatting-plugin DoS (~~/==/^^); this one fires on default-config mistune with no plugins required. File: src/mistune/inline_parser.py lines 41-48 (the EMPHASIS_END_RE family) and the surrounding emphasis dispatch. Root cause: for every opening run of *s the parser scans forward …

Mistune directives/include: mutual `.. include::` recursion crashes the renderer with `RecursionError`, denial of service via two attacker-controlled markdown files

Type: Uncontrolled recursion via mutual include. The Include directive checks for direct self-reference (a.md cannot include a.md), but does not detect indirect cycles. Two markdown files that include each other (a.md → includes b.md → includes a.md) cause unbounded recursion until Python's stack limit fires RecursionError. The exception propagates out of the renderer and crashes the calling code. File: src/mistune/directives/include.py, lines 33-37 (the self-include check is the only cycle-detection logic). …

Mistune block_parser: quadratic-time parsing on long lists of repeated reference-link definitions

Type: Algorithmic-complexity DoS in reference-link definition handling. A markdown document with N reference-link definitions of the same key (or many distinct keys) takes O(N²) parser time. 5000 repeated [a]: u\n definitions take ~1.1 second; 10000 → ~4.5 seconds. File: src/mistune/block_parser.py (reference-link def parsing) and the surrounding ref_links env-dictionary handling. Root cause: every reference definition is parsed by scanning forward from each candidate position. The unikey normalisation runs per-def, the dictionary …

Mistune TOC Anchor Injection XSS

render_toc_ul() builds a <ul> table-of-contents tree from a list of (level, id, text) tuples. Both the id value (used as href="#<id>") and the text value (used as the visible link label) are inserted into <a> tags via a plain Python format string — with no HTML escaping applied to either value. When heading IDs are derived from user-supplied heading text (the standard use-case for readable slug anchors), an attacker can …

Mistune Image Directive CSS Injection Vulnerability

The Image directive plugin validates the :width: and :height: options with a regex compiled as _num_re = re.compile(r"^\d+(?:.\d*)?"). This pattern is applied via re.match() (which anchors only at the start of the string, not the end). Any value that begins with one or more digits passes validation, regardless of what follows. When the validated value is not a plain integer, render_block_image() inserts it directly into a style="width:…;" or style="height:…;" attribute. …

Mistune Heading ID Attribute has Injection XSS

HTMLRenderer.heading() builds the opening <hN> tag by string-concatenating the id attribute value directly into the HTML — with no call to escape(), safe_entity(), or any other sanitisation function. A double-quote character " in the id value terminates the attribute, allowing an attacker to inject arbitrary additional attributes (event handlers, src=, href=, etc.) into the heading element. The default TOC hook assigns safe auto-incremented IDs (toc_1, toc_2, …) that never contain …

Mistune Math Plugin has an XSS Escape Bypass

The mistune math plugin renders inline math ($…$) and block math ($$…$$) by concatenating the raw user-supplied content directly into the HTML output without any HTML escaping. This occurs even when the parser is explicitly created with escape=True, which is supposed to guarantee that all user-controlled text is sanitised before reaching the DOM. The result is a silent contract violation: a developer who enables escape=True reasonably expects complete XSS protection, …

Mistune has XSS via unescaped figclass/figwidth in Figure directive

In src/mistune/directives/image.py, the render_figure() function concatenates figclass and figwidth options directly into HTML attributes without escaping (lines 152-168). This allows attribute injection and XSS even when HTMLRenderer(escape=True) is used, because these values bypass the inline renderer. Other attributes in the same file (src, alt, style) are properly escaped; figclass/figwidth were missed.

2022
2019
2017