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
- critical · 9.4CVE-2026-63221CVE-2026-63221: CodeIgniter4 Query Builder SQL Injection via deleteBatch() and where()
- high · 7.5CVE-2026-63222CVE-2026-63222: CodeIgniter4 Path Traversal in UploadedFile::move()
- critical · 9.8CVE-2026-63223CVE-2026-63223: CodeIgniter4 Uploaded File Extension Validation Bypass in is_image and mime_in Rules