CVE-2026-76098: Mistune has Denial of Service — RecursionError via Excessive Emphasis Markers in Markdown
Summary Mistune v3.3.2 is vulnerable to a Denial of Service (DoS) attack via uncontrolled recursion in the HTML rendering of deeply-nested emphasis tokens. By submitting Markdown containing approximately 1,000 consecutive asterisk characters, an attacker causes the Python process to crash with RecursionError.
Details The InlineParser's processemphasisdelimiters() creates deeply nested <strong> tokens from consecutive emphasis markers (every 2 asterisks add one nesting level). With 1,000 consecutive asterisks, approximately 500 levels of nesting are produced. The HTMLRenderer.rendertoken() method (src/mistune/renderers/html.py:40-57) renders these tokens recursively: when a token has children, line 48 calls self.rendertokens(token['children'], state), entering child rendering. Each nesting level produces ~2 stack frames, so 500 levels ≈ 1,000 frames, exceeding Python's default recursion limit (sys.getrecursionlimit() = 1000). The emphasis() and strong() methods (lines 80-84) wrap recursively rendered child content in <em> and <strong> tags, perpetuating the recursion. This vulnerability affects all mistune APIs including markdown() and html().
Core vulnerable code path:
python src/mistune/renderers/html.py:40-57 def rendertoken(self, token: Dict[str, Any], state: BlockState) -> str: func = self.getmethod(token["type"]) attrs = token.get("attrs") if "raw" in token: text = token["raw"] elif "children" in token: text = self.rendertokens(token["children"], state) else: if attrs: return func(attrs) else: return func() if attrs: return func(text, attrs) else: return func(text)
The recursive call to rendertokens() on line 48 processes nested child tokens. With 500 levels of nested emphasis/strong tokens, this recursion exceeds Python's default recursion limit of 1000, causing a RecursionError.
python src/mistune/renderers/html.py:80-84 def emphasis(self, text: str) -> str: return "<em>" + text + "</em>"
def strong(self, text: str) -> str: return "<strong>" + text + "</strong>"
The emphasis() and strong() methods wrap their text content (which is itself the recursively-rendered output of nested child tokens) in HTML tags, creating the chain of recursion: each call to strong() includes rendered children that themselves call strong(), etc.
POC
wiki from mistune import html
payload = '' 1000 try: result = html(payload) print('No crash - recursion handled') except RecursionError as e: print(f'[VULN] RecursionError: {e} - Process would crash!') except Exception as e: print(f'Error: {type(e).name}: {e}')
<img width="1139" height="664" alt="1" src="https://github.com/user-attachments/assets/d2095b9f-0a6a-4bef-8013-cbc6946cf8f1" />
Impact CVSS 3.1: 7.5 (High) — AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H. An attacker can crash any server process using mistune with approximately 2KB of Markdown input. In web applications, this can be triggered through user-generated content such as forum posts or comments, causing denial of service for all concurrent users sharing the same Python process. Both mistune.markdown() and mistune.html() are affected.
Remediation 1. Add a maximum nesting depth limit in the emphasis parsing stage, similar to BlockParser's maxnestedlevel mechanism (currently at DEFAULTMAXNESTEDLEVEL = 20). When the limit is exceeded, treat excess emphasis markers as literal text. 2. Alternatively, refactor HTMLRenderer to use an explicit stack-based iterative approach instead of recursive calls for rendering nested tokens. 3. As a defense-in-depth measure, document the recursion risk and recommend that applications deploying mistune set a higher recursion limit or implement request-level timeouts.
Other sources
Mistune is a Python Markdown parser with renderers and plugins. Versions 3.3.0 through 3.3.2 are vulnerable to DoS through deeply nested tokens. HTML rendering creates deeply nested emphasis tokens from consecutive asterisk characters, and recursive rendering in HTMLRenderer.rendertoken() can exceed Python's recursion limit and raise RecursionError, allowing crafted Markdown to crash a parsing process. This issue is fixed in version 3.3.3
— NVD
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
pip/mistuneto a version that resolves this vulnerability.Fixed in 3.3.3 - Upgrade
Upgrade
mistuneto a version that resolves this vulnerability.Fixed in 3.3.3 - Compensating control
As a defense-in-depth measure, document the recursion risk for mistune.emphasis/strong HTML rendering and ensure applications deploying mistune set an appropriately higher recursion limit and/or implement request-level timeouts to mitigate RecursionError/DoS risk when rendering crafted Markdown.
Event History
Frequently Asked Questions
Who is exposed to this denial-of-service issue?
Applications using Mistune 3.3.0 through 3.3.2 to HTML-render Markdown are exposed when they process attacker-controlled Markdown. An unauthenticated remote attacker can supply crafted input to crash the parsing process.
What input is required to trigger the crash?
The attacker needs Markdown containing consecutive asterisk characters that produces deeply nested emphasis tokens. During HTML rendering, recursive token rendering can exceed Python's recursion limit and raise RecursionError.
Is upgrading available to remediate the issue?
Yes. The issue is fixed in Mistune version 3.3.3.