{"id":"GHSA-mh25-x5hq-wrqp","summary":"league/commonmark: Denial of service via colliding heading slugs","details":"### Impact\n\n`UniqueSlugNormalizer::normalize()` makes each slug document-unique by searching for an unused numeric suffix, but **restarts that search from `1` on every collision**. The k-th heading that collapses to the same base slug performs k−1 array lookups, so K colliding slugs cost Σ(k−1) = **O(K²)**. An attacker can force every heading onto a single base slug trivially — many empty ATX headings, identical heading text, or punctuation-only headings that normalize to the empty string.\n\nThe path is reached whenever the shared slug normalizer runs over attacker-controlled text. That happens when `HeadingPermalinkExtension` is registered (its `HeadingPermalinkProcessor` normalizes every heading), independently through `FootnoteExtension` (its `AnonymousFootnoteRefParser` normalizes every `^[label]` reference), and on any `TableOfContentsExtension` site (which requires `HeadingPermalinkExtension` to be co-registered). The default `slug_normalizer/unique` setting (`UniqueSlugNormalizerInterface::PER_DOCUMENT`) accumulates collisions across the whole document. No authentication is required — a small document body turns into seconds of CPU and denies service. Availability impact only. **`UniqueSlugNormalizer` was introduced in 2.0.0 (first shipped in 2.0.0-beta1, May 2021); the 1.x heading-permalink slug generator performed no de-duplication and is not affected. All 2.x releases (including 2.8.x) are affected.**\n\n### Workarounds\n\nIntegrators who cannot upgrade immediately can:\n\n- **Set `slug_normalizer/unique` to `false` / `UniqueSlugNormalizerInterface::DISABLED`**, which stops the de-duplication scan entirely — at the cost of losing id uniqueness (colliding headings then share an anchor).\n- **Disable `HeadingPermalinkExtension`** (and `TableOfContentsExtension`, which depends on it), and `FootnoteExtension` where anonymous footnotes reach the same normalizer, for untrusted Markdown.\n- **Cap the accepted document size / heading count upstream** so K cannot reach the quadratic danger zone.\n\nEach of these trades off functionality or correctness; upgrading to the patched release (which removes the quadratic behavior while keeping unique ids and identical output) is the recommended remediation.","modified":"2026-08-06T20:45:15.564635612Z","published":"2026-08-06T20:41:45Z","database_specific":{"nvd_published_at":null,"cwe_ids":["CWE-407"],"severity":"HIGH","github_reviewed":true,"github_reviewed_at":"2026-08-06T20:41:45Z"},"references":[{"type":"WEB","url":"https://github.com/thephpleague/commonmark/security/advisories/GHSA-mh25-x5hq-wrqp"},{"type":"PACKAGE","url":"https://github.com/thephpleague/commonmark"},{"type":"WEB","url":"https://github.com/thephpleague/commonmark/releases/tag/2.9.0"}],"affected":[{"package":{"name":"league/commonmark","ecosystem":"Packagist","purl":"pkg:composer/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"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/08/GHSA-mh25-x5hq-wrqp/GHSA-mh25-x5hq-wrqp.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"}]}