Advisories for Composer/League/Commonmark package

2026

league/commonmark: Quadratic-time denial of service when parsing crafted Markdown

Affected versions of league/commonmark can have quadratic time complexity when parsing specially crafted Markdown lines. In practical terms, doubling the length of an affected line can make the parser perform roughly four times as much work. The parser identifies locations using character positions, but regular-expression matches report byte positions. These positions differ when a UTF-8 character uses more than one byte. Several parsing paths repeatedly rescan growing portions of the …

league/commonmark: Denial of service via deeply nested XML output

XmlRenderer pretty-prints XML by emitting depth-proportional indentation whitespace for every opening and closing tag. For a tree of depth n, the indentation alone sums to O(n²) bytes of output (and corresponding memory), reachable through MarkdownToXmlConverter — e.g. str_repeat('> ', $depth) . "x\n", a single line of nested blockquotes — or through a direct XmlRenderer::renderDocument() call on an attacker-influenced AST. This affects applications that convert untrusted Markdown to XML, which is …

league/commonmark: Denial of service via colliding heading slugs

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 …

league/commonmark: Denial of service via adjacent inline attribute blocks

With the Attributes extension enabled, AttributesListener::findTargetAndDirection() resolves each attribute node's target by walking outward through its siblings. For a run of N adjacent inline attribute blocks placed at the start of a block (with nothing to their left), each node scans the entire sibling list to the far-right end before giving up and falling back to the parent. Each resolution is therefore Θ(N) and the whole run is Θ(N²). Reaching …

league/commonmark: AttributesExtension href/src unsafe-link filter bypass via embedded control bytes

Summary The AttributesExtension's href/src unsafe-link filter (AttributesHelper::filterAttributes()) can be bypassed by embedding control bytes in a javascript: URL that browsers discard before parsing the scheme. Two variants: Tab/newline inside the scheme — a literal ASCII TAB (0x09), CR (0x0D), or LF (0x0A), e.g. java<TAB>script:alert(1). Per the WHATWG URL Standard's "basic URL parser" step 3, browsers "remove all ASCII tab or newline from input". Leading C0 controls — e.g. <0x01>javascript:alert(1). Per …

league/commonmark: Denial of service via duplicate footnote definitions

The Footnote extension records one backref per footnote reference and then appends the entire backref list for every footnote definition block in the document, without ever de-duplicating or removing repeated definitions of the same label (GatherFootnotesListener, populated by NumberFootnotesListener). A document that references a single label N times and also supplies N duplicate [^a]: definitions of that label therefore produces N × N FootnoteBackref nodes, so output size, parse time, …

league/commonmark has an embed extension allowed_domains bypass

The DomainFilteringAdapter in the Embed extension is vulnerable to an allowlist bypass due to a missing hostname boundary assertion in the domain-matching regex. An attacker-controlled domain like youtube.com.evil passes the allowlist check when youtube.com is an allowed domain. This enables two attack vectors: SSRF: The OscaroteroEmbedAdapter makes server-side HTTP requests to the embed URL via the embed/embed library. A bypassed domain filter causes the server to make outbound requests to …

CommonMark has DisallowedRawHtml extension bypass via whitespace in HTML tag names

The DisallowedRawHtml extension can be bypassed by inserting a newline, tab, or other ASCII whitespace character between a disallowed HTML tag name and the closing >. For example, <script\n> would pass through unfiltered and be rendered as a valid HTML tag by browsers. This is a cross-site scripting (XSS) vector for any application that relies on this extension to sanitize untrusted user input. All applications using the DisallowedRawHtml extension to …

2025
2024

league/commonmark's quadratic complexity bugs may lead to a denial of service

Several polynomial time complexity issues in league/commonmark may lead to unbounded resource exhaustion and subsequent denial of service. Malicious users could trigger that inefficient code with carefully crafted Markdown inputs that are specifically designed to ensure the worst-case performance is reached. Sending multiple such requests in parallel could tie up all available CPU resources and/or PHP-FPM processes, leading to denial of service for legitimate users.

2019

Cross-site Scripting

Cross-site scripting (XSS) vulnerability in the PHP League CommonMark library allows remote attackers to insert unsafe links into HTML by using double-encoded HTML entities that are not properly escaped during rendering, a different vulnerability than CVE-2018-20583.

2018

Cross-site Scripting

Cross-site scripting (XSS) vulnerability in the PHP League CommonMark library allows remote attackers to insert unsafe URLs into HTML (even if allow_unsafe_links is false) via a newline character (e.g., writing javascript as javascri%0apt).