VAITP Dataset

← Back to the dataset

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.

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 s

Payload

# " 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 ::