Advisory Database
  • Advisories
  • Dependency Scanning
  1. composer
  2. ›
  3. league/commonmark
  4. ›
  5. GHSA-j8pm-gj4c-rq4x

GHSA-j8pm-gj4c-rq4x: league/commonmark: Denial of service via crafted code fences, reference links, and emphasis delimiters

September 1, 2026

Affected versions of league/commonmark perform super-linear work on three independent parsing paths, all of which are reachable on a stock new CommonMarkConverter() with default configuration and no extensions registered. Each trigger fits on a single line of input, so no complex Markdown structure is required.

The three paths were introduced at different times. This advisory’s version range is their union; the individual ranges are:

PathAffected fromAffected through
1. Fenced code block detection0.6.02.9.0
2. Reference link label lookup0.6.02.9.0
3. Emphasis / strikethrough delimiters (*, _, ~)2.6.02.9.0
3. Highlight delimiters (=)2.9.02.9.0

1. Fenced code block detection — quadratic, affected from 0.6.0.

FencedCodeStartParser matches the following pattern:

/^[ \t]*(?:`{3,}(?!.*`)|~{3,})/

The lookahead enforces the CommonMark rule that a backtick fence’s info string may not itself contain a backtick, but neither the lookahead nor the backtick run it guards is atomic or possessive. On a line consisting of a long backtick run, filler text, and a single trailing backtick, the quantifier gives back one character at a time and re-runs the lookahead across the remainder of the line on every candidate fence length.

A 320 KB single line takes roughly 27 seconds to convert. The identical payload with one x character prefixed — which fails the parser’s own leading-character guard — takes 0.011 seconds. preg_last_error() returns 0 at every input size tested, including runs of 160,000 characters, so PCRE never reaches pcre.backtrack_limit and this is sustained CPU consumption rather than an early bail-out.

2. Reference link label lookup — effectively quadratic, affected from 0.6.0.

When a shortcut or collapsed reference link is attempted, CloseBracketParser::tryParseReference() copies the entire span between the brackets and passes it to ReferenceMap::get(), which normalizes the label — up to four full passes over its length (trim, preg_replace, mb_check_encoding, and strtolower, or mb_convert_case on the non-ASCII path). Nested brackets produce one such lookup per closing bracket, each on a span two characters longer than the last.

In 2.x the normalization sits behind an early return for an empty reference map, so a single 8-byte reference definition anywhere in the document ([x]: y) is enough to unlock the path. At n = 64,000 nested brackets the same input takes 22.0 seconds with that line present versus 0.59 seconds without it. A single non-ASCII character inside the brackets forces the mb_convert_case branch, costing roughly 2.5x more again.

3. Emphasis, strikethrough, and highlight delimiter processing — super-linear, affected from 2.6.0.

DelimiterStack::processDelimiters() remains linear only because of the openersBottom memo, which bounds the backward opener scan — an argument that holds only if the memo’s key space is O(1). EmphasisDelimiterProcessor::getCacheKey(), and the equivalents in StrikethroughDelimiterProcessor and MarkDelimiterProcessor, embed the closer’s raw current run length in the key, leaving that space unbounded. An attacker spends O(n) bytes minting a growing number of distinct run lengths; each distinct length is a fresh key whose recorded bound starts at zero, forcing a full backward re-scan of the entire pile of openers.

The resulting work grows as roughly n^1.5. This is sub-quadratic, but the amplification over linear growth itself scales with input size, so it worsens as inputs grow: 800 KB of ordinary asterisks, letters, and spaces costs roughly 27 seconds on a stock converter.

This path is a regression introduced in 2.6.0. Before that release the cache key was the bare delimiter character — a bounded key space that amortized correctly. * and _ are affected on any default configuration from 2.6.0 onward. ~ (StrikethroughExtension, included in GithubFlavoredMarkdownConverter and GithubFlavoredMarkdownExtension) is affected from 2.6.0. = (HighlightExtension) is affected only from 2.9.0, when MarkDelimiterProcessor was declared cacheable.

Overall impact. An unauthenticated attacker who can submit Markdown for conversion can use a comparatively small request to consume disproportionate CPU time. Repeated or concurrent requests can occupy all available PHP workers and prevent legitimate requests from completing. The impact is limited to availability: no data is disclosed, rendered output is unchanged, and no rendering restriction is bypassed. Applications that process only trusted Markdown are not remotely exploitable.

Settings such as html_input, allow_unsafe_links, and max_nesting_level do not mitigate any of these, because the expensive work occurs during parsing, before rendering. max_delimiters_per_line bounds the third path only, and does so lossily — it silently discards emphasis once the cap is exhausted.

References

  • github.com/advisories/GHSA-j8pm-gj4c-rq4x
  • github.com/thephpleague/commonmark/commit/0768217751fbfaeb8d76762f6944e9af7114295e
  • github.com/thephpleague/commonmark/commit/d9375fadc308a63a02950a68d822417a6e4c33b2
  • github.com/thephpleague/commonmark/commit/e0036ef031fd36ec1c3c82db8743fc928b5271c8
  • github.com/thephpleague/commonmark/releases/tag/2.9.1
  • github.com/thephpleague/commonmark/security/advisories/GHSA-j8pm-gj4c-rq4x

Code Behaviors & Features

Detect and mitigate GHSA-j8pm-gj4c-rq4x with GitLab Dependency Scanning

Secure your software supply chain by verifying that all open source dependencies used in your projects contain no disclosed vulnerabilities. Learn more about Dependency Scanning →

Affected versions

All versions starting from 0.6.0 before 2.9.1

Fixed versions

  • 2.9.1

Solution

Upgrade to version 2.9.1 or above.

Impact 7.5 HIGH

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

Learn more about CVSS

Weakness

  • CWE-1050: Excessive Platform Resource Consumption within a Loop
  • CWE-1333: Inefficient Regular Expression Complexity
  • CWE-407: Inefficient Algorithmic Complexity

Source file

packagist/league/commonmark/GHSA-j8pm-gj4c-rq4x.yml

Spotted a mistake? Edit the file on GitLab.

  • Site Repo
  • About GitLab
  • Terms
  • Privacy Statement
  • Contact

Page generated Thu, 10 Sep 2026 00:18:36 +0000.