League CommonMark AttributesExtension XSS Vulnerability (CVE-2026-71478) (Medium) -DC-Aug2026-1458

Listen to this Post

The vulnerability resides in the `AttributesExtension` of the league/commonmark PHP library, which allows Markdown users to add HTML attributes to elements. The extension provides an unsafe-link filter (AttributesHelper::filterAttributes()) that is supposed to remove `href` and `src` attributes pointing to dangerous protocols like javascript:. This filter relies on RegexHelper::isLinkPotentiallyUnsafe(), which uses a literal anchored-prefix regular expression (REGEX_UNSAFE_PROTOCOL = '/^(?:javascript|vbscript|file|data):/i') to detect unsafe URLs.
An attacker can bypass this filter by embedding control bytes that the browser strips before interpreting the URL scheme, while the PHP regex sees the raw, unnormalized string and fails to match. Two primary variants exist:
1. Tab/Newline inside the scheme – Inserting an ASCII tab (0x09), carriage return (0x0D), or line feed (0x0A) between the protocol name and the colon, e.g., java<TAB>script:alert(1). Per the WHATWG URL Standard’s “basic URL parser” step 3, browsers remove all ASCII tab or newline characters from the input, so the browser executes javascript:alert(1).
2. Leading C0 control characters – Prefixing the URL with a C0 control byte such as 0x01, e.g., <0x01>javascript:alert(1). Step 1 of the same algorithm removes leading and trailing C0 controls and spaces. A leading space alone does not bypass because `parseAttributes()` already trims the value, but other C0 bytes are not trimmed.
The AttributesExtension‘s quote-value grammar (PARTIAL_DOUBLEQUOTEDVALUE = '"[^"]"') accepts any byte except a double quote inside attribute values, including raw control bytes. The `parseAttributes()` function only applies a default trim (which includes space, tab, newline, carriage return, null byte, and vertical tab) – so a leading 0x01 survives. In contrast, the core Markdown link destination parser (UrlEncoder::unescapeAndEncode()) percent-encodes control bytes before the safety check, but the Attributes extension has no such normalization.
This bypass is exploitable even when `allow_unsafe_links => false` is set – the library’s own documented hardening guidance for untrusted input. An attacker can supply Markdown like [Click me](javascript:alert(0)){href="java<TAB>script:alert(document.cookie)"}. The filter does not match the obfuscated href, so it is emitted as <a href="java<TAB>script:alert(document.cookie)">Click me</a>. The browser strips the tab and executes the script in the victim’s session, leading to stored XSS, cookie theft, and account takeover.
The core destination must itself be unsafe (javascript:alert(0)) to prevent `LinkRenderer` from overwriting the attribute-supplied `href` with a safe core URL. This is trivial for an attacker who controls the entire Markdown document. The issue affects all versions from 1.5.0 (when the AttributesExtension was introduced) through 2.8.3. It is a sibling gap to CVE-2025-46734, which fixed a different Attributes-extension XSS but did not normalize control bytes.

DailyCVE Form:

Platform: league/commonmark
Version: 1.5.0–2.8.3
Vulnerability: XSS (control bypass)
Severity: Medium (CVSS 6.1)
Date: 2026-08-07

Prediction: 2026-08-07 (2.9.0)

What Undercode Say:

Analytics – The vulnerability stems from incomplete denylist filtering (CWE-692) and improper neutralization of invalid characters (CWE-86), leading to XSS (CWE-79). The anchored-prefix regex fails to account for browser normalization, and the attribute parser does not normalize control bytes before the security check. This is a classic case of server‑side blacklist vs. client‑side parsing discrepancy.
Bash Commands / Codes – To reproduce, install an affected version and render the payload:

composer require league/commonmark:2.8.3
use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\Attributes\AttributesExtension;
use League\CommonMark\MarkdownConverter;
$environment = new Environment(['allow_unsafe_links' => false]);
$environment->addExtension(new AttributesExtension());
$converter = new MarkdownConverter($environment);
$payload = '<a href="javascript:alert(0)">Click me</a>{href="java' . "\t" . 'script:alert(document.cookie)"}';
echo $converter->convert($payload);
// Outputs: <a href="java   script:alert(document.cookie)">Click me</a>

The filter function `RegexHelper::isLinkPotentiallyUnsafe()` uses:

public const REGEX_UNSAFE_PROTOCOL = '/^(?:javascript|vbscript|file|data):/i';

which does not match the obfuscated string. The attribute filter in AttributesHelper::filterAttributes():

if (! $allowUnsafeLinks && ($attrNameLower === 'href' || $attrNameLower === 'src') && is_string($value) && RegexHelper::isLinkPotentiallyUnsafe($value)) {
unset($attributes[$name]);
}

thus leaves the malicious attribute intact.

Exploit:

An attacker submits Markdown with a `javascript:` URL containing a tab, newline, or leading C0 control byte inside the `href` or `src` attribute of the Attributes extension. The filter does not detect the obfuscated protocol, so the attribute is rendered in the HTML. When a victim clicks the link (or in some cases, merely hovers over it), the browser normalizes the URL by removing the control bytes and executes the JavaScript in the context of the victim’s session. This enables stored XSS, session hijacking, credential theft, and arbitrary actions on behalf of the user.

Protection:

Upgrade to league/commonmark version 2.9.0 or later, which fixes the issue by normalizing control bytes inside `RegexHelper::isLinkPotentiallyUnsafe()` before applying the regex, mirroring the WHATWG URL parser’s normalization. If upgrading is not immediately possible, consider disabling the AttributesExtension entirely or implementing a custom event listener that sanitizes `href` and `src` attributes by removing control bytes before the filter runs. Additionally, avoid rendering untrusted Markdown with the AttributesExtension enabled.

Impact:

  • Confidentiality: Attacker can read the victim’s cookies, session tokens, or other sensitive data accessible via JavaScript.
  • Integrity: Attacker can perform actions on behalf of the victim, such as modifying account settings, posting content, or initiating transactions.
  • Availability: While not directly impacting availability, the XSS can be used to deface pages or redirect users to malicious sites.
  • Scope: All applications using league/commonmark versions 1.5.0 through 2.8.3 with the AttributesExtension enabled and `allow_unsafe_links` set to `false` are vulnerable. The vulnerability is particularly critical in multi‑user environments (e.g., forums, CMS, comment systems) where an attacker can inject persistent payloads.

🎯Let’s Practice Exploiting & Learn Patching For Free:

🎓 Live Courses & Certifications:

Join Undercode Academy for Verified Certifications

🚀 Request a Custom Project:

Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top