CVE-2026-59928: Mistune block_parser Quadratic-time Parsing On Long Lists Vulnerability

HIGH
Advisory Identifier: GHSA-ffq3-xpv3-j92q
CVSS 7.5
CWE-1333 CWE-407
Published
Advisory Summary

CVE-2026-59928 is a HIGH (CVSS 7.5) quadratic-time parsing on long lists vulnerability (CWE-1333: Inefficient Regular Expression Complexity) affecting Mistune block_parser libraries in the PyPI ecosystem. The issue involves quadratic-time parsing on long lists of repeated reference-link definitions. Remediation is available by upgrading affected packages to version 3.3.0.

CVE-2026-59928 at a Glance

CVE Identifier CVE-2026-59928
GHSA Identifier GHSA-ffq3-xpv3-j92q
Severity HIGH
CVSS v3.1 Base Score 7.5
CWE Classification CWE-1333: Inefficient Regular Expression Complexity
Affected Product Mistune block_parser
Package Ecosystem PyPI
Published Date
Last Updated

Security Impact

CVSS: 7.5

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required None (PR:N)
User Interaction None (UI:N)
Scope Unchanged (S:U)
Confidentiality None (C:N)
Integrity None (I:N)
Availability High (A:H)

How to Fix CVE-2026-59928

To remediate this vulnerability, upgrade all affected Mistune block_parser dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

mistune fixed: 3.3.0
pip install mistune>=3.3.0
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

pip show mistune

How to Check if You Are Affected

To determine whether your Python environment is affected by CVE-2026-59928, inspect your installed packages using pip:

pip show mistune

Analyze the terminal output to locate any version of Mistune block_parser in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 3.3.0), your application is affected.

Note: The pip show mistune command checks currently installed packages in your active Python environment.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-1333: Inefficient Regular Expression Complexity, where the product uses a regular expression with a worst-case computational complexity that is inefficient and possibly exponential. In Mistune block_parser, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## Summary **Type:** Algorithmic-complexity DoS in reference-link definition handling. A markdown document with N reference-link definitions of the same key (or many distinct keys) takes O(N²) parser time. 5000 repeated `[a]: u\n` definitions take ~1.1 second; 10000 → ~4.5 seconds. **File:** `src/mistune/block_parser.py` (reference-link def parsing) and the surrounding `ref_links` env-dictionary handling. **Root cause:** every reference definition is parsed by scanning forward from each candidate position. The `unikey` normalisation runs per-def, the dictionary insert is per-def, and the lookup-by-label-then-iterate-defs path is linear in the number of stored defs. For input with N defs, the total work is O(N²). ## Affected Code `src/mistune/block_parser.py` — reference-definition rule fires on every line that matches `[label]: url`. For each one: - `unikey(label)` is called (linear scan of the label). - The def is appended to `state.env['ref_links']`. - Later inline-link resolution looks up by `unikey(label)` in the dict (O(1)) but the surrounding parser revisits the def list for paragraph-vs-def disambiguation. The cumulative parse time grows as the square of the number of defs. **Why it's wrong:** the parser does not amortise the def-list scan. A single forward pass with a hash-keyed dict (already in place) plus a per-line classifier should make this O(N). ## Exploit Chain 1. Application uses mistune to render attacker-supplied markdown. No plugins required. 2. Attacker submits a 35 KB document of `[a]: u\n` repeated 5000 times followed by `[click][a]`. 3. CPU pegs for ~1.1 seconds. 10000 defs → ~4.5 s. 20000 → ~18 s. Doubling input quadruples time. ## Security Impact **Attacker capability:** small input → large CPU. Predictable scaling. Can be repeated. **Preconditions:** application uses `mistune.create_markdown()` (default config) on attacker-supplied markdown. Worth noting: the `ref_links` dictionary persists for the lifetime of the parse, so a long document with many defs builds up memory; with N defs of attacker-chosen length, the per-def normalisation cost compounds. **Differential:** PoC-verified against mistune@3.2.1, default config: ```python import mistune, time md = mistune.create_markdown() for n in [1000, 2000, 5000, 10000]: s = '[a]: u\n' * n + '[click][a]' t = time.time() md(s) print(f' ref defs * {n} ({len(s)}b): {(time.time() - t) * 1000:.0f}ms') # Output (Python 3.13, Linux, 2.5GHz CPU): # ref defs * 1000 ( 7012b): 46ms # ref defs * 2000 (14012b): 186ms # ref defs * 5000 (35012b): 1121ms # ref defs * 10000 (70012b): 4400ms ``` The patched build (with the surrounding parser amortised to O(N)) keeps the time linear. ## Suggested Fix Replace the per-def re-scan with a single forward pass that classifies each line into `ref_def | paragraph | other` once and only inserts into `ref_links` once per def. The dict already exists; the wasted work is in the surrounding scan loop, not in the dict operations. A regression test asserting that `md('[a]: u\n' * 50_000 + '[click][a]')` completes in under 1 second would catch any regression.

CVE-2026-59928 Affected Mistune block_parser Versions

1 component(s)
Fixed Versions:
3.3.0
Product / Package Ecosystem Affected Versions Fixed Version
mistune
PyPI
< 3.3.0
3.3.0

References

Frequently Asked Questions

What is CVE-2026-59928?
CVE-2026-59928 is a security vulnerability in Mistune block_parser involving quadratic-time parsing on long lists of repeated reference-link definitions under CWE-1333: Inefficient Regular Expression Complexity.
Which Mistune block_parser versions are affected?
Affected versions include releases prior to 3.3.0 across affected packages: mistune.
What version fixes CVE-2026-59928?
Upgrading to 3.3.0 (or the latest patched release for your respective branch) resolves the vulnerability.
Is mistune affected?
Yes, mistune is affected in releases prior to 3.3.0. Upgrade your dependency to 3.3.0 or later.
What is the CVSS score of CVE-2026-59928?
CVE-2026-59928 has a CVSS v3.1 base score of 7.5 (HIGH) with vector string CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H.
What CWE is associated with CVE-2026-59928?
CVE-2026-59928 is classified under CWE-1333: Inefficient Regular Expression Complexity (The product uses a regular expression with a worst-case computational complexity that is inefficient and possibly exponential.).
How can I check PyPI dependencies for CVE-2026-59928?
Run the dependency tree command: 'pip show mistune'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-ffq3-xpv3-j92q",
  "modified" : "2026-07-20T21:24:18Z",
  "published" : "2026-07-20T21:24:18Z",
  "aliases" : [ "CVE-2026-59928" ],
  "summary" : "Mistune block_parser: quadratic-time parsing on long lists of repeated reference-link definitions",
  "details" : "## Summary\n\n**Type:** Algorithmic-complexity DoS in reference-link definition handling. A markdown document with N reference-link definitions of the same key (or many distinct keys) takes O(N²) parser time. 5000 repeated `[a]: u\\n` definitions take ~1.1 second; 10000 → ~4.5 seconds.\n**File:** `src/mistune/block_parser.py` (reference-link def parsing) and the surrounding `ref_links` env-dictionary handling.\n**Root cause:** every reference definition is parsed by scanning forward from each candidate position. The `unikey` normalisation runs per-def, the dictionary insert is per-def, and the lookup-by-label-then-iterate-defs path is linear in the number of stored defs. For input with N defs, the total work is O(N²).\n\n## Affected Code\n\n`src/mistune/block_parser.py` — reference-definition rule fires on every line that matches `[label]: url`. For each one:\n- `unikey(label)` is called (linear scan of the label).\n- The def is appended to `state.env['ref_links']`.\n- Later inline-link resolution looks up by `unikey(label)` in the dict (O(1)) but the surrounding parser revisits the def list for paragraph-vs-def disambiguation.\n\nThe cumulative parse time grows as the square of the number of defs.\n\n**Why it's wrong:** the parser does not amortise the def-list scan. A single forward pass with a hash-keyed dict (already in place) plus a per-line classifier should make this O(N).\n\n## Exploit Chain\n\n1. Application uses mistune to render attacker-supplied markdown. No plugins required.\n2. Attacker submits a 35 KB document of `[a]: u\\n` repeated 5000 times followed by `[click][a]`.\n3. CPU pegs for ~1.1 seconds. 10000 defs → ~4.5 s. 20000 → ~18 s. Doubling input quadruples time.\n\n## Security Impact\n\n**Attacker capability:** small input → large CPU. Predictable scaling. Can be repeated.\n**Preconditions:** application uses `mistune.create_markdown()` (default config) on attacker-supplied markdown. Worth noting: the `ref_links` dictionary persists for the lifetime of the parse, so a long document with many defs builds up memory; with N defs of attacker-chosen length, the per-def normalisation cost compounds.\n**Differential:** PoC-verified against mistune@3.2.1, default config:\n\n```python\nimport mistune, time\nmd = mistune.create_markdown()\nfor n in [1000, 2000, 5000, 10000]:\n    s = '[a]: u\\n' * n + '[click][a]'\n    t = time.time()\n    md(s)\n    print(f'  ref defs * {n} ({len(s)}b): {(time.time() - t) * 1000:.0f}ms')\n\n# Output (Python 3.13, Linux, 2.5GHz CPU):\n#   ref defs *  1000  ( 7012b):    46ms\n#   ref defs *  2000 (14012b):   186ms\n#   ref defs *  5000 (35012b):  1121ms\n#   ref defs * 10000 (70012b):  4400ms\n```\n\nThe patched build (with the surrounding parser amortised to O(N)) keeps the time linear.\n\n## Suggested Fix\n\nReplace the per-def re-scan with a single forward pass that classifies each line into `ref_def | paragraph | other` once and only inserts into `ref_links` once per def. The dict already exists; the wasted work is in the surrounding scan loop, not in the dict operations.\n\nA regression test asserting that `md('[a]: u\\n' * 50_000 + '[click][a]')` completes in under 1 second would catch any regression.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "PyPI",
      "name" : "mistune"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "3.3.0"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/lepture/mistune/security/advisories/GHSA-ffq3-xpv3-j92q"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-59928"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/lepture/mistune/commit/2b04d7ba341c16ac78fe82d3076bdd5c3de87c69"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/lepture/mistune"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/lepture/mistune/releases/tag/v3.3.0"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/pypa/advisory-database/tree/main/vulns/mistune/PYSEC-2026-2216.yaml"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-1333", "CWE-407" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-07-20T21:24:18Z",
    "nvd_published_at" : "2026-07-08T17:17:28Z"
  }
}