{"id":"CVE-2026-71478","aliases":["CLSA-2026-1788257350","CLSA-2026-1788257456","CLSA-2026-1788257590","DEBIAN-CVE-2026-71478","EUVD-2026-54261","GHSA-29pj-957v-52mc","GHSA-g2gp-3wwq-f4ph","GHSA-jfm3-95jq-q3rf","GHSA-mh25-x5hq-wrqp","GHSA-mj63-m3rc-8ppr","UBUNTU-CVE-2026-71478"],"status":"published","title":"league/commonmark: AttributesExtension href/src unsafe-link filter bypass via embedded control bytes","description":"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.","severity":{"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","from":"cve"},"epss":{"day":"2026-11-12","score":0.00355,"percentile":0.29642,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-405","CWE-407","CWE-692","CWE-79","CWE-86"],"published":"2026-08-06T20:30:32Z","modified":"2026-10-28T02:30:21Z","fixes":[{"ecosystem":"Packagist","package":"league/commonmark","fixed_in":["2.9.0"],"sources":["ghsa","osv"]},{"ecosystem":"Debian:14","package":"php-league-commonmark","fixed_in":["2.9.0-1"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"Packagist","package":"league/commonmark","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.5.0"},{"last_affected":"2.8.3"},{"fixed":"2.9.0"}]}]},{"source":"ghsa","ecosystem":"Packagist","package":"league/commonmark","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.5.0"},{"fixed":"2.9.0"}]}]},{"source":"ghsa","ecosystem":"Packagist","package":"league/commonmark","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.5.0"},{"fixed":"2.9.0"}]}]},{"source":"ghsa","ecosystem":"Packagist","package":"league/commonmark","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"2.0.0"},{"fixed":"2.9.0"}]}]},{"source":"osv","ecosystem":"TuxCare:Packagist","package":"league/commonmark","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.6.7"},{"fixed":"1.6.7-p4+tuxcare"}]}],"versions":["1.6.7","1.6.7-p1+tuxcare","1.6.7-p2+tuxcare","1.6.7-p3+tuxcare"],"bundled":2},{"source":"osv","ecosystem":"TuxCare:Packagist","package":"league/commonmark","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"2.8.2"},{"fixed":"2.8.2-p2+tuxcare"}]}],"versions":["2.8.2"],"bundled":2},{"source":"osv","ecosystem":"TuxCare:Packagist","package":"league/commonmark","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"2.7.1"},{"fixed":"2.7.1-p2+tuxcare"}]}],"versions":["2.7.1","2.7.1-p1+tuxcare"],"bundled":2},{"source":"osv","ecosystem":"Debian:12","package":"php-league-commonmark","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2.10.0-1","2.10.1-1","2.10.3-1","2.10.4-1","2.3.9-1","2.3.9-1+deb12u1","2.4.0-1","2.4.1-1","2.4.2-1","2.4.2-2","2.5.0-1","2.5.1-1","2.5.3-1","2.6.0-1","2.6.1-1","2.6.1-2","2.6.2-1","2.7.0-1","2.7.1-1","2.7.1-2","2.8.0-1","2.8.1-1","2.8.2-1","2.8.2-2","2.8.3-1","2.9.0-1"]},{"source":"osv","ecosystem":"Debian:13","package":"php-league-commonmark","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2.10.0-1","2.10.1-1","2.10.3-1","2.10.4-1","2.7.0-1","2.7.0-1+deb13u1","2.7.1-1","2.7.1-2","2.8.0-1","2.8.1-1","2.8.2-1","2.8.2-2","2.8.3-1","2.9.0-1"]},{"source":"osv","ecosystem":"Debian:14","package":"php-league-commonmark","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.9.0-1"}]}],"versions":["2.7.0-1","2.7.1-1","2.7.1-2","2.8.0-1","2.8.1-1","2.8.2-1","2.8.2-2","2.8.3-1"]},{"source":"osv","ecosystem":"Packagist","package":"league/commonmark","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.5.0"},{"fixed":"2.9.0"}]}],"versions":["1.5.0","1.5.1","1.5.2","1.5.3","1.5.4","1.5.5","1.5.6","1.5.7","1.5.8","1.6.0","1.6.1","1.6.2","1.6.3","1.6.4","1.6.5","1.6.6","1.6.7","2.0.0","2.0.0-beta1","2.0.0-beta2","2.0.0-beta3","2.0.0-rc1","2.0.0-rc2","2.0.1","2.0.2","2.0.3","2.0.4","2.1.0","2.1.1","2.1.2","2.1.3","2.2.0","2.2.1","2.2.2","2.2.3","2.2.4","2.2.5","2.3.0","2.3.1","2.3.2","2.3.3","2.3.4","2.3.5","2.3.6","2.3.7","2.3.8","2.3.9","2.4.0","2.4.1","2.4.2","2.4.3","2.4.4","2.5.0","2.5.1","2.5.2","2.5.3","2.6.0","2.6.1","2.6.2","2.7.0","2.7.1","2.8.0","2.8.1","2.8.2","2.8.3"]},{"source":"osv","ecosystem":"Packagist","package":"league/commonmark","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.5.0"},{"fixed":"2.9.0"}]}],"versions":["1.5.0","1.5.1","1.5.2","1.5.3","1.5.4","1.5.5","1.5.6","1.5.7","1.5.8","1.6.0","1.6.1","1.6.2","1.6.3","1.6.4","1.6.5","1.6.6","1.6.7","2.0.0","2.0.0-beta1","2.0.0-beta2","2.0.0-beta3","2.0.0-rc1","2.0.0-rc2","2.0.1","2.0.2","2.0.3","2.0.4","2.1.0","2.1.1","2.1.2","2.1.3","2.2.0","2.2.1","2.2.2","2.2.3","2.2.4","2.2.5","2.3.0","2.3.1","2.3.2","2.3.3","2.3.4","2.3.5","2.3.6","2.3.7","2.3.8","2.3.9","2.4.0","2.4.1","2.4.2","2.4.3","2.4.4","2.5.0","2.5.1","2.5.2","2.5.3","2.6.0","2.6.1","2.6.2","2.7.0","2.7.1","2.8.0","2.8.1","2.8.2","2.8.3"]},{"source":"osv","ecosystem":"Packagist","package":"league/commonmark","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.5.0"},{"fixed":"2.9.0"}]}],"versions":["1.5.0","1.5.1","1.5.2","1.5.3","1.5.4","1.5.5","1.5.6","1.5.7","1.5.8","1.6.0","1.6.1","1.6.2","1.6.3","1.6.4","1.6.5","1.6.6","1.6.7","2.0.0","2.0.0-beta1","2.0.0-beta2","2.0.0-beta3","2.0.0-rc1","2.0.0-rc2","2.0.1","2.0.2","2.0.3","2.0.4","2.1.0","2.1.1","2.1.2","2.1.3","2.2.0","2.2.1","2.2.2","2.2.3","2.2.4","2.2.5","2.3.0","2.3.1","2.3.2","2.3.3","2.3.4","2.3.5","2.3.6","2.3.7","2.3.8","2.3.9","2.4.0","2.4.1","2.4.2","2.4.3","2.4.4","2.5.0","2.5.1","2.5.2","2.5.3","2.6.0","2.6.1","2.6.2","2.7.0","2.7.1","2.8.0","2.8.1","2.8.2","2.8.3"]},{"source":"osv","ecosystem":"Packagist","package":"league/commonmark","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"2.0.0"},{"fixed":"2.9.0"}]}],"versions":["2.0.0","2.0.1","2.0.2","2.0.3","2.0.4","2.1.0","2.1.1","2.1.2","2.1.3","2.2.0","2.2.1","2.2.2","2.2.3","2.2.4","2.2.5","2.3.0","2.3.1","2.3.2","2.3.3","2.3.4","2.3.5","2.3.6","2.3.7","2.3.8","2.3.9","2.4.0","2.4.1","2.4.2","2.4.3","2.4.4","2.5.0","2.5.1","2.5.2","2.5.3","2.6.0","2.6.1","2.6.2","2.7.0","2.7.1","2.8.0","2.8.1","2.8.2","2.8.3"]},{"source":"osv","ecosystem":"Ubuntu:16.04:LTS","package":"markdown","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["1.0.1-7","1.0.1-8"]},{"source":"osv","ecosystem":"Ubuntu:18.04:LTS","package":"markdown","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["1.0.1-9","1.0.1-10"]},{"source":"osv","ecosystem":"Ubuntu:20.04:LTS","package":"markdown","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["1.0.1-10"]},{"source":"osv","ecosystem":"Ubuntu:22.04:LTS","package":"commonmark","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.9.1-3"]},{"source":"osv","ecosystem":"Ubuntu:22.04:LTS","package":"markdown","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["1.0.1-10.1"]},{"source":"osv","ecosystem":"Ubuntu:24.04:LTS","package":"commonmark","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.9.1-4","0.9.1-5"]},{"source":"osv","ecosystem":"Ubuntu:24.04:LTS","package":"markdown","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["1.0.1-12"]},{"source":"osv","ecosystem":"Ubuntu:26.10","package":"commonmark","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.9.1-8build1"]},{"source":"osv","ecosystem":"Ubuntu:26.10","package":"markdown","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["1.0.1-14"]},{"source":"osv","ecosystem":"Ubuntu:26.04:LTS","package":"commonmark","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.9.1-8","0.9.1-8build1"]},{"source":"osv","ecosystem":"Ubuntu:26.04:LTS","package":"markdown","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["1.0.1-14"]}],"products":[{"vendor":"thephpleague","product":"commonmark"}],"references":[{"url":"https://github.com/thephpleague/commonmark/security/advisories/GHSA-29pj-957v-52mc","tags":["x_refsource_CONFIRM","exploit","advisory","web","report"]},{"url":"https://github.com/thephpleague/commonmark/commit/493a5aa7d65754b73846006eaff9c2c4431a8e2c","tags":["x_refsource_MISC","fix","web","report"]},{"url":"https://github.com/thephpleague/commonmark/releases/tag/2.9.0","tags":["x_refsource_MISC","web","report"]},{"url":"https://github.com/advisories/GHSA-29pj-957v-52mc"},{"url":"https://github.com/thephpleague/commonmark/security/advisories/GHSA-g2gp-3wwq-f4ph","tags":["web"]},{"url":"https://github.com/thephpleague/commonmark/commit/2d4c0fafa62501be919262064cffa6d71687430b","tags":["web"]},{"url":"https://github.com/advisories/GHSA-g2gp-3wwq-f4ph"},{"url":"https://github.com/thephpleague/commonmark/security/advisories/GHSA-jfm3-95jq-q3rf","tags":["web"]},{"url":"https://github.com/thephpleague/commonmark/commit/66028124a17ba193da7b11cc3dfda92df21bfbf4","tags":["web"]},{"url":"https://github.com/advisories/GHSA-jfm3-95jq-q3rf"},{"url":"https://github.com/thephpleague/commonmark/security/advisories/GHSA-mj63-m3rc-8ppr","tags":["web"]},{"url":"https://github.com/thephpleague/commonmark/commit/b5ac8c3947ca81844e85a09c7e0a5b4148bde2e1","tags":["web"]},{"url":"https://github.com/advisories/GHSA-mj63-m3rc-8ppr"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/71xxx/CVE-2026-71478.json","tags":["advisory"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-71478","tags":["advisory"]},{"url":"https://security-tracker.debian.org/tracker/CVE-2026-71478","tags":["advisory"]},{"url":"https://github.com/thephpleague/commonmark","tags":["package"]},{"url":"https://ubuntu.com/security/CVE-2026-71478","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-71478","tags":["report"]}],"sources":{"cve":[{"id":"CVE-2026-71478","title":"league/commonmark: AttributesExtension href/src unsafe-link filter bypass via embedded control bytes","description":"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.","status":"published","published":"2026-08-06T20:30:32.045Z","modified":"2026-08-07T17:05:57.244Z","cvss":[{"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-79","CWE-86","CWE-692"],"updated_at":"2026-11-02T14:19:49.559463Z"}],"euvd":[{"id":"EUVD-2026-54261","description":"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.","status":"published","published":"2026-08-06T20:30:32Z","modified":"2026-08-07T17:05:57Z","cvss":[{"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"}],"updated_at":"2026-11-02T18:02:06.930016Z"}],"ghsa":[{"id":"GHSA-29pj-957v-52mc","title":"league/commonmark: AttributesExtension href/src unsafe-link filter bypass via embedded control bytes","description":"﻿## Summary\n\nThe `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:\n\n- **Tab/newline inside the scheme** — a literal ASCII TAB (0x09), CR (0x0D), or LF (0x0A), e.g. `java\u003cTAB\u003escript:alert(1)`. Per the WHATWG URL Standard's \"basic URL parser\" step 3, browsers \"remove all ASCII tab or newline from input\".\n- **Leading C0 controls** — e.g. `\u003c0x01\u003ejavascript: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, because `parseAttributes()` already `trim()`s the value; other C0 bytes are not trimmed.)\n\nThe 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)`.\n\n**This is confirmed reproducible even with `allow_unsafe_links =\u003e false` set** — i.e. even applications that have followed the library's own documented hardening guidance for untrusted input remain exploitable.\n\nThis 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.\n\n## Vulnerability\n\n**Files**:\n- `src/Util/RegexHelper.php:69` (`REGEX_UNSAFE_PROTOCOL`), `:239-242` (`isLinkPotentiallyUnsafe()`)\n- `src/Extension/Attributes/Util/AttributesHelper.php:149-179` (`filterAttributes()`)\n\n**CWE**: CWE-79 (Improper Neutralization of Input During Web Page Generation / XSS) — primary\n- CWE-692 (Incomplete Denylist to Cross-Site Scripting) — the anchored-prefix denylist in `REGEX_UNSAFE_PROTOCOL` is incomplete. This is a composite of CWE-184 and CWE-79, so it captures the full \"incomplete denylist → XSS\" chain on its own.\n- 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.\n\n### Root Cause\n```php\n// src/Util/RegexHelper.php\npublic const REGEX_UNSAFE_PROTOCOL = '/^(?:javascript|vbscript|file|data):/i';\n\npublic static function isLinkPotentiallyUnsafe(string $url): bool\n{\n    return \\preg_match(self::REGEX_UNSAFE_PROTOCOL, $url) !== 0 \u0026\u0026 \\preg_match(self::REGEX_SAFE_DATA_PROTOCOL, $url) === 0;\n}\n\n// src/Extension/Attributes/Util/AttributesHelper.php\nforeach ($attributes as $name =\u003e $value) {\n    $attrNameLower = \\strtolower($name);\n    if (! $allowUnsafeLinks \u0026\u0026 ($attrNameLower === 'href' || $attrNameLower === 'src') \u0026\u0026 \\is_string($value) \u0026\u0026 RegexHelper::isLinkPotentiallyUnsafe($value)) {\n        unset($attributes[$name]);\n        continue;\n    }\n    ...\n```\nThe 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 `\u0026 \u003c \u003e \" '`, not tab/CR/LF, since they're legal bytes inside an HTML attribute).\n\n### Attack Scenario\n1. An application enables the (commonly-used) `AttributesExtension` and sets `allow_unsafe_links =\u003e false` — the project's own documented hardening step for untrusted input.\n2. An attacker submits Markdown: `[Click me](javascript:alert(0)){href=\"java\u003cTAB\u003escript:alert(document.cookie)\"}` (TAB is one literal 0x09 byte).\n3. The library emits `\u003ca href=\"java\u003cTAB\u003escript:alert(document.cookie)\"\u003eClick me\u003c/a\u003e` — `isLinkPotentiallyUnsafe()` doesn't match the tab-split scheme, so the filter takes no action.\n4. A victim viewing/clicking the link has the browser strip the embedded TAB and execute `javascript:alert(document.cookie)` in the victim's session — stored XSS, cookie theft, account takeover potential.\n\n**Why the payload needs an unsafe core destination.** Step 2 above deliberately uses `[Click me](javascript:alert(0))` rather than a normal link. `LinkRenderer` overwrites `attrs['href']` with the node's own URL *unless* that URL is itself judged unsafe — so `[x](https://example.com){href=\"java\u003cTAB\u003escript:...\"}` renders the harmless `href=\"https://example.com\"`, and an empty destination `[x](){href=\"...\"}` renders `href=\"\"`. The attacker therefore supplies a core destination that the filter *does* catch, which suppresses the overwrite and lets the attribute-supplied `href` reach the final tag. This is no obstacle in practice — the attacker writes the entire Markdown document.\n\nTwo related forms that are **not** exploitable, noted so the fix isn't over-scoped:\n\n- Attaching the attribute to a non-link block — `hi {href=\"java\u003cTAB\u003escript:alert(1)\"}` — does bypass the filter and emits `\u003cp href=\"java\u003cTAB\u003escript:alert(1)\"\u003e`, but `href` on a `\u003cp\u003e` is inert: there is nothing to navigate. (An earlier draft of this report described this as a \"simpler, unconditional variant\" of the attack; it is a filter bypass, not an XSS.)\n- `\u003cimg src\u003e` is unaffected, since `ImageRenderer` unconditionally overwrites `src` from the core URL regardless of the safety verdict.\n\n### Recommended Fix\n\nNormalize inside `RegexHelper::isLinkPotentiallyUnsafe()` before testing, mirroring the WHATWG URL parser's own normalization. This covers both variants, fixes every call site at once (`LinkRenderer`, `ImageRenderer`, and any third-party callers), and needs no changes in the Attributes extension.\n\n## Affected Versions\n\n**`\u003e= 1.5.0, \u003c= 2.8.3`** - every release that ships the `AttributesExtension`. Verified by installing each version and rendering the payloads with `allow_unsafe_links =\u003e false`. The attribute-value grammar (`PARTIAL_DOUBLEQUOTEDVALUE = '\"[^\"]*\"'`) has accepted raw control bytes since the extension was introduced, and none of the intervening parser rewrites narrowed it.\n\n## Prior Related Advisories\n\nGHSA-3527-qv2q-pfvx / CVE-2025-46734 fixed a different Attributes-extension XSS (unallowlisted `on*` handlers, `href`/`src` not respecting `allow_unsafe_links` at all) in v2.7.0. This issue bypasses the specific `href`/`src` protection that fix introduced (the control-byte normalization gap was not part of that fix) - but the obfuscated inputs also work on older versions.","status":"published","published":"2026-08-06T20:30:39Z","modified":"2026-08-06T20:30:40Z","cvss":[{"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-79","CWE-86","CWE-692"],"updated_at":"2026-11-02T14:40:57.999375Z"},{"id":"GHSA-g2gp-3wwq-f4ph","title":"league/commonmark: Denial of service via adjacent inline attribute blocks","description":"### Impact\n\nWith 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²)**.\n\nReaching 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.**\n\n### Workarounds\n\nThere is no library-level configuration that gates the quadratic walk. Integrators who cannot upgrade can only reduce exposure indirectly:\n\n- **Disable the Attributes extension** for untrusted input, or\n- **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.\n\nUpgrading to a release containing the fix is recommended.","status":"published","published":"2026-08-06T20:39:52Z","modified":"2026-08-06T20:39:53Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"cwes":["CWE-407"],"updated_at":"2026-11-02T14:40:57.999375Z"},{"id":"GHSA-jfm3-95jq-q3rf","title":"league/commonmark:  Denial of service via duplicate footnote definitions","description":"### Impact\n\nThe 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²)**.\n\nReaching 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.**\n\n### Workarounds\n\nThere 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:\n\n- **Disable the Footnote extension** for untrusted input, or\n- **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.\n\nUpgrading to the patched release is the recommended remediation.","status":"published","published":"2026-08-06T20:40:53Z","modified":"2026-08-06T20:42:11Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"cwes":["CWE-407"],"updated_at":"2026-11-02T14:40:57.999375Z"},{"id":"GHSA-mj63-m3rc-8ppr","title":"league/commonmark: Denial of service via deeply nested XML output","description":"### Impact\n\n`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('\u003e ', $depth) . \"x\\n\"`, a single line of nested blockquotes — or through a direct `XmlRenderer::renderDocument()` call on an attacker-influenced AST.\n\nThis 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.\n\n### Workarounds\n\nApplications converting untrusted Markdown to XML should:\n\n- **Lower `max_nesting_level`** to 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 to `XmlRenderer`.\n- **Cap input size before conversion**, since the amplification is driven by input-proportional depth.\n- **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.\n- **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.","status":"published","published":"2026-08-06T20:42:54Z","modified":"2026-08-06T20:42:56Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L","level":"medium"}],"cwes":["CWE-405"],"updated_at":"2026-11-02T14:40:57.999375Z"}],"nvd":[{"id":"CVE-2026-71478","description":"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.","status":"published","published":"2026-08-06T22:18:31.75Z","modified":"2026-10-24T15:10:00.19Z","cvss":[{"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-79","CWE-86","CWE-692"],"updated_at":"2026-11-02T14:12:35.386166Z"}],"osv":[{"id":"CVE-2026-71478","title":"league/commonmark: AttributesExtension href/src unsafe-link filter bypass via embedded control bytes","description":"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.","status":"published","published":"2026-08-06T20:30:32.045Z","modified":"2026-10-28T02:30:21.813656968Z","cvss":[{"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-692","CWE-79","CWE-86"],"updated_at":"2026-11-02T15:45:15.164658Z"},{"id":"DEBIAN-CVE-2026-71478","description":"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.","status":"published","published":"2026-08-06T22:18:31.75Z","modified":"2026-10-25T09:47:35.771987994Z","cvss":[{"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"}],"updated_at":"2026-11-02T15:46:02.094661Z"},{"id":"GHSA-29pj-957v-52mc","title":"league/commonmark: AttributesExtension href/src unsafe-link filter bypass via embedded control bytes","description":"﻿## Summary\n\nThe `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:\n\n- **Tab/newline inside the scheme** — a literal ASCII TAB (0x09), CR (0x0D), or LF (0x0A), e.g. `java\u003cTAB\u003escript:alert(1)`. Per the WHATWG URL Standard's \"basic URL parser\" step 3, browsers \"remove all ASCII tab or newline from input\".\n- **Leading C0 controls** — e.g. `\u003c0x01\u003ejavascript: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, because `parseAttributes()` already `trim()`s the value; other C0 bytes are not trimmed.)\n\nThe 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)`.\n\n**This is confirmed reproducible even with `allow_unsafe_links =\u003e false` set** — i.e. even applications that have followed the library's own documented hardening guidance for untrusted input remain exploitable.\n\nThis 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.\n\n## Vulnerability\n\n**Files**:\n- `src/Util/RegexHelper.php:69` (`REGEX_UNSAFE_PROTOCOL`), `:239-242` (`isLinkPotentiallyUnsafe()`)\n- `src/Extension/Attributes/Util/AttributesHelper.php:149-179` (`filterAttributes()`)\n\n**CWE**: CWE-79 (Improper Neutralization of Input During Web Page Generation / XSS) — primary\n- CWE-692 (Incomplete Denylist to Cross-Site Scripting) — the anchored-prefix denylist in `REGEX_UNSAFE_PROTOCOL` is incomplete. This is a composite of CWE-184 and CWE-79, so it captures the full \"incomplete denylist → XSS\" chain on its own.\n- 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.\n\n### Root Cause\n```php\n// src/Util/RegexHelper.php\npublic const REGEX_UNSAFE_PROTOCOL = '/^(?:javascript|vbscript|file|data):/i';\n\npublic static function isLinkPotentiallyUnsafe(string $url): bool\n{\n    return \\preg_match(self::REGEX_UNSAFE_PROTOCOL, $url) !== 0 \u0026\u0026 \\preg_match(self::REGEX_SAFE_DATA_PROTOCOL, $url) === 0;\n}\n\n// src/Extension/Attributes/Util/AttributesHelper.php\nforeach ($attributes as $name =\u003e $value) {\n    $attrNameLower = \\strtolower($name);\n    if (! $allowUnsafeLinks \u0026\u0026 ($attrNameLower === 'href' || $attrNameLower === 'src') \u0026\u0026 \\is_string($value) \u0026\u0026 RegexHelper::isLinkPotentiallyUnsafe($value)) {\n        unset($attributes[$name]);\n        continue;\n    }\n    ...\n```\nThe 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 `\u0026 \u003c \u003e \" '`, not tab/CR/LF, since they're legal bytes inside an HTML attribute).\n\n### Attack Scenario\n1. An application enables the (commonly-used) `AttributesExtension` and sets `allow_unsafe_links =\u003e false` — the project's own documented hardening step for untrusted input.\n2. An attacker submits Markdown: `[Click me](javascript:alert(0)){href=\"java\u003cTAB\u003escript:alert(document.cookie)\"}` (TAB is one literal 0x09 byte).\n3. The library emits `\u003ca href=\"java\u003cTAB\u003escript:alert(document.cookie)\"\u003eClick me\u003c/a\u003e` — `isLinkPotentiallyUnsafe()` doesn't match the tab-split scheme, so the filter takes no action.\n4. A victim viewing/clicking the link has the browser strip the embedded TAB and execute `javascript:alert(document.cookie)` in the victim's session — stored XSS, cookie theft, account takeover potential.\n\n**Why the payload needs an unsafe core destination.** Step 2 above deliberately uses `[Click me](javascript:alert(0))` rather than a normal link. `LinkRenderer` overwrites `attrs['href']` with the node's own URL *unless* that URL is itself judged unsafe — so `[x](https://example.com){href=\"java\u003cTAB\u003escript:...\"}` renders the harmless `href=\"https://example.com\"`, and an empty destination `[x](){href=\"...\"}` renders `href=\"\"`. The attacker therefore supplies a core destination that the filter *does* catch, which suppresses the overwrite and lets the attribute-supplied `href` reach the final tag. This is no obstacle in practice — the attacker writes the entire Markdown document.\n\nTwo related forms that are **not** exploitable, noted so the fix isn't over-scoped:\n\n- Attaching the attribute to a non-link block — `hi {href=\"java\u003cTAB\u003escript:alert(1)\"}` — does bypass the filter and emits `\u003cp href=\"java\u003cTAB\u003escript:alert(1)\"\u003e`, but `href` on a `\u003cp\u003e` is inert: there is nothing to navigate. (An earlier draft of this report described this as a \"simpler, unconditional variant\" of the attack; it is a filter bypass, not an XSS.)\n- `\u003cimg src\u003e` is unaffected, since `ImageRenderer` unconditionally overwrites `src` from the core URL regardless of the safety verdict.\n\n### Recommended Fix\n\nNormalize inside `RegexHelper::isLinkPotentiallyUnsafe()` before testing, mirroring the WHATWG URL parser's own normalization. This covers both variants, fixes every call site at once (`LinkRenderer`, `ImageRenderer`, and any third-party callers), and needs no changes in the Attributes extension.\n\n## Affected Versions\n\n**`\u003e= 1.5.0, \u003c= 2.8.3`** - every release that ships the `AttributesExtension`. Verified by installing each version and rendering the payloads with `allow_unsafe_links =\u003e false`. The attribute-value grammar (`PARTIAL_DOUBLEQUOTEDVALUE = '\"[^\"]*\"'`) has accepted raw control bytes since the extension was introduced, and none of the intervening parser rewrites narrowed it.\n\n## Prior Related Advisories\n\nGHSA-3527-qv2q-pfvx / CVE-2025-46734 fixed a different Attributes-extension XSS (unallowlisted `on*` handlers, `href`/`src` not respecting `allow_unsafe_links` at all) in v2.7.0. This issue bypasses the specific `href`/`src` protection that fix introduced (the control-byte normalization gap was not part of that fix) - but the obfuscated inputs also work on older versions.","status":"published","published":"2026-08-06T20:30:39Z","modified":"2026-08-21T05:28:22.850809866Z","cvss":[{"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-692","CWE-79","CWE-86"],"updated_at":"2026-11-02T15:46:33.956911Z"},{"id":"GHSA-g2gp-3wwq-f4ph","title":"league/commonmark: Denial of service via adjacent inline attribute blocks","description":"### Impact\n\nWith 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²)**.\n\nReaching 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.**\n\n### Workarounds\n\nThere is no library-level configuration that gates the quadratic walk. Integrators who cannot upgrade can only reduce exposure indirectly:\n\n- **Disable the Attributes extension** for untrusted input, or\n- **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.\n\nUpgrading to a release containing the fix is recommended.","status":"published","published":"2026-08-06T20:39:52Z","modified":"2026-08-06T20:45:15.558862305Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"cwes":["CWE-407"],"updated_at":"2026-11-02T15:46:47.119724Z"},{"id":"GHSA-jfm3-95jq-q3rf","title":"league/commonmark:  Denial of service via duplicate footnote definitions","description":"### Impact\n\nThe 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²)**.\n\nReaching 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.**\n\n### Workarounds\n\nThere 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:\n\n- **Disable the Footnote extension** for untrusted input, or\n- **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.\n\nUpgrading to the patched release is the recommended remediation.","status":"published","published":"2026-08-06T20:40:53Z","modified":"2026-08-06T20:45:15.56408473Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"cwes":["CWE-407"],"updated_at":"2026-11-02T15:46:50.336262Z"},{"id":"GHSA-mj63-m3rc-8ppr","title":"league/commonmark: Denial of service via deeply nested XML output","description":"### Impact\n\n`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('\u003e ', $depth) . \"x\\n\"`, a single line of nested blockquotes — or through a direct `XmlRenderer::renderDocument()` call on an attacker-influenced AST.\n\nThis 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.\n\n### Workarounds\n\nApplications converting untrusted Markdown to XML should:\n\n- **Lower `max_nesting_level`** to 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 to `XmlRenderer`.\n- **Cap input size before conversion**, since the amplification is driven by input-proportional depth.\n- **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.\n- **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.","status":"published","published":"2026-08-06T20:42:54Z","modified":"2026-08-06T20:45:15.556829535Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L","level":"medium"}],"cwes":["CWE-405"],"updated_at":"2026-11-02T15:46:51.811331Z"},{"id":"UBUNTU-CVE-2026-71478","description":"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.","status":"published","published":"2026-08-06T22:18:00Z","modified":"2026-10-17T13:00:44.815170484Z","cvss":[{"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"}],"updated_at":"2026-11-02T16:57:03.035847Z"},{"id":"CLSA-2026-1788257350","title":"TuxCare security update for league/commonmark (4 CVEs)","description":"TuxCare rebuilt league/commonmark to address 4 CVEs. Fixed in 1.6.7-p4+tuxcare.","status":"published","published":"2026-09-01T10:09:10Z","modified":"2026-10-15T09:48:00.582192094Z","updated_at":"2026-11-02T15:43:47.591214Z"},{"id":"CLSA-2026-1788257456","title":"TuxCare security update for league/commonmark (6 CVEs)","description":"TuxCare rebuilt league/commonmark to address 6 CVEs. Fixed in 2.8.2-p2+tuxcare.","status":"published","published":"2026-09-01T10:10:56Z","modified":"2026-10-15T09:48:00.493236951Z","updated_at":"2026-11-02T15:43:47.591214Z"},{"id":"CLSA-2026-1788257590","title":"TuxCare security update for league/commonmark (6 CVEs)","description":"TuxCare rebuilt league/commonmark to address 6 CVEs. Fixed in 2.7.1-p2+tuxcare.","status":"published","published":"2026-09-01T10:13:10Z","modified":"2026-10-15T09:48:00.463295018Z","updated_at":"2026-11-02T15:43:47.591214Z"}]}}
