CVE-2026-44898
Mistune vulnerable to XSS in TOC rendering via unescaped heading text.
- CVSS 6.1
- CWE-79
- Input Validation and Sanitization
- Remote
Mistune is a Python Markdown parser with renderers and plugins. Prior to 3.2.1, 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 craft a heading whose text breaks out of the href="#…" attribute context, injecting arbitrary HTML tags including <script> blocks directly into the rendered TOC. This vulnerability is fixed in 3.2.1.
- CWE
- CWE-79
- CVSS base score
- 6.1
- Published
- 2026-05-26
- OWASP
- A03 Injection
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Input Validation and Sanitization
- Subcategory
- Cross-Site Scripting (XSS)
- Accessibility scope
- Remote
- Impact
- Arbitrary Code Execution
- Affected component
- Mistune
- Fixed by upgrading
- Yes
Solution
Upgrade Mistune to version 3.2.1 or later.
Vulnerable code sample
#!/usr/bin/env python3
def render_toc_ul(toc):
"""
A representative vulnerable function that renders a Table of Contents.
This function does not escape the 'id' or 'text' values, leading to
an HTML injection vulnerability.
"""
html = '<ul>\n'
for level, id_val, text_val in toc:
# The vulnerability is in the f-string below, which does not escape inputs.
# An attacker can inject HTML attributes or tags via id_val or text_val.
html += f' <li><a href="#{id_val}">{text_val}</a></li>\n'
html += '</ul>'
return html
# --- Demonstration of the exploit ---
if __name__ == '__main__':
# A legitimate heading
safe_heading = (1, 'safe-heading-id', 'This is a Safe Heading')
# A crafted, malicious heading.
# The 'id' value is crafted to break out of the href attribute
# and inject an 'onmouseover' event handler.
malicious_id = 'malicious-id" onmouseover="alert(\'XSS executed!\')'
malicious_heading = (1, malicious_id, 'Hover over me for a surprise')
# The list of headings to be rendered in the table of contents
table_of_contents_data = [
safe_heading,
malicious_heading,
]
# Generate the vulnerable HTML output
vulnerable_output = render_toc_ul(table_of_contents_data)
print("--- Generated Vulnerable HTML ---")
print(vulnerable_output)
print("\nTo see the exploit, save the output above as an HTML file and open it")
print("in a web browser. Hovering your mouse over the second link will")
print("trigger the JavaScript alert.")Patched code sample
import html
def fixed_render_toc_ul(toc):
"""
Represents the fixed version of the function, which safely renders
a table of contents by escaping user-controlled values.
Note: This is a simplified implementation that does not handle nested
lists to clearly demonstrate the security fix.
"""
s = "<ul>\n"
for _, item_id, text in toc:
# The fix: The 'id' and 'text' values are escaped before being
# inserted into the HTML string. This prevents malicious values
# from breaking out of the attribute or tag context.
safe_id = html.escape(item_id, quote=True)
safe_text = html.escape(text)
s += f'<li><a href="#{safe_id}">{safe_text}</a></li>\n'
s += "</ul>"
return sPayload
# " onmouseover="alert('XSS')
Cite this entry
@misc{vaitp:cve202644898,
title = {{Mistune vulnerable to XSS in TOC rendering via unescaped heading text.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-44898},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-44898/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
