An XSS vulnerability in Mistune allows bypassing of safe_url() protections via percent-encoded javascript URIs.
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.
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:
Open the rendered HTML in a browser and click the link.
The browser decodes "%3A" into ":" and executes:
javascript:alert(1)
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:
This affects any application that renders user-controlled Markdown using Mistune without additional URL sanitization.
{
"cwe_ids": [
"CWE-79"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-20T21:32:49Z",
"nvd_published_at": "2026-07-08T17:17:27Z",
"severity": "MODERATE"
}