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
- highCVE-2026-67434CVE-2026-67434: PHP_CodeSniffer OS Command Injection via Crafted Filename in Blame Reports
- highCraft CMS Improper Authorization: Arbitrary Password Reset via Element Save
- high · 8.1CVE-2026-64665CVE-2026-64665: Statamic CMS OAuth Account Takeover via Unverified Email Matching