CVE-2026-71478
league/commonmark: AttributesExtension href/src unsafe-link filter bypass via embedded control bytes
league/commonmark is a PHP library for parsing and rendering CommonMark Markdown. From 1.5.0 until 2.9.0, the AttributesExtension's href and src unsafe-link filter can be bypassed by embedding control bytes, such as a tab, carriage return, line feed, or leading C0 control character, in a javascript: URL that browsers discard before parsing the scheme, causing the browser to still execute the script even when the unsafe-link filter is enabled. This issue is fixed in 2.9.0.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- Required
What an attacker can affect
- Confidentiality
- Low
- Integrity
- Low
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
6.1
league/commonmark: AttributesExtension href/src unsafe-link filter bypass via embedded control bytes
league/commonmark is a PHP library for parsing and rendering CommonMark Markdown. From 1.5.0 until 2.9.0, the AttributesExtension's href and src unsafe-link filter can be bypassed by embedding control bytes, such as a tab, carriage return, line feed, or leading C0 control character, in a javascript: URL that browsers discard before parsing the scheme, causing the browser to still execute the script even when the unsafe-link filter is enabled. This issue is fixed in 2.9.0.
- Record
CVE-2026-71478- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N - Weakness
- CWE-79, CWE-86, CWE-692
- Updated
- 2026-11-02
EUVDEUVD-2026-54261
6.1
league/commonmark is a PHP library for parsing and rendering CommonMark Markdown. From 1.5.0 until 2.9.0, the AttributesExtension's href and src unsafe-link filter can be bypassed by embedding control bytes, such as a tab, carriage return, line feed, or leading C0 control character, in a javascript: URL that browsers discard before parsing the scheme, causing the browser to still execute the script even when the unsafe-link filter is enabled. This issue is fixed in 2.9.0.
- Record
EUVD-2026-54261- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-29pj-957v-52mc
6.1
league/commonmark: AttributesExtension href/src unsafe-link filter bypass via embedded control bytes
## Summary
The AttributesExtension's href/src unsafe-link filter (AttributesHelper::filterAttributes()) can be bypassed by embedding control bytes in a javascript: URL that browsers discard before parsing the scheme. Two variants:
- Tab/newline inside the scheme — a literal ASCII TAB (0x09), CR (0x0D), or LF (0x0A), 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 from input". - Leading C0 controls — e.g.
<0x01>javascript:alert(1). Per step 1 of the same algorithm, browsers remove any leading or trailing C0 control or space. (A leading space alone does not bypass, becauseparseAttributes()alreadytrim()s the value; other C0 bytes are not trimmed.)
The filter is a literal anchored-prefix regex (RegexHelper::isLinkPotentiallyUnsafe() / REGEX_UNSAFE_PROTOCOL) that matches neither obfuscated form, so in both cases the browser still executes javascript:alert(1).
This is confirmed reproducible even with allow_unsafe_links => false set — i.e. even applications that have followed the library's own documented hardening guidance for untrusted input remain exploitable.
This is a sibling gap in the same defense that CVE-2025-46734 (GHSA-3527-qv2q-pfvx) fixed in v2.7.0 — that fix made href/src respect allow_unsafe_links, but did not normalize control bytes before checking, so these obfuscation techniques were never covered.
Vulnerability
Files:
src/Util/RegexHelper.php:69(REGEX_UNSAFE_PROTOCOL),:239-242(isLinkPotentiallyUnsafe())src/Extension/Attributes/Util/AttributesHelper.php:149-179(filterAttributes())
CWE: CWE-79 (Improper Neutralization of Input During Web Page Generation / XSS) — primary
- CWE-692 (Incomplete Denylist to Cross-Site Scripting) — the anchored-prefix denylist in
REGEX_UNSAFE_PROTOCOLis incomplete. This is a composite of CWE-184 and CWE-79, so it captures the full "incomplete denylist → XSS" chain on its own. - CWE-86 (Improper Neutralization of Invalid Characters in Identifiers in Web Pages) — the specific evasion technique: control bytes embedded within the URI scheme identifier, which the browser strips before resolving it.
Root Cause
// src/Util/RegexHelper.php
public const REGEX_UNSAFE_PROTOCOL = '/^(?:javascript|vbscript|file|data):/i';
public static function isLinkPotentiallyUnsafe(string $url): bool
{
return \preg_match(self::REGEX_UNSAFE_PROTOCOL, $url) !== 0 && \preg_match(self::REGEX_SAFE_DATA_PROTOCOL, $url) === 0;
}
// src/Extension/Attributes/Util/AttributesHelper.php
foreach ($attributes as $name => $value) {
$attrNameLower = \strtolower($name);
if (! $allowUnsafeLinks && ($attrNameLower === 'href' || $attrNameLower === 'src') && \is_string($value) && RegexHelper::isLinkPotentiallyUnsafe($value)) {
unset($attributes[$name]);
continue;
}
...
The Attributes extension's own quote-value grammar (PARTIAL_DOUBLEQUOTEDVALUE = '"[^"]*"') accepts any byte except " inside quotes, including raw tab/CR/LF and other C0 controls, and parseAttributes() only trim()s (leading/trailing, and only the default charlist " \t\n\r\0\x0B" — so a leading \x01 survives). Critically, the core Markdown link-destination path (LinkParserHelper → UrlEncoder::unescapeAndEncode()) percent-encodes every control byte before this same safety check ever runs — but the Attributes extension's href/src handling has no equivalent normalization step, so the raw control byte reaches both the check and the final HTML output (Xml::escape() only escapes & < > " ', not tab/CR/LF, since they're legal bytes inside an HTML attribute).
Attack Scenario
- An application enables the (commonly-used)
AttributesExtensionand setsallow_unsafe_links => false— the project's own documented hardening step for untrusted input. - An attacker submits Markdown: `[Click…
- Record
GHSA-29pj-957v-52mc- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N - Weakness
- CWE-79, CWE-86, CWE-692
- Updated
- 2026-11-02
GitHub AdvisoriesGHSA-g2gp-3wwq-f4ph
7.5
league/commonmark: Denial of service via adjacent inline attribute blocks
Impact
With the Attributes extension enabled, AttributesListener::findTargetAndDirection() resolves each attribute node's target by walking outward through its siblings. For a run of N adjacent inline attribute blocks placed at the start of a block (with nothing to their left), each node scans the entire sibling list to the far-right end before giving up and falling back to the parent. Each resolution is therefore Θ(N) and the whole run is Θ(N²).
Reaching the path requires AttributesExtension (opt-in, but first-party: League\CommonMark\Extension\Attributes\AttributesExtension). No other configuration matters — the quadratic walk runs unconditionally during parsing and is not gated by the attributes/allow allow-list, the on* hardening added in 2.7.0, or allow_unsafe_links. An unauthenticated attacker can submit a ~32 KB input ({#a} repeated 8,000 times) that takes over 5 seconds to convert, with time growing quadratically in input length — a cheap denial of service. Availability impact only. The Attributes extension was introduced in 1.5.0 (May 2020) with this outward-walk resolver present from the first commit, so all releases from 1.5.0 onward (including every 2.x) are affected.
Workarounds
There is no library-level configuration that gates the quadratic walk. Integrators who cannot upgrade can only reduce exposure indirectly:
- Disable the Attributes extension for untrusted input, or
- Impose a strict maximum input length before conversion — noting that because the cost is quadratic, even a modest cap must be small to meaningfully bound worst-case CPU.
Upgrading to a release containing the fix is recommended.
- Record
GHSA-g2gp-3wwq-f4ph- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-407
- Updated
- 2026-11-02
GitHub AdvisoriesGHSA-jfm3-95jq-q3rf
7.5
league/commonmark: Denial of service via duplicate footnote definitions
Impact
The Footnote extension records one backref per footnote reference and then appends the entire backref list for every footnote definition block in the document, without ever de-duplicating or removing repeated definitions of the same label (GatherFootnotesListener, populated by NumberFootnotesListener). A document that references a single label N times and also supplies N duplicate [^a]: definitions of that label therefore produces N × N FootnoteBackref nodes, so output size, parse time, and peak memory are all O(N²).
Reaching the vulnerable path requires FootnoteExtension to be registered on the Environment. This is opt-in, but is a commonly enabled GFM-style feature; no other non-default configuration is required. An unauthenticated attacker can expand a ~10 KB request into a ~62 MB HTML response, ~3 s of CPU, and ~440 MB of peak memory — enough to OOM-kill a default 128 MB PHP worker and deny service. Availability impact only; no confidentiality or integrity effect. The Footnote extension was introduced in 1.5.0 (May 2020) with this backref logic present from the first commit, so all releases from 1.5.0 onward (including every 2.x through 2.8.x) are affected.
Workarounds
There is no library-level option to cap the number of footnotes, references, or definitions, so no configuration switch prevents the amplification. Integrators who cannot upgrade should:
- Disable the Footnote extension for untrusted input, or
- Enforce a strict input-size limit before conversion — but note this is a weak control here, since the ~10 KB payload that already triggers the 62 MB / ~440 MB blowup is well within typical request-body limits, so any cap must be aggressively small to help.
Upgrading to the patched release is the recommended remediation.
- Record
GHSA-jfm3-95jq-q3rf- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-407
- Updated
- 2026-11-02
GitHub AdvisoriesGHSA-mj63-m3rc-8ppr
5.3
league/commonmark: Denial of service via deeply nested XML output
Impact
XmlRenderer pretty-prints XML by emitting depth-proportional indentation whitespace for every opening and closing tag. For a tree of depth n, the indentation alone sums to O(n²) bytes of output (and corresponding memory), reachable through MarkdownToXmlConverter — e.g. str_repeat('> ', $depth) . "x\n", a single line of nested blockquotes — or through a direct XmlRenderer::renderDocument() call on an attacker-influenced AST.
This affects applications that convert untrusted Markdown to XML, which is an opt-in output path. The parser's max_nesting_level bounds the depth of parser-created trees, but its default is high enough to reach damaging sizes, can be raised by the host application, and does not constrain custom or programmatically built ASTs handed straight to the renderer. The result is a memory / output-size amplification rather than a hard crash, which is why this issue is rated Medium rather than High. No confidentiality or integrity impact. XML rendering was introduced in 2.0.0 (first shipped in 2.0.0-beta1, June 2021) and has emitted depth-proportional indentation ever since, so all 2.x releases are affected (verified against 2.8.x, clean upstream 1902f60f). 1.x has no XML renderer and is not affected.
Workarounds
Applications converting untrusted Markdown to XML should:
- Lower
max_nesting_levelto a conservative value appropriate to expected content, so the parser refuses to build extremely deep trees. This is the most direct lever for parser-produced ASTs, but does not protect trees built programmatically and passed straight toXmlRenderer. - Cap input size before conversion, since the amplification is driven by input-proportional depth.
- Constrain XML consumers with memory / output-size limits (and streaming or size caps on any downstream XML parser or storage) so one request cannot allocate unbounded output.
- Prefer HTML rendering for untrusted content where XML is not strictly required — the HTML renderer does not emit depth-proportional indentation and is not subject to this amplification.
- Record
GHSA-mj63-m3rc-8ppr- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L - Weakness
- CWE-405
- Updated
- 2026-11-02
Show 11 moreShow less
NVD
6.1
league/commonmark is a PHP library for parsing and rendering CommonMark Markdown. From 1.5.0 until 2.9.0, the AttributesExtension's href and src unsafe-link filter can be bypassed by embedding control bytes, such as a tab, carriage return, line feed, or leading C0 control character, in a javascript: URL that browsers discard before parsing the scheme, causing the browser to still execute the script even when the unsafe-link filter is enabled. This issue is fixed in 2.9.0.
- Record
CVE-2026-71478- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N - Weakness
- CWE-79, CWE-86, CWE-692
- Updated
- 2026-11-02
OSV
6.1
league/commonmark: AttributesExtension href/src unsafe-link filter bypass via embedded control bytes
league/commonmark is a PHP library for parsing and rendering CommonMark Markdown. From 1.5.0 until 2.9.0, the AttributesExtension's href and src unsafe-link filter can be bypassed by embedding control bytes, such as a tab, carriage return, line feed, or leading C0 control character, in a javascript: URL that browsers discard before parsing the scheme, causing the browser to still execute the script even when the unsafe-link filter is enabled. This issue is fixed in 2.9.0.
- Record
CVE-2026-71478- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N - Weakness
- CWE-692, CWE-79, CWE-86
- Updated
- 2026-11-02
OSVDEBIAN-CVE-2026-71478
6.1
league/commonmark is a PHP library for parsing and rendering CommonMark Markdown. From 1.5.0 until 2.9.0, the AttributesExtension's href and src unsafe-link filter can be bypassed by embedding control bytes, such as a tab, carriage return, line feed, or leading C0 control character, in a javascript: URL that browsers discard before parsing the scheme, causing the browser to still execute the script even when the unsafe-link filter is enabled. This issue is fixed in 2.9.0.
- Record
DEBIAN-CVE-2026-71478- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N - Updated
- 2026-11-02
OSVGHSA-29pj-957v-52mc
6.1
league/commonmark: AttributesExtension href/src unsafe-link filter bypass via embedded control bytes
## Summary
The AttributesExtension's href/src unsafe-link filter (AttributesHelper::filterAttributes()) can be bypassed by embedding control bytes in a javascript: URL that browsers discard before parsing the scheme. Two variants:
- Tab/newline inside the scheme — a literal ASCII TAB (0x09), CR (0x0D), or LF (0x0A), 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 from input". - Leading C0 controls — e.g.
<0x01>javascript:alert(1). Per step 1 of the same algorithm, browsers remove any leading or trailing C0 control or space. (A leading space alone does not bypass, becauseparseAttributes()alreadytrim()s the value; other C0 bytes are not trimmed.)
The filter is a literal anchored-prefix regex (RegexHelper::isLinkPotentiallyUnsafe() / REGEX_UNSAFE_PROTOCOL) that matches neither obfuscated form, so in both cases the browser still executes javascript:alert(1).
This is confirmed reproducible even with allow_unsafe_links => false set — i.e. even applications that have followed the library's own documented hardening guidance for untrusted input remain exploitable.
This is a sibling gap in the same defense that CVE-2025-46734 (GHSA-3527-qv2q-pfvx) fixed in v2.7.0 — that fix made href/src respect allow_unsafe_links, but did not normalize control bytes before checking, so these obfuscation techniques were never covered.
Vulnerability
Files:
src/Util/RegexHelper.php:69(REGEX_UNSAFE_PROTOCOL),:239-242(isLinkPotentiallyUnsafe())src/Extension/Attributes/Util/AttributesHelper.php:149-179(filterAttributes())
CWE: CWE-79 (Improper Neutralization of Input During Web Page Generation / XSS) — primary
- CWE-692 (Incomplete Denylist to Cross-Site Scripting) — the anchored-prefix denylist in
REGEX_UNSAFE_PROTOCOLis incomplete. This is a composite of CWE-184 and CWE-79, so it captures the full "incomplete denylist → XSS" chain on its own. - CWE-86 (Improper Neutralization of Invalid Characters in Identifiers in Web Pages) — the specific evasion technique: control bytes embedded within the URI scheme identifier, which the browser strips before resolving it.
Root Cause
// src/Util/RegexHelper.php
public const REGEX_UNSAFE_PROTOCOL = '/^(?:javascript|vbscript|file|data):/i';
public static function isLinkPotentiallyUnsafe(string $url): bool
{
return \preg_match(self::REGEX_UNSAFE_PROTOCOL, $url) !== 0 && \preg_match(self::REGEX_SAFE_DATA_PROTOCOL, $url) === 0;
}
// src/Extension/Attributes/Util/AttributesHelper.php
foreach ($attributes as $name => $value) {
$attrNameLower = \strtolower($name);
if (! $allowUnsafeLinks && ($attrNameLower === 'href' || $attrNameLower === 'src') && \is_string($value) && RegexHelper::isLinkPotentiallyUnsafe($value)) {
unset($attributes[$name]);
continue;
}
...
The Attributes extension's own quote-value grammar (PARTIAL_DOUBLEQUOTEDVALUE = '"[^"]*"') accepts any byte except " inside quotes, including raw tab/CR/LF and other C0 controls, and parseAttributes() only trim()s (leading/trailing, and only the default charlist " \t\n\r\0\x0B" — so a leading \x01 survives). Critically, the core Markdown link-destination path (LinkParserHelper → UrlEncoder::unescapeAndEncode()) percent-encodes every control byte before this same safety check ever runs — but the Attributes extension's href/src handling has no equivalent normalization step, so the raw control byte reaches both the check and the final HTML output (Xml::escape() only escapes & < > " ', not tab/CR/LF, since they're legal bytes inside an HTML attribute).
Attack Scenario
- An application enables the (commonly-used)
AttributesExtensionand setsallow_unsafe_links => false— the project's own documented hardening step for untrusted input. - An attacker submits Markdown: `[Click…
- Record
GHSA-29pj-957v-52mc- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N - Weakness
- CWE-692, CWE-79, CWE-86
- Updated
- 2026-11-02
OSVGHSA-g2gp-3wwq-f4ph
7.5
league/commonmark: Denial of service via adjacent inline attribute blocks
Impact
With the Attributes extension enabled, AttributesListener::findTargetAndDirection() resolves each attribute node's target by walking outward through its siblings. For a run of N adjacent inline attribute blocks placed at the start of a block (with nothing to their left), each node scans the entire sibling list to the far-right end before giving up and falling back to the parent. Each resolution is therefore Θ(N) and the whole run is Θ(N²).
Reaching the path requires AttributesExtension (opt-in, but first-party: League\CommonMark\Extension\Attributes\AttributesExtension). No other configuration matters — the quadratic walk runs unconditionally during parsing and is not gated by the attributes/allow allow-list, the on* hardening added in 2.7.0, or allow_unsafe_links. An unauthenticated attacker can submit a ~32 KB input ({#a} repeated 8,000 times) that takes over 5 seconds to convert, with time growing quadratically in input length — a cheap denial of service. Availability impact only. The Attributes extension was introduced in 1.5.0 (May 2020) with this outward-walk resolver present from the first commit, so all releases from 1.5.0 onward (including every 2.x) are affected.
Workarounds
There is no library-level configuration that gates the quadratic walk. Integrators who cannot upgrade can only reduce exposure indirectly:
- Disable the Attributes extension for untrusted input, or
- Impose a strict maximum input length before conversion — noting that because the cost is quadratic, even a modest cap must be small to meaningfully bound worst-case CPU.
Upgrading to a release containing the fix is recommended.
- Record
GHSA-g2gp-3wwq-f4ph- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-407
- Updated
- 2026-11-02
OSVGHSA-jfm3-95jq-q3rf
7.5
league/commonmark: Denial of service via duplicate footnote definitions
Impact
The Footnote extension records one backref per footnote reference and then appends the entire backref list for every footnote definition block in the document, without ever de-duplicating or removing repeated definitions of the same label (GatherFootnotesListener, populated by NumberFootnotesListener). A document that references a single label N times and also supplies N duplicate [^a]: definitions of that label therefore produces N × N FootnoteBackref nodes, so output size, parse time, and peak memory are all O(N²).
Reaching the vulnerable path requires FootnoteExtension to be registered on the Environment. This is opt-in, but is a commonly enabled GFM-style feature; no other non-default configuration is required. An unauthenticated attacker can expand a ~10 KB request into a ~62 MB HTML response, ~3 s of CPU, and ~440 MB of peak memory — enough to OOM-kill a default 128 MB PHP worker and deny service. Availability impact only; no confidentiality or integrity effect. The Footnote extension was introduced in 1.5.0 (May 2020) with this backref logic present from the first commit, so all releases from 1.5.0 onward (including every 2.x through 2.8.x) are affected.
Workarounds
There is no library-level option to cap the number of footnotes, references, or definitions, so no configuration switch prevents the amplification. Integrators who cannot upgrade should:
- Disable the Footnote extension for untrusted input, or
- Enforce a strict input-size limit before conversion — but note this is a weak control here, since the ~10 KB payload that already triggers the 62 MB / ~440 MB blowup is well within typical request-body limits, so any cap must be aggressively small to help.
Upgrading to the patched release is the recommended remediation.
- Record
GHSA-jfm3-95jq-q3rf- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-407
- Updated
- 2026-11-02
OSVGHSA-mj63-m3rc-8ppr
5.3
league/commonmark: Denial of service via deeply nested XML output
Impact
XmlRenderer pretty-prints XML by emitting depth-proportional indentation whitespace for every opening and closing tag. For a tree of depth n, the indentation alone sums to O(n²) bytes of output (and corresponding memory), reachable through MarkdownToXmlConverter — e.g. str_repeat('> ', $depth) . "x\n", a single line of nested blockquotes — or through a direct XmlRenderer::renderDocument() call on an attacker-influenced AST.
This affects applications that convert untrusted Markdown to XML, which is an opt-in output path. The parser's max_nesting_level bounds the depth of parser-created trees, but its default is high enough to reach damaging sizes, can be raised by the host application, and does not constrain custom or programmatically built ASTs handed straight to the renderer. The result is a memory / output-size amplification rather than a hard crash, which is why this issue is rated Medium rather than High. No confidentiality or integrity impact. XML rendering was introduced in 2.0.0 (first shipped in 2.0.0-beta1, June 2021) and has emitted depth-proportional indentation ever since, so all 2.x releases are affected (verified against 2.8.x, clean upstream 1902f60f). 1.x has no XML renderer and is not affected.
Workarounds
Applications converting untrusted Markdown to XML should:
- Lower
max_nesting_levelto a conservative value appropriate to expected content, so the parser refuses to build extremely deep trees. This is the most direct lever for parser-produced ASTs, but does not protect trees built programmatically and passed straight toXmlRenderer. - Cap input size before conversion, since the amplification is driven by input-proportional depth.
- Constrain XML consumers with memory / output-size limits (and streaming or size caps on any downstream XML parser or storage) so one request cannot allocate unbounded output.
- Prefer HTML rendering for untrusted content where XML is not strictly required — the HTML renderer does not emit depth-proportional indentation and is not subject to this amplification.
- Record
GHSA-mj63-m3rc-8ppr- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L - Weakness
- CWE-405
- Updated
- 2026-11-02
OSVUBUNTU-CVE-2026-71478
6.1
league/commonmark is a PHP library for parsing and rendering CommonMark Markdown. From 1.5.0 until 2.9.0, the AttributesExtension's href and src unsafe-link filter can be bypassed by embedding control bytes, such as a tab, carriage return, line feed, or leading C0 control character, in a javascript: URL that browsers discard before parsing the scheme, causing the browser to still execute the script even when the unsafe-link filter is enabled. This issue is fixed in 2.9.0.
- Record
UBUNTU-CVE-2026-71478- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N - Updated
- 2026-11-02
OSVCLSA-2026-1788257350 No score
TuxCare security update for league/commonmark (4 CVEs)
TuxCare rebuilt league/commonmark to address 4 CVEs. Fixed in 1.6.7-p4+tuxcare.
- Record
CLSA-2026-1788257350- Updated
- 2026-11-02
OSVCLSA-2026-1788257456 No score
TuxCare security update for league/commonmark (6 CVEs)
TuxCare rebuilt league/commonmark to address 6 CVEs. Fixed in 2.8.2-p2+tuxcare.
- Record
CLSA-2026-1788257456- Updated
- 2026-11-02
OSVCLSA-2026-1788257590 No score
TuxCare security update for league/commonmark (6 CVEs)
TuxCare rebuilt league/commonmark to address 6 CVEs. Fixed in 2.7.1-p2+tuxcare.
- Record
CLSA-2026-1788257590- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
league/commonmarkPackagist |
1.5.0 to 2.8.3 | 2.9.0 |
league/commonmarkPackagist |
1.5.0 up to 2.9.0 | 2.9.0 |
league/commonmarkPackagist |
2.0.0 up to 2.9.0 | 2.9.0 |
league/commonmarkTuxCare:Packagist |
1.6.7 up to 1.6.7-p4+tuxcare, plus listed versions 1.6.7-p1+tuxcare1.6.7-p2+tuxcare1.6.7-p3+tuxcare |
1.6.7-p4+tuxcare |
league/commonmarkTuxCare:Packagist |
2.8.2 up to 2.8.2-p2+tuxcare | 2.8.2-p2+tuxcare |
league/commonmarkTuxCare:Packagist |
2.7.1 up to 2.7.1-p2+tuxcare, plus listed versions 2.7.1-p1+tuxcare |
2.7.1-p2+tuxcare |
php-league-commonmarkDebian:12 |
All versions, plus listed versions 2.10.0-12.10.1-12.10.3-12.10.4-12.3.9-1 Show 21 moreShow less2.3.9-1+deb12u12.4.0-12.4.1-12.4.2-12.4.2-22.5.0-12.5.1-12.5.3-12.6.0-12.6.1-12.6.1-22.6.2-12.7.0-12.7.1-12.7.1-22.8.0-12.8.1-12.8.2-12.8.2-22.8.3-12.9.0-1 |
None known |
php-league-commonmarkDebian:13 |
All versions, plus listed versions 2.10.0-12.10.1-12.10.3-12.10.4-12.7.0-1 Show 9 moreShow less2.7.0-1+deb13u12.7.1-12.7.1-22.8.0-12.8.1-12.8.2-12.8.2-22.8.3-12.9.0-1 |
None known |
php-league-commonmarkDebian:14 |
before 2.9.0-1, plus listed versions 2.7.0-12.7.1-12.7.1-22.8.0-12.8.1-12.8.2-12.8.2-22.8.3-1 |
2.9.0-1 |
league/commonmarkPackagist |
1.5.0 up to 2.9.0, plus listed versions 1.5.11.5.21.5.31.5.41.5.5 Show 45 moreShow less1.5.61.5.71.5.81.6.01.6.11.6.21.6.31.6.41.6.51.6.61.6.72.0.02.0.0-beta12.0.0-beta22.0.0-beta32.0.0-rc12.0.0-rc22.0.12.0.22.0.32.0.42.1.02.1.12.1.22.1.32.2.02.2.12.2.22.2.32.2.42.2.52.3.02.3.12.3.22.3.32.3.42.3.52.3.62.3.72.3.82.3.92.4.02.4.12.4.22.4.3 |
2.9.0 |
Show 12 moreShow less
| Package | Affected | Fixed |
|---|---|---|
league/commonmarkPackagist |
2.0.0 up to 2.9.0, plus listed versions 2.0.12.0.22.0.32.0.42.1.0 Show 37 moreShow less2.1.12.1.22.1.32.2.02.2.12.2.22.2.32.2.42.2.52.3.02.3.12.3.22.3.32.3.42.3.52.3.62.3.72.3.82.3.92.4.02.4.12.4.22.4.32.4.42.5.02.5.12.5.22.5.32.6.02.6.12.6.22.7.02.7.12.8.02.8.12.8.22.8.3 |
2.9.0 |
markdownUbuntu:16.04:LTS |
All versions, plus listed versions 1.0.1-71.0.1-8 |
None known |
markdownUbuntu:18.04:LTS |
All versions, plus listed versions 1.0.1-91.0.1-10 |
None known |
markdownUbuntu:20.04:LTS |
All versions, plus listed versions 1.0.1-10 |
None known |
commonmarkUbuntu:22.04:LTS |
All versions, plus listed versions 0.9.1-3 |
None known |
markdownUbuntu:22.04:LTS |
All versions, plus listed versions 1.0.1-10.1 |
None known |
commonmarkUbuntu:24.04:LTS |
All versions, plus listed versions 0.9.1-40.9.1-5 |
None known |
markdownUbuntu:24.04:LTS |
All versions, plus listed versions 1.0.1-12 |
None known |
commonmarkUbuntu:26.10 |
All versions, plus listed versions 0.9.1-8build1 |
None known |
markdownUbuntu:26.10 |
All versions, plus listed versions 1.0.1-14 |
None known |
commonmarkUbuntu:26.04:LTS |
All versions, plus listed versions 0.9.1-80.9.1-8build1 |
None known |
markdownUbuntu:26.04:LTS |
All versions, plus listed versions 1.0.1-14 |
None known |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-71478 is a medium vulnerability (CVSS 6.1) in Thephpleague Commonmark, published August 6, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.36%, the 29th percentile. Fixed in league/commonmark 2.9.0.
References
github.com/thephpleague/commonmark/security/advisories/GHSA-29pj-957v-52mc
github.com/thephpleague/commonmark/commit/493a5aa7d65754b73846006eaff9c2c4431a8e2c
github.com/thephpleague/commonmark/releases/tag/2.9.0
github.com/advisories/GHSA-29pj-957v-52mc
github.com/thephpleague/commonmark/security/advisories/GHSA-g2gp-3wwq-f4ph
github.com/thephpleague/commonmark/commit/2d4c0fafa62501be919262064cffa6d71687430b
Show 13 moreShow less
github.com/advisories/GHSA-g2gp-3wwq-f4ph
github.com/thephpleague/commonmark/security/advisories/GHSA-jfm3-95jq-q3rf
github.com/thephpleague/commonmark/commit/66028124a17ba193da7b11cc3dfda92df21bfbf4
github.com/advisories/GHSA-jfm3-95jq-q3rf
github.com/thephpleague/commonmark/security/advisories/GHSA-mj63-m3rc-8ppr
github.com/thephpleague/commonmark/commit/b5ac8c3947ca81844e85a09c7e0a5b4148bde2e1
github.com/advisories/GHSA-mj63-m3rc-8ppr
github.com/CVEProject/cvelistV5/tree/main/cves/2026/71xxx/CVE-2026-71478.json
nvd.nist.gov/vuln/detail/CVE-2026-71478
security-tracker.debian.org/tracker/CVE-2026-71478
github.com/thephpleague/commonmark
ubuntu.com/security/CVE-2026-71478
cve.org/CVERecord?id=CVE-2026-71478
Left out here: 14 versions and the rest of 2 source descriptions. The JSON record has them.