VAITP Dataset

← Back to the dataset

CVE-2026-59922

Mistune formatting plugins are vulnerable to DoS via CPU exhaustion.

  • CVSS 7.5
  • CWE-407
  • Resource Management
  • Remote

Mistune is a Python Markdown parser with renderers and plugins. Prior to 3.3.0, a run of closed tilde, equals-sign, or caret marker pairs around a character causes quadratic work in src/mistune/plugins/formatting.py when the strikethrough, mark, or insert plugin scans for matching markers from each possible start position, allowing denial of service through CPU exhaustion. This issue is fixed in version 3.3.0.

CVSS base score
7.5
Published
2026-07-08
OWASP
A04 Insecure Design
Orthogonal defect classification
Algorithm
Code defect classification
Incorrect Algorithm
Category
Resource Management
Subcategory
Resource Exhaustion
Accessibility scope
Remote
Impact
Denial of Service (DoS)
Affected component
Mistune
Fixed by upgrading
Yes

Solution

Upgrade Mistune to version 3.3.0 or later.

Vulnerable code sample

def vulnerable_parser(text, marker="~~"):
    """
    This function is a simplified representation of the vulnerable logic.
    For each potential starting marker, it scans the rest of the string
    from that point to find a closing marker, leading to quadratic complexity.
    """
    i = 0
    marker_len = len(marker)
    text_len = len(text)
    
    # Outer loop iterates through the text
    while i < text_len:
        # Check if the current position is a starting marker
        if text[i:i + marker_len] == marker:
            
            # If a starting marker is found, start a new scan for the closing marker
            # This nested search causes the O(n^2) behavior on crafted input
            j = i + marker_len
            found_end = False
            while j < text_len:
                if text[j:j + marker_len] == marker:
                    # Closing marker found. In a real parser, content would be processed.
                    # We advance the main index 'i' past this entire section.
                    i = j + marker_len
                    found_end = True
                    break
                j += 1
            
            # If no closing marker was found, treat the start as literal text
            if not found_end:
                i += marker_len
        else:
            # Not a marker, advance by one character
            i += 1

# Craft a payload that triggers the quadratic behavior.
# A long string of opening markers will cause many nested scans.
# For example, with "~~" * 10, the first "~~" will cause a scan over the next 9 pairs.
# The second "~~" will cause a scan over the next 8 pairs, and so on.
# A value of 20000 or higher will cause significant CPU load for many seconds.
payload = "~~" * 20000 + "vulnerable text"

# This call will hang for a long time due to CPU exhaustion,
# demonstrating the denial-of-service vulnerability.
vulnerable_parser(payload)

Patched code sample

def _fixed_parse_formatting(text, token_type, marker):
    """
    This function demonstrates the logic used to fix the denial-of-service
    vulnerability described in CVE-2026-59922 for the Mistune library.

    The vulnerability was caused by a quadratic-time (O(n^2)) complexity issue.
    The old implementation could repeatedly re-scan the string when processing
    inputs with many formatting markers (e.g., '~~a~~b~~c~~...'), leading to
    CPU exhaustion.

    This fixed implementation avoids that vulnerability by performing a single,
    linear-time (O(n)) scan of the string. It uses `text.find()` to locate
    markers and manually advances a position index (`pos`), ensuring that each
    part of the string is processed only once.
    """
    tokens = []
    pos = 0
    marker_len = len(marker)

    while pos < len(text):
        # Find the next opening marker from the current position. `text.find()`
        # is highly optimized and is a key part of the linear scan.
        start_pos = text.find(marker, pos)

        if start_pos == -1:
            # No more markers found; add the remainder of the text and exit.
            if pos < len(text):
                tokens.append(('text', text[pos:]))
            break

        # Capture any plain text located before this marker.
        if start_pos > pos:
            tokens.append(('text', text[pos:start_pos]))

        # Find the corresponding closing marker. The search starts immediately
        # after the opening marker's position.
        end_pos = text.find(marker, start_pos + marker_len)

        if end_pos == -1:
            # No closing marker was found, so the opening marker and the rest
            # of the string are treated as plain text.
            tokens.append(('text', text[start_pos:]))
            break

        # Extract the content between the markers.
        content = text[start_pos + marker_len : end_pos]

        # Check if content is valid (not empty or just whitespace).
        if content and not content.isspace():
            tokens.append((token_type, content))
            # The crucial step: advance the main position (`pos`) to the end of
            # the just-processed token. This prevents re-scanning this segment.
            pos = end_pos + marker_len
        else:
            # If content is invalid, treat the opening marker as plain text
            # and continue scanning from the character right after it.
            tokens.append(('text', text[start_pos : start_pos + marker_len]))
            pos = start_pos + marker_len

    return tokens

Payload

'~~' * 20000 + 'a' + '~~'

Cite this entry

@misc{vaitp:cve202659922,
  title        = {{Mistune formatting plugins are vulnerable to DoS via CPU exhaustion.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-59922},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-59922/}}
}
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 ::