{"id":"CVE-2026-5389","aliases":["EUVD-2026-64653","GHSA-5vp3-3cg6-2rq3","GHSA-q3jf-9wv7-p29w"],"status":"published","title":"justhtml before 1.13.0 XSS via code fence breakout","description":"justhtml versions before 1.13.0 contain a cross-site scripting vulnerability in the to_markdown() function when serializing attacker-controlled pre content. Attackers can place backticks inside sanitized pre elements to break out of fixed-length code fences, allowing raw HTML to execute when the generated Markdown is rendered by CommonMark or GFM-style renderers.","severity":{"version":"4.0","score":5.1,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-10","score":0.0023,"percentile":0.1391,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-79","CWE-80"],"published":"2026-03-24T19:22:21Z","modified":"2026-10-28T02:31:05Z","fixes":[{"ecosystem":"PyPI","package":"justhtml","fixed_in":["1.13.0"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"justhtml","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"1.12.0"},{"fixed":"1.13.0"}]}]},{"source":"osv","ecosystem":"PyPI","package":"justhtml","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.13.0"}]}],"versions":["0.1.0","0.10.0","0.11.0","0.12.0","0.13.0","0.13.1","0.14.0","0.15.0","0.16.0","0.17.0","0.18.0","0.19.0","0.2.0","0.20.0","0.21.0","0.22.0","0.23.0","0.24.0","0.25.0","0.26.0","0.27.0","0.28.0","0.29.0","0.3.0","0.30.0","0.31.0","0.32.0","0.33.0","0.34.0","0.35.0","0.36.0","0.37.0","0.38.0","0.39.0","0.4.0","0.40.0","0.5.0","0.5.1","0.5.2","0.6.0","0.7.0","0.8.0","0.9.0","1.0.0","1.1.0","1.10.0","1.11.0","1.12.0","1.2.0","1.3.0","1.4.0","1.5.0","1.6.0","1.7.0","1.8.0","1.9.0","1.9.1"]}],"products":[{"vendor":"emilstenstrom","product":"justhtml"}],"references":[{"url":"https://github.com/EmilStenstrom/justhtml/security/advisories/GHSA-5vp3-3cg6-2rq3","tags":["vendor-advisory","exploit","advisory","web"]},{"url":"https://www.vulncheck.com/advisories/justhtml-before-xss-via-code-fence-breakout","tags":["third-party-advisory","advisory"]},{"url":"https://github.com/EmilStenstrom/justhtml/commit/f35f8f723c713bd8f912d86e9ec6881275ff5af9","tags":["web"]},{"url":"https://github.com/EmilStenstrom/justhtml/releases/tag/v1.13.0","tags":["web"]},{"url":"https://github.com/advisories/GHSA-5vp3-3cg6-2rq3"},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-5389","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-q3jf-9wv7-p29w"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/5xxx/CVE-2026-5389.json","tags":["advisory"]},{"url":"https://github.com/EmilStenstrom/justhtml","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-5389","title":"justhtml before 1.13.0 XSS via code fence breakout","description":"justhtml versions before 1.13.0 contain a cross-site scripting vulnerability in the to_markdown() function when serializing attacker-controlled pre content. Attackers can place backticks inside sanitized pre elements to break out of fixed-length code fences, allowing raw HTML to execute when the generated Markdown is rendered by CommonMark or GFM-style renderers.","status":"published","published":"2026-08-23T13:34:09.865Z","modified":"2026-08-24T18:15:49.005Z","cvss":[{"version":"4.0","score":5.1,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N","level":"medium","assessor":"cna"},{"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":"cna"}],"cwes":["CWE-80"],"updated_at":"2026-11-02T13:39:27.849196Z"}],"euvd":[{"id":"EUVD-2026-64653","description":"justhtml versions before 1.13.0 contain a cross-site scripting vulnerability in the to_markdown() function when serializing attacker-controlled pre content. Attackers can place backticks inside sanitized pre elements to break out of fixed-length code fences, allowing raw HTML to execute when the generated Markdown is rendered by CommonMark or GFM-style renderers.","status":"published","published":"2026-08-23T13:34:09Z","modified":"2026-08-24T18:15:49Z","cvss":[{"version":"4.0","score":5.1,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N","level":"medium"}],"updated_at":"2026-11-02T18:01:34.563496Z"}],"ghsa":[{"id":"GHSA-5vp3-3cg6-2rq3","title":"JustHTML is vulnerable to XSS via code fence breakout in \u003cpre\u003e content","description":"## Summary\n\n`to_markdown()` is vulnerable when serializing attacker-controlled `\u003cpre\u003e` content. The `\u003cpre\u003e` handler emits a fixed three-backtick fenced code block, but writes decoded text content into that fence without choosing a delimiter longer than any backtick run inside the content.\n\nAn attacker can place backticks and HTML-like text inside a sanitized `\u003cpre\u003e` element so that the generated Markdown closes the fence early and leaves raw HTML outside the code block. When that Markdown is rendered by a CommonMark/GFM-style renderer that allows raw HTML, the HTML executes.\n\nThis is a bypass of the v1.12.0 Markdown hardening. That fix escaped HTML-significant characters for regular text nodes, but `\u003cpre\u003e` uses a separate serialization path and does not apply the same protection.\n\n## Details\n\nThe vulnerable `\u003cpre\u003e` Markdown path:\n\n- extracts decoded text from the `\u003cpre\u003e` subtree\n- opens a fenced block with a fixed delimiter of ``````\n- writes the decoded text directly into the output\n- closes with another fixed ``````\n\nBecause the fence length is fixed, attacker-controlled content containing a backtick run of length 3 or more can terminate the code block. If the content also contains decoded HTML-like text such as `\u0026lt;img ...\u0026gt;`, that text appears outside the fence in the resulting Markdown and is treated as raw HTML by downstream Markdown renderers.\n\nThe issue is not that HTML-like text appears inside code blocks. The issue is that the serializer allows attacker-controlled `\u003cpre\u003e` text to break out of the fixed fence.\n\n## Reproduction\n\n```python\nfrom justhtml import JustHTML\n\npayload = \"\u003cpre\u003e\u0026#96;\u0026#96;\u0026#96;\\n\u0026lt;img src=x onerror=alert(1)\u0026gt;\u003c/pre\u003e\"\ndoc = JustHTML(payload, fragment=True)  # default sanitize=True\n\nprint(doc.to_html(pretty=False))\n# \u003cpre\u003e```\n# \u0026lt;img src=x onerror=alert(1)\u0026gt;\u003c/pre\u003e\n\nprint(doc.to_markdown())\n# ```\n# ```\n# \u003cimg src=x onerror=alert(1)\u003e\n# ```\n\n```\n\nRendered as CommonMark/GFM-style Markdown, that output is interpreted as:\n\n1. Line 1 opens a fenced code block\n2. Line 2 closes it\n3. Line 3 is raw HTML outside the fence\n4. Line 4 opens a new fence\n\n## Impact\n\nApplications that treat `JustHTML(..., sanitize=True).to_markdown()` output as safe for direct rendering in Markdown contexts may be exposed to XSS, depending on the downstream Markdown renderer's raw-HTML handling.\n\n## Root Cause\n\nThe `\u003cpre\u003e` Markdown serializer uses a fixed fence instead of selecting a delimiter longer than the longest backtick run in the content.\n\n## Fix\n\nWhen serializing `\u003cpre\u003e` content to Markdown, choose a fence length longer than any backtick run present in the code block content, with a minimum length of 3.","status":"published","published":"2026-03-24T19:22:21Z","modified":"2026-03-24T19:22:21Z","cvss":[{"version":"4.0","score":7.1,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N","level":"high"}],"cwes":["CWE-79","CWE-80"],"updated_at":"2026-11-02T14:39:41.724707Z"},{"id":"GHSA-q3jf-9wv7-p29w","title":"justhtml versions before 1.13.0 contain a cross-site scripting vulnerability in the to_markdown()...","description":"justhtml versions before 1.13.0 contain a cross-site scripting vulnerability in the to_markdown() function when serializing attacker-controlled pre content. Attackers can place backticks inside sanitized pre elements to break out of fixed-length code fences, allowing raw HTML to execute when the generated Markdown is rendered by CommonMark or GFM-style renderers.","status":"published","published":"2026-08-23T15:33:04Z","modified":"2026-08-23T15:33:12Z","cvss":[{"version":"4.0","score":5.1,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","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"}],"cwes":["CWE-80"],"updated_at":"2026-11-02T15:10:52.226529Z"}],"nvd":[{"id":"CVE-2026-5389","description":"justhtml versions before 1.13.0 contain a cross-site scripting vulnerability in the to_markdown() function when serializing attacker-controlled pre content. Attackers can place backticks inside sanitized pre elements to break out of fixed-length code fences, allowing raw HTML to execute when the generated Markdown is rendered by CommonMark or GFM-style renderers.","status":"published","published":"2026-08-23T14:16:53.647Z","modified":"2026-10-12T16:10:00.22Z","cvss":[{"version":"4.0","score":5.1,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","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":"secondary"}],"cwes":["CWE-80"],"updated_at":"2026-11-02T14:12:41.018848Z"}],"osv":[{"id":"CVE-2026-5389","title":"justhtml before 1.13.0 XSS via code fence breakout","description":"justhtml versions before 1.13.0 contain a cross-site scripting vulnerability in the to_markdown() function when serializing attacker-controlled pre content. Attackers can place backticks inside sanitized pre elements to break out of fixed-length code fences, allowing raw HTML to execute when the generated Markdown is rendered by CommonMark or GFM-style renderers.","status":"published","published":"2026-08-23T13:34:09.865Z","modified":"2026-10-28T02:31:05.244764367Z","cvss":[{"version":"4.0","score":5.1,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N","level":"medium"}],"cwes":["CWE-80"],"updated_at":"2026-11-02T15:45:08.132336Z"},{"id":"GHSA-5vp3-3cg6-2rq3","title":"JustHTML is vulnerable to XSS via code fence breakout in \u003cpre\u003e content","description":"## Summary\n\n`to_markdown()` is vulnerable when serializing attacker-controlled `\u003cpre\u003e` content. The `\u003cpre\u003e` handler emits a fixed three-backtick fenced code block, but writes decoded text content into that fence without choosing a delimiter longer than any backtick run inside the content.\n\nAn attacker can place backticks and HTML-like text inside a sanitized `\u003cpre\u003e` element so that the generated Markdown closes the fence early and leaves raw HTML outside the code block. When that Markdown is rendered by a CommonMark/GFM-style renderer that allows raw HTML, the HTML executes.\n\nThis is a bypass of the v1.12.0 Markdown hardening. That fix escaped HTML-significant characters for regular text nodes, but `\u003cpre\u003e` uses a separate serialization path and does not apply the same protection.\n\n## Details\n\nThe vulnerable `\u003cpre\u003e` Markdown path:\n\n- extracts decoded text from the `\u003cpre\u003e` subtree\n- opens a fenced block with a fixed delimiter of ``````\n- writes the decoded text directly into the output\n- closes with another fixed ``````\n\nBecause the fence length is fixed, attacker-controlled content containing a backtick run of length 3 or more can terminate the code block. If the content also contains decoded HTML-like text such as `\u0026lt;img ...\u0026gt;`, that text appears outside the fence in the resulting Markdown and is treated as raw HTML by downstream Markdown renderers.\n\nThe issue is not that HTML-like text appears inside code blocks. The issue is that the serializer allows attacker-controlled `\u003cpre\u003e` text to break out of the fixed fence.\n\n## Reproduction\n\n```python\nfrom justhtml import JustHTML\n\npayload = \"\u003cpre\u003e\u0026#96;\u0026#96;\u0026#96;\\n\u0026lt;img src=x onerror=alert(1)\u0026gt;\u003c/pre\u003e\"\ndoc = JustHTML(payload, fragment=True)  # default sanitize=True\n\nprint(doc.to_html(pretty=False))\n# \u003cpre\u003e```\n# \u0026lt;img src=x onerror=alert(1)\u0026gt;\u003c/pre\u003e\n\nprint(doc.to_markdown())\n# ```\n# ```\n# \u003cimg src=x onerror=alert(1)\u003e\n# ```\n\n```\n\nRendered as CommonMark/GFM-style Markdown, that output is interpreted as:\n\n1. Line 1 opens a fenced code block\n2. Line 2 closes it\n3. Line 3 is raw HTML outside the fence\n4. Line 4 opens a new fence\n\n## Impact\n\nApplications that treat `JustHTML(..., sanitize=True).to_markdown()` output as safe for direct rendering in Markdown contexts may be exposed to XSS, depending on the downstream Markdown renderer's raw-HTML handling.\n\n## Root Cause\n\nThe `\u003cpre\u003e` Markdown serializer uses a fixed fence instead of selecting a delimiter longer than the longest backtick run in the content.\n\n## Fix\n\nWhen serializing `\u003cpre\u003e` content to Markdown, choose a fence length longer than any backtick run present in the code block content, with a minimum length of 3.","status":"published","published":"2026-03-24T19:22:21Z","modified":"2026-08-24T03:56:00.203556914Z","cvss":[{"version":"4.0","score":7.1,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N","level":"high"}],"cwes":["CWE-79","CWE-80"],"updated_at":"2026-11-02T15:46:38.911014Z"}]}}
