Summary
Feeding mistune.html() a string of unbalanced square brackets makes the call run in quadratic time. About 25 KB of brackets stalls a worker past 15 seconds on a Mac M2. 50 KB drags it out to roughly two minutes. Any server-side caller of mistune.html() on markdown an attacker can influence can be wedged.
Details
The payload is [ times N, then a single x, then ] times N. Every [ opens a parse_link attempt. parse_link calls parse_link_text in helpers.py, which forward-scans the rest of the source balancing brackets. The text ends up not being a link (no (url) after the closing bracket), the outer inline loop bumps the cursor by one character, and the next [ does the whole scan again. N full scans of an N-character input. Profile on N=1600 shows parse_link_text called 1599 times with about 2.56 million re.Pattern.search calls underneath, which is N² right on the money.
Tested on 3.2.0, then 3.2.1, then main HEAD. Identical on all three. The 3.2.1 release notes don't touch parse_link_text.
This isn't GHSA-hjph-f4mc-wx4c / CVE-2026-33441 from the May batch. That one's parse_link_title reached via malformed [label]: reference definitions with escaped brackets.
Suggested fix: cap how far parse_link_text scans forward. The existing LINK_LABEL = r"(?:[^\\\[\]]|\.){0,500}" already does this kind of thing. Optimising failed start positions for a given source would also work but the cap is the one-line option and matches code already in the file.
PoC
pip install mistune==3.2.1
import mistune, time
for n in (400, 800, 1600, 3200, 6400, 12800):
md = '[' * n + 'x' + ']' * n
t = time.perf_counter()
mistune.html(md)
print(n, len(md), f'{(time.perf_counter()-t)*1000:.0f} ms')
Output, Python 3.14, mistune 3.2.1:
400 801 35 ms
800 1601 130 ms
1600 3201 501 ms
3200 6401 2003 ms
6400 12801 8066 ms
12800 25601 killed at >15000 ms
Doubling N quadruples the time.
Impact
Denial of service. CWE-407 inefficient algorithmic complexity.
Anyone calling mistune.html() on input an attacker can influence. Web forms, comment fields, federated content, scheduled jobs pulling markdown from an external feed. Our own use is a Celery task rendering markdown from a first-party source so direct exposure for us is low, but the bug class hits every downstream putting user-facing markdown through mistune.
Summary
Feeding mistune.html() a string of unbalanced square brackets makes the call run in quadratic time. About 25 KB of brackets stalls a worker past 15 seconds on a Mac M2. 50 KB drags it out to roughly two minutes. Any server-side caller of mistune.html() on markdown an attacker can influence can be wedged.
Details
The payload is [ times N, then a single x, then ] times N. Every [ opens a parse_link attempt. parse_link calls parse_link_text in helpers.py, which forward-scans the rest of the source balancing brackets. The text ends up not being a link (no (url) after the closing bracket), the outer inline loop bumps the cursor by one character, and the next [ does the whole scan again. N full scans of an N-character input. Profile on N=1600 shows parse_link_text called 1599 times with about 2.56 million re.Pattern.search calls underneath, which is N² right on the money.
Tested on 3.2.0, then 3.2.1, then main HEAD. Identical on all three. The 3.2.1 release notes don't touch parse_link_text.
This isn't GHSA-hjph-f4mc-wx4c / CVE-2026-33441 from the May batch. That one's parse_link_title reached via malformed [label]: reference definitions with escaped brackets.
Suggested fix: cap how far parse_link_text scans forward. The existing LINK_LABEL = r"(?:[^\\\[\]]|\.){0,500}" already does this kind of thing. Optimising failed start positions for a given source would also work but the cap is the one-line option and matches code already in the file.
PoC
Output, Python 3.14, mistune 3.2.1:
400 801 35 ms
800 1601 130 ms
1600 3201 501 ms
3200 6401 2003 ms
6400 12801 8066 ms
12800 25601 killed at >15000 ms
Doubling N quadruples the time.
Impact
Denial of service. CWE-407 inefficient algorithmic complexity.
Anyone calling mistune.html() on input an attacker can influence. Web forms, comment fields, federated content, scheduled jobs pulling markdown from an external feed. Our own use is a Celery task rendering markdown from a first-party source so direct exposure for us is low, but the bug class hits every downstream putting user-facing markdown through mistune.