{"id":"CVE-2026-59922","aliases":["AZL-92220","DEBIAN-CVE-2026-59922","EUVD-2026-42329","GHSA-c8j7-8cv4-2xmq","PYSEC-2026-2210","SUSE-SU-2026:22655-1","UBUNTU-CVE-2026-59922","openSUSE-SU-2026:21339-1"],"status":"published","title":"Mistune plugins/formatting: quadratic-time parsing on long runs of `~~x~~`, `==x==`, and `^^x^^` markers (strikethrough / mark / insert)","description":"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.","severity":{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-10","score":0.00644,"percentile":0.51209,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-1333","CWE-407"],"published":"2026-07-08T16:12:00Z","modified":"2026-10-28T02:31:00Z","fixes":[{"ecosystem":"PyPI","package":"mistune","fixed_in":["3.3.0"],"sources":["ghsa","osv"]},{"ecosystem":"Azure Linux:3","package":"python-mistune","fixed_in":["3.3.0-1"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"mistune","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3.3.0"}]}]},{"source":"osv","ecosystem":"Azure Linux:3","package":"python-mistune","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3.3.0-1"}]}]},{"source":"osv","ecosystem":"Debian:13","package":"mistune","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3.1.3-1","3.1.4-1","3.3.4-1","3.4.0-1"]},{"source":"osv","ecosystem":"Debian:14","package":"mistune","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3.1.3-1","3.1.4-1","3.3.4-1","3.4.0-1"]},{"source":"osv","ecosystem":"PyPI","package":"mistune","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3.3.0"}]}],"versions":["0.1.0","0.2.0","0.3.0","0.3.1","0.4","0.4.1","0.5","0.5.1","0.6","0.7","0.7.1","0.7.2","0.7.3","0.7.4","0.8","0.8.1","0.8.2","0.8.3","0.8.4","2.0.0","2.0.0a1","2.0.0a2","2.0.0a3","2.0.0a4","2.0.0a5","2.0.0a6","2.0.0rc1","2.0.1","2.0.2","2.0.3","2.0.4","2.0.5","2.1.0","3.0.0","3.0.0a1","3.0.0a2","3.0.0a3","3.0.0rc1","3.0.0rc2","3.0.0rc3","3.0.0rc4","3.0.0rc5","3.0.1","3.0.2","3.1.0","3.1.1","3.1.2","3.1.3","3.1.4","3.2.0","3.2.1"]},{"source":"osv","ecosystem":"PyPI","package":"mistune","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3.3.0"}]}],"versions":["0.1.0","0.2.0","0.3.0","0.3.1","0.4","0.4.1","0.5","0.5.1","0.6","0.7","0.7.1","0.7.2","0.7.3","0.7.4","0.8","0.8.1","0.8.2","0.8.3","0.8.4","2.0.0","2.0.0a1","2.0.0a2","2.0.0a3","2.0.0a4","2.0.0a5","2.0.0a6","2.0.0rc1","2.0.1","2.0.2","2.0.3","2.0.4","2.0.5","2.1.0","3.0.0","3.0.0a1","3.0.0a2","3.0.0a3","3.0.0rc1","3.0.0rc2","3.0.0rc3","3.0.0rc4","3.0.0rc5","3.0.1","3.0.2","3.1.0","3.1.1","3.1.2","3.1.3","3.1.4","3.2.0","3.2.1"]},{"source":"osv","ecosystem":"SUSE:Linux Enterprise Server 16.0","package":"python-mistune","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3.1.3-160000.5.1"}]}],"bundled":10},{"source":"osv","ecosystem":"SUSE:Linux Enterprise Server for SAP applications 16.0","package":"python-mistune","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3.1.3-160000.5.1"}]}],"bundled":10},{"source":"osv","ecosystem":"Ubuntu:16.04:LTS","package":"mistune","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.6-2","0.7.1-1"]},{"source":"osv","ecosystem":"Ubuntu:18.04:LTS","package":"mistune","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.7.4-1","0.8-1","0.8.1-1","0.8.3-2"]},{"source":"osv","ecosystem":"Ubuntu:20.04:LTS","package":"mistune","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.8.4-1","0.8.4-2"]},{"source":"osv","ecosystem":"Ubuntu:22.04:LTS","package":"mistune","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.8.4-4","0.8.4-5","2.0.0-1+really0.8.4-1"]},{"source":"osv","ecosystem":"Ubuntu:24.04:LTS","package":"mistune","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2.0.4-2","3.0.2-1"]},{"source":"osv","ecosystem":"Ubuntu:25.10","package":"mistune","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3.0.2-2","3.1.3-1"]},{"source":"osv","ecosystem":"Ubuntu:26.10","package":"mistune","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3.1.4-1"]},{"source":"osv","ecosystem":"Ubuntu:26.04:LTS","package":"mistune","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3.1.3-1","3.1.4-1"]},{"source":"osv","ecosystem":"openSUSE:Leap 16.0","package":"python-mistune","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3.1.3-160000.5.1"}]}],"bundled":10}],"products":[{"vendor":"lepture","product":"mistune"},{"vendor":"mistune_project","product":"mistune"}],"references":[{"url":"https://github.com/lepture/mistune/security/advisories/GHSA-c8j7-8cv4-2xmq","tags":["x_refsource_CONFIRM","exploit","Exploit","Vendor Advisory","advisory","web","evidence","report"]},{"url":"https://github.com/lepture/mistune/commit/96d0f57f8fe9eeb06bb4cff521962a27d7c402e7","tags":["x_refsource_MISC","Patch","fix","web"]},{"url":"https://github.com/lepture/mistune/releases/tag/v3.3.0","tags":["x_refsource_MISC","Release Notes","web","advisory"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-59922","tags":["web","advisory"]},{"url":"https://github.com/pypa/advisory-database/tree/main/vulns/mistune/PYSEC-2026-2210.yaml","tags":["web"]},{"url":"https://github.com/advisories/GHSA-c8j7-8cv4-2xmq"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/59xxx/CVE-2026-59922.json","tags":["advisory"]},{"url":"https://security-tracker.debian.org/tracker/CVE-2026-59922","tags":["advisory"]},{"url":"https://github.com/lepture/mistune","tags":["package"]},{"url":"https://www.suse.com/support/update/announcement/2026/suse-su-202622655-1/","tags":["advisory"]},{"url":"https://bugzilla.suse.com/1271082","tags":["report"]},{"url":"https://bugzilla.suse.com/1271117","tags":["report"]},{"url":"https://bugzilla.suse.com/1271119","tags":["report"]},{"url":"https://bugzilla.suse.com/1271121","tags":["report"]},{"url":"https://bugzilla.suse.com/1271125","tags":["report"]},{"url":"https://bugzilla.suse.com/1271127","tags":["report"]},{"url":"https://bugzilla.suse.com/1271128","tags":["report"]},{"url":"https://bugzilla.suse.com/1271131","tags":["report"]},{"url":"https://bugzilla.suse.com/1271132","tags":["report"]},{"url":"https://www.suse.com/security/cve/CVE-2026-44896","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-59922","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-59923","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-59924","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-59925","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-59926","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-59927","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-59928","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-59929","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-59930","tags":["web"]},{"url":"https://ubuntu.com/security/CVE-2026-59922","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-59922","tags":["report"]},{"url":"","tags":["advisory"]}],"sources":{"cve":[{"id":"CVE-2026-59922","title":"Mistune plugins/formatting: quadratic-time parsing on long runs of `~~x~~`, `==x==`, and `^^x^^` markers (strikethrough / mark / insert)","description":"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.","status":"published","published":"2026-07-08T16:12:00.545Z","modified":"2026-07-09T14:40:22.315Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high","assessor":"cna"}],"cwes":["CWE-407","CWE-1333"],"updated_at":"2026-11-02T14:19:58.944663Z"}],"euvd":[{"id":"EUVD-2026-42329","description":"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.","status":"published","published":"2026-07-08T16:12:00Z","modified":"2026-07-09T14:40:22Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"updated_at":"2026-11-02T18:20:04.126011Z"}],"ghsa":[{"id":"GHSA-c8j7-8cv4-2xmq","title":"Mistune plugins/formatting: quadratic-time parsing on long runs of `~~x~~`, `==x==`, and `^^x^^` markers (strikethrough / mark / insert)","description":"## Summary\n\n**Type:** Algorithmic-complexity denial of service. A run of N closed pairs `~~x~~~~x~~...` (or the analogous `==x==` for `mark`, `^^x^^` for `insert`) causes O(N²) work in the formatting parser. With the `strikethrough`, `mark`, or `insert` plugin enabled, an 8 KB input pegs the CPU for ~4 seconds; 16 KB → ~17 seconds. \n**File:** `src/mistune/plugins/formatting.py`, lines 13-15 (the `_STRIKE_END` / `_MARK_END` / `_INSERT_END` patterns and their per-position scan).\n**Root cause:** for each opening `~~`/`==`/`^^` the parser scans forward for the matching close pattern. The scan itself uses a bounded regex, but the parser tries the close-scan at every potential start position. For input shaped like `~~x~~` repeated N times, every `~~` is examined as a possible start, each scan covers up to the end of input. Total work is O(N²). Default config without these plugins handles the same input in linear time (4 ms for 4000 reps), confirming the cost is in the formatting plugin's per-marker scan, not in core parsing.\n\n## Affected Code\n\n**File:** `src/mistune/plugins/formatting.py`, lines 12-16.\n\n```python\n_STRIKE_END = re.compile(r\"(?:\" + PREVENT_BACKSLASH + r\"\\\\~|[^\\s~])~~(?!~)\")\n_MARK_END = re.compile(r\"(?:\" + PREVENT_BACKSLASH + r\"\\\\=|[^\\s=])==(?!=)\")\n_INSERT_END = re.compile(r\"(?:\" + PREVENT_BACKSLASH + r\"\\\\\\^|[^\\s^])\\^\\^(?!\\^)\")\n# Each pattern is scanned forward from every start position fired by the\n# corresponding inline rule. The end-pattern itself is bounded; the cost\n# comes from the surrounding parser invoking the scan at every '~~' / '==' / '^^'\n# token in the input, giving N starts × O(N) per scan = O(N^2) total.\n```\n\n**Why it's wrong:** the same algorithmic-complexity flaw class as `[` / `[a` parsing in core: a per-token retry loop without memoisation of failed positions. Each formatting marker is tried as both a potential start and as a continuation. A linear-pass delimiter-stack algorithm (matching how `commonmark-py` and `markdown-it-py` handle emphasis) would do this work in O(N) total. The bounded regex on each individual scan does not bound the parser-level repetition.\n\n## Exploit Chain\n\n1. Application uses mistune to render user-supplied markdown and has any of the formatting plugins enabled (`plugins=['strikethrough']`, `['mark']`, `['insert']`, or any superset). These plugins are commonly enabled because GitHub-flavoured-Markdown compatibility requires `~~strikethrough~~` and many editors emit `==highlighting==` and `^^underline^^` shortcuts.\n2. Attacker submits an 8 KB markdown payload of the form `~~x~~~~x~~~~x~~...` (40 000 characters of `~~x~~` repeated 8000 times, or the analogous shape with `==` / `^^`).\n3. Server calls `mistune.create_markdown(plugins=['strikethrough'])(payload)`. CPU pegs for ~4 seconds; 16 KB → ~17 seconds; 32 KB → ~70 seconds. Pure CPU cost, no significant memory growth.\n4. Repeating the request floods the worker pool. On a single-thread WSGI handler this is one request per outage; on a thread pool, a small number of concurrent attackers exhausts capacity.\n\n## Security Impact\n\n**Severity:** sec-high. Network-reachable, no authentication, predictable scaling, single-payload primitive. Only requires a user-supplied markdown sink and a formatting plugin enabled — both are common.\n**Attacker capability:** small input → large CPU. Doubling input size quadruples CPU time. Sustained requests deny service to other users.\n**Preconditions:** application uses mistune with any of `strikethrough`, `mark`, or `insert` plugins enabled. Default config does NOT enable these (so the attack only fires against the substantial deployed population that turns them on for GFM/markdown-extra compatibility).\n**Differential:** PoC-verified against mistune@3.2.1:\n\n```python\nimport mistune, time\nmd = mistune.create_markdown(plugins=['strikethrough'])\nfor n in [500, 1000, 2000, 4000, 8000]:\n    s = '~~x~~' * n\n    t = time.time()\n    md(s)\n    print(f'  ~~x~~ * {n} ({len(s)}b): {(time.time() - t) * 1000:.0f}ms')\n\n# Output (Python 3.13, Linux, 2.5GHz CPU):\n#   ~~x~~ *  500  (2500b):    19ms\n#   ~~x~~ * 1000  (5000b):    71ms\n#   ~~x~~ * 2000 (10000b):   272ms\n#   ~~x~~ * 4000 (20000b):  1090ms\n#   ~~x~~ * 8000 (40000b):  4302ms\n\n# Identical scaling for `==x==` (mark) and `^^x^^` (insert):\nmd = mistune.create_markdown(plugins=['mark'])\nmd('==x==' * 4000)   # ~1100ms\nmd = mistune.create_markdown(plugins=['insert'])\nmd('^^x^^' * 4000)   # ~1080ms\n\n# Without the plugin, the same input parses in linear time:\nmd = mistune.create_markdown()  # no plugins\nmd('~~x~~' * 4000)               # 4ms (1000x faster)\n```\n\nThe patched build (with the suggested fix below — either a delimiter-stack rewrite or a hard cap on the number of unmatched markers tracked) keeps the time linear in N.\n\n## Suggested Fix\n\nThe minimal fix is to cap the number of simultaneously-tracked unmatched markers, treating extras as literal text. The proper fix is a single-pass delimiter-stack algorithm matching the CommonMark reference implementation. Surgical patch:\n\n```diff\n--- a/src/mistune/plugins/formatting.py\n+++ b/src/mistune/plugins/formatting.py\n@@ ... in the parse_strikethrough / parse_mark / parse_insert functions\n+    # Bound the number of open markers the parser will track concurrently.\n+    # Inputs with more than this many open ~~ / == / ^^ in flight are\n+    # almost certainly adversarial; CommonMark gives no semantics to\n+    # deeply nested unmatched markers.\n+    MAX_OPEN_MARKERS = 100\n+    if open_marker_count \u003e MAX_OPEN_MARKERS:\n+        # treat remaining markers as literal text, do not invoke the\n+        # forward-scan to find a close\n+        ...\n```\n\nA regression test should assert that `md('~~x~~' * 50_000)` completes in under 1 second. The same fix shape applies to `_MARK_END` and `_INSERT_END`.","status":"published","published":"2026-07-20T21:34:37Z","modified":"2026-07-20T21:34:38Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"cwes":["CWE-407","CWE-1333"],"updated_at":"2026-11-02T14:40:48.410776Z"}],"nvd":[{"id":"CVE-2026-59922","description":"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.","status":"published","published":"2026-07-08T17:17:27.77Z","modified":"2026-10-25T17:10:00.197Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high","assessor":"secondary"}],"cwes":["CWE-407","CWE-1333"],"updated_at":"2026-11-02T14:12:28.040501Z"}],"osv":[{"id":"CVE-2026-59922","title":"Mistune plugins/formatting: quadratic-time parsing on long runs of `~~x~~`, `==x==`, and `^^x^^` markers (strikethrough / mark / insert)","description":"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.","status":"published","published":"2026-07-08T16:12:00.545Z","modified":"2026-10-28T02:31:00.544808422Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"cwes":["CWE-1333","CWE-407"],"updated_at":"2026-11-02T15:45:12.496548Z"},{"id":"DEBIAN-CVE-2026-59922","description":"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.","status":"published","published":"2026-07-08T17:17:27.77Z","modified":"2026-10-26T12:01:33.463876687Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"updated_at":"2026-11-02T15:45:57.932517Z"},{"id":"GHSA-c8j7-8cv4-2xmq","title":"Mistune plugins/formatting: quadratic-time parsing on long runs of `~~x~~`, `==x==`, and `^^x^^` markers (strikethrough / mark / insert)","description":"## Summary\n\n**Type:** Algorithmic-complexity denial of service. A run of N closed pairs `~~x~~~~x~~...` (or the analogous `==x==` for `mark`, `^^x^^` for `insert`) causes O(N²) work in the formatting parser. With the `strikethrough`, `mark`, or `insert` plugin enabled, an 8 KB input pegs the CPU for ~4 seconds; 16 KB → ~17 seconds. \n**File:** `src/mistune/plugins/formatting.py`, lines 13-15 (the `_STRIKE_END` / `_MARK_END` / `_INSERT_END` patterns and their per-position scan).\n**Root cause:** for each opening `~~`/`==`/`^^` the parser scans forward for the matching close pattern. The scan itself uses a bounded regex, but the parser tries the close-scan at every potential start position. For input shaped like `~~x~~` repeated N times, every `~~` is examined as a possible start, each scan covers up to the end of input. Total work is O(N²). Default config without these plugins handles the same input in linear time (4 ms for 4000 reps), confirming the cost is in the formatting plugin's per-marker scan, not in core parsing.\n\n## Affected Code\n\n**File:** `src/mistune/plugins/formatting.py`, lines 12-16.\n\n```python\n_STRIKE_END = re.compile(r\"(?:\" + PREVENT_BACKSLASH + r\"\\\\~|[^\\s~])~~(?!~)\")\n_MARK_END = re.compile(r\"(?:\" + PREVENT_BACKSLASH + r\"\\\\=|[^\\s=])==(?!=)\")\n_INSERT_END = re.compile(r\"(?:\" + PREVENT_BACKSLASH + r\"\\\\\\^|[^\\s^])\\^\\^(?!\\^)\")\n# Each pattern is scanned forward from every start position fired by the\n# corresponding inline rule. The end-pattern itself is bounded; the cost\n# comes from the surrounding parser invoking the scan at every '~~' / '==' / '^^'\n# token in the input, giving N starts × O(N) per scan = O(N^2) total.\n```\n\n**Why it's wrong:** the same algorithmic-complexity flaw class as `[` / `[a` parsing in core: a per-token retry loop without memoisation of failed positions. Each formatting marker is tried as both a potential start and as a continuation. A linear-pass delimiter-stack algorithm (matching how `commonmark-py` and `markdown-it-py` handle emphasis) would do this work in O(N) total. The bounded regex on each individual scan does not bound the parser-level repetition.\n\n## Exploit Chain\n\n1. Application uses mistune to render user-supplied markdown and has any of the formatting plugins enabled (`plugins=['strikethrough']`, `['mark']`, `['insert']`, or any superset). These plugins are commonly enabled because GitHub-flavoured-Markdown compatibility requires `~~strikethrough~~` and many editors emit `==highlighting==` and `^^underline^^` shortcuts.\n2. Attacker submits an 8 KB markdown payload of the form `~~x~~~~x~~~~x~~...` (40 000 characters of `~~x~~` repeated 8000 times, or the analogous shape with `==` / `^^`).\n3. Server calls `mistune.create_markdown(plugins=['strikethrough'])(payload)`. CPU pegs for ~4 seconds; 16 KB → ~17 seconds; 32 KB → ~70 seconds. Pure CPU cost, no significant memory growth.\n4. Repeating the request floods the worker pool. On a single-thread WSGI handler this is one request per outage; on a thread pool, a small number of concurrent attackers exhausts capacity.\n\n## Security Impact\n\n**Severity:** sec-high. Network-reachable, no authentication, predictable scaling, single-payload primitive. Only requires a user-supplied markdown sink and a formatting plugin enabled — both are common.\n**Attacker capability:** small input → large CPU. Doubling input size quadruples CPU time. Sustained requests deny service to other users.\n**Preconditions:** application uses mistune with any of `strikethrough`, `mark`, or `insert` plugins enabled. Default config does NOT enable these (so the attack only fires against the substantial deployed population that turns them on for GFM/markdown-extra compatibility).\n**Differential:** PoC-verified against mistune@3.2.1:\n\n```python\nimport mistune, time\nmd = mistune.create_markdown(plugins=['strikethrough'])\nfor n in [500, 1000, 2000, 4000, 8000]:\n    s = '~~x~~' * n\n    t = time.time()\n    md(s)\n    print(f'  ~~x~~ * {n} ({len(s)}b): {(time.time() - t) * 1000:.0f}ms')\n\n# Output (Python 3.13, Linux, 2.5GHz CPU):\n#   ~~x~~ *  500  (2500b):    19ms\n#   ~~x~~ * 1000  (5000b):    71ms\n#   ~~x~~ * 2000 (10000b):   272ms\n#   ~~x~~ * 4000 (20000b):  1090ms\n#   ~~x~~ * 8000 (40000b):  4302ms\n\n# Identical scaling for `==x==` (mark) and `^^x^^` (insert):\nmd = mistune.create_markdown(plugins=['mark'])\nmd('==x==' * 4000)   # ~1100ms\nmd = mistune.create_markdown(plugins=['insert'])\nmd('^^x^^' * 4000)   # ~1080ms\n\n# Without the plugin, the same input parses in linear time:\nmd = mistune.create_markdown()  # no plugins\nmd('~~x~~' * 4000)               # 4ms (1000x faster)\n```\n\nThe patched build (with the suggested fix below — either a delimiter-stack rewrite or a hard cap on the number of unmatched markers tracked) keeps the time linear in N.\n\n## Suggested Fix\n\nThe minimal fix is to cap the number of simultaneously-tracked unmatched markers, treating extras as literal text. The proper fix is a single-pass delimiter-stack algorithm matching the CommonMark reference implementation. Surgical patch:\n\n```diff\n--- a/src/mistune/plugins/formatting.py\n+++ b/src/mistune/plugins/formatting.py\n@@ ... in the parse_strikethrough / parse_mark / parse_insert functions\n+    # Bound the number of open markers the parser will track concurrently.\n+    # Inputs with more than this many open ~~ / == / ^^ in flight are\n+    # almost certainly adversarial; CommonMark gives no semantics to\n+    # deeply nested unmatched markers.\n+    MAX_OPEN_MARKERS = 100\n+    if open_marker_count \u003e MAX_OPEN_MARKERS:\n+        # treat remaining markers as literal text, do not invoke the\n+        # forward-scan to find a close\n+        ...\n```\n\nA regression test should assert that `md('~~x~~' * 50_000)` completes in under 1 second. The same fix shape applies to `_MARK_END` and `_INSERT_END`.","status":"published","published":"2026-07-20T21:34:37Z","modified":"2026-07-20T21:46:39.999565306Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"cwes":["CWE-1333","CWE-407"],"updated_at":"2026-11-02T15:46:44.927642Z"},{"id":"PYSEC-2026-2210","description":"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.","status":"published","published":"2026-07-08T17:17:27.77Z","modified":"2026-07-13T07:15:29.086264845Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"updated_at":"2026-11-02T15:52:55.537808Z"},{"id":"UBUNTU-CVE-2026-59922","description":"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.","status":"published","published":"2026-07-08T17:17:00Z","modified":"2026-11-11T09:40:07.140848653Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"updated_at":"2026-11-11T10:02:12.942344Z"},{"id":"AZL-92220","title":"CVE-2026-59922 affecting package python-mistune for versions less than 3.3.0-1","description":"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.","status":"published","published":"2026-07-08T17:17:27Z","modified":"2026-09-09T06:55:05Z","updated_at":"2026-11-02T15:28:11.723718Z"},{"id":"openSUSE-SU-2026:21339-1","title":"Security update for python-mistune","description":"This update for python-mistune fixes the following issues\n\n- CVE-2026-59922: quadratic-time parsing on long runs of some markers in `formatting.py` can lead to DoS (bsc#1271117).\n- CVE-2026-59923: `HTMLRenderer.safe_url()` does not block percent-encoded javascript URIs and allows for XSS\n  (bsc#1271119).\n- CVE-2026-59924: improper processing of user-supplied include paths in `Include.parse()` can lead to path traversal\n  and arbitrary file reads (bsc#1271121).\n- CVE-2026-59925: quadratic-time parsing on long runs of some emphasis pairs in `inline_parser` can lead to DoS\n  (bsc#1271125).\n- CVE-2026-59926: improper escaping in `render_admonition()` can lead to atribute injection and XSS (bsc#1271127).\n- CVE-2026-59927: uncontrolled recursion when processing two markdown files that include each other can lead to a DoS\n   (bsc#1271128).\n- CVE-2026-59928: quadratic-time parsing on long lists of repeated reference-link definitions in `block_parser` can\n  lead to DoS (bsc#1271131).\n- CVE-2026-59929: HARMFUL_PROTOCOLS list misses legacy and chained schemes and allow arbitrary script execution in\n  user agents (bsc#1271132).\n- CVE-2026-59930: the `toc` plugin and `TableOfContents` directive generate heading IDs with predictable values and\n  allow for collisions with attacker-controlled `id=\"toc_N\"` content (bsc#1271082).\n","status":"published","published":"2026-07-14T10:41:18Z","modified":"2026-07-15T10:00:11.548963212Z","updated_at":"2026-11-02T16:58:41.885092Z"},{"id":"SUSE-SU-2026:22655-1","title":"Security update for python-mistune","description":"This update for python-mistune fixes the following issues\n\n- CVE-2026-59922: quadratic-time parsing on long runs of some markers in `formatting.py` can lead to DoS (bsc#1271117).\n- CVE-2026-59923: `HTMLRenderer.safe_url()` does not block percent-encoded javascript URIs and allows for XSS\n  (bsc#1271119).\n- CVE-2026-59924: improper processing of user-supplied include paths in `Include.parse()` can lead to path traversal\n  and arbitrary file reads (bsc#1271121).\n- CVE-2026-59925: quadratic-time parsing on long runs of some emphasis pairs in `inline_parser` can lead to DoS\n  (bsc#1271125).\n- CVE-2026-59926: improper escaping in `render_admonition()` can lead to atribute injection and XSS (bsc#1271127).\n- CVE-2026-59927: uncontrolled recursion when processing two markdown files that include each other can lead to a DoS\n   (bsc#1271128).\n- CVE-2026-59928: quadratic-time parsing on long lists of repeated reference-link definitions in `block_parser` can\n  lead to DoS (bsc#1271131).\n- CVE-2026-59929: HARMFUL_PROTOCOLS list misses legacy and chained schemes and allow arbitrary script execution in\n  user agents (bsc#1271132).\n- CVE-2026-59930: the `toc` plugin and `TableOfContents` directive generate heading IDs with predictable values and\n  allow for collisions with attacker-controlled `id=\"toc_N\"` content (bsc#1271082).\n","status":"published","published":"2026-07-14T10:37:38Z","modified":"2026-07-17T18:24:05.645518218Z","updated_at":"2026-11-02T15:54:03.080707Z"}]}}
