CVE-2026-59923 is a medium-severity (CVSS 6.1) Cross-site Scripting (XSS) vulnerability in mistune. A fix is available for mistune — see the affected versions and patch details below.
Mistune: XSS via percent-encoded javascript URI bypass in safe_url()
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
Exploitation and automatability from CISA’s SSVC triage for CVE-2026-59923.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
How urgent is this, really
CVE-2026-59923 by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.
Where this sits among everything scored
Of 395,308 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.
Real-World Exposure
mistuneReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.
Description
Summary
An XSS vulnerability in Mistune allows bypassing of safe_url() protections via percent-encoded javascript URIs.
Details
The vulnerability exists in HTMLRenderer.safe_url() in Mistune.
The function is intended to block harmful URL schemes such as "javascript:" by checking the prefix of the provided URL:
_url = url.lower()
if _url.startswith(self.HARMFUL_PROTOCOLS):
return "#harmful-link"
However, the input URL is not URL-decoded before this check. Because of this, an attacker can use percent-encoding to bypass the filter. For example:
javascript%3Aalert(1)
Since "%3A" is not decoded to ":", the check does not detect the "javascript:" scheme.
When rendered in a browser, the URL is decoded, resulting in execution of arbitrary JavaScript upon user interaction.
This effectively bypasses Mistune's built-in safe_url() protection mechanism.
PoC
-
Install vulnerable version:
pip install mistune==3.2.0
-
Run the following code:
import mistune
markdown = mistune.create_markdown() html = markdown("j")
print(html)
-
Output:
<p><a href="javascript%3Aalert(1)">j</a></p> -
Open the rendered HTML in a browser and click the link.
-
The browser decodes "%3A" into ":" and executes:
javascript:alert(1)
Impact
This is a cross-site scripting (XSS) vulnerability.
An attacker can craft a malicious Markdown link that executes JavaScript in the victim's browser when clicked.
Impact includes:
- Session hijacking (e.g., cookie theft)
- Execution of arbitrary JavaScript in the victim's context
- Potential account takeover depending on the application
This affects any application that renders user-controlled Markdown using Mistune without additional URL sanitization.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐍PyPI | mistune | all versions | 3.3.0pip install --upgrade 'mistune==3.3.0' |
Affected Products
mistunemistune_projectDetection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for mistune, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update mistune to 3.3.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-59923 is resolved across your whole dependency graph.
Workarounds
Escape or sanitise the affected output on the server side rather than relying on client-side filtering, and add a Content-Security-Policy that blocks inline script execution so injected markup cannot run even if it reaches the page.
Fixing This On Your OS
If you run this on a Linux distribution, patch through your package manager against the distro's own security advisory below — it tracks the exact backported fix for your release, which can ship on a different timeline (and sometimes a different severity) than the upstream project.
Red Hat products ship python-mistune in both the older 0.8.x series and newer 3.x series. The URL sanitization bypass applies to both versions: the 0.8.x escape_link() function and the 3.x safe_url() function both fail to decode percent-encoded characters before checking for dangerous URI schemes. Exploitation…
Applications that render user-supplied Markdown with mistune can pass the HTML output through a dedicated HTML sanitizer such as bleach or nh3 before serving it to users. This removes dangerous URI schemes regardless of encoding.Source: Red Hat security advisory for CVE-2026-59923 (CC BY 4.0)
Frequently Asked Questions
Is CVE-2026-59923 in your dependencies?
Find it across PyPI, including transitive dependencies.