postcss AST Serialization container.js toString recursion
Description
A vulnerability was determined in postcss up to 7.1.1. Affected is the function toString of the file src/selectors/container.js of the component AST Serialization. Executing a manipulation can lead to uncontrolled recursion. It is possible to launch the attack remotely. The exploit has been publicly disclosed and may be utilized. The vendor explains, that according to his definition "DoS on server-side on user-generated CSS is low risk for us (since most users compile own CSS with PostCSS)."
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Uncontrolled recursion in postcss's AST serialization (toString) allows remote DoS via deeply nested CSS selectors, affecting versions ≤7.1.1.
Vulnerability
The vulnerability resides in postcss-selector-parser (postcss) versions up to 7.1.1. The toString() method in src/selectors/container.js and pseudo.js recursively calls itself without depth limits when serializing deeply nested pseudo-class selectors (e.g., :not(:not(:not(...)))). This leads to uncontrolled recursion and stack overflow. The attack can be triggered remotely by supplying a crafted CSS selector that parses successfully but causes a crash when toString() is invoked. [1]
Exploitation
An attacker can send a specially crafted CSS selector string with deeply nested pseudo-classes. The parser processes the input successfully, but when the application calls toString() on the resulting AST (e.g., during processSync() or process() with updateSelector enabled), the recursion depth exceeds ~887 levels, causing a stack overflow. No authentication is required; the attack is remote. Public exploit code is available. [1]
Impact
Successful exploitation results in a denial of service (DoS) due to stack overflow. The vendor considers this low risk for server-side processing of user-generated CSS, but the vulnerability can crash applications that serialize untrusted CSS. [1]
Mitigation
As of the latest version (7.1.1), no fix has been released. The vendor has not addressed the issue. Workarounds include limiting the depth of CSS selectors accepted from untrusted sources or avoiding calls to toString() on parsed ASTs from user input. [1]
AI Insight generated on May 24, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
1Patches
0No patches discovered yet.
Vulnerability mechanics
Root cause
"Uncontrolled mutual recursion between Container.toString() and Pseudo.toString() with no depth limit causes stack overflow."
Attack vector
An attacker supplies a deeply nested CSS selector string (e.g., `:not(:not(:not(...)))` or `:where()` repeated ~900 times) that the parser successfully parses but that causes a stack overflow when `toString()` is called on the resulting AST [ref_id=1]. The crash threshold (~887 levels) is lower than the parser crash threshold for some pseudo-classes, so inputs exist that parse successfully but crash on serialization [ref_id=1]. The attack is remotely exploitable: any application that parses user-supplied CSS and then serializes it (e.g., via a PostCSS plugin that calls `processSync(rule, { updateSelector: true })`) will trigger the uncontrolled recursion [ref_id=1].
Affected code
The vulnerability is in the `toString()` method of `Container` (src/selectors/container.js) and `Pseudo` (src/selectors/pseudo.js) nodes. When serializing a nested pseudo-class tree, `Container.toString()` calls `this.map(String)` which invokes `String()` on each child, and `Pseudo.toString()` similarly calls `this.map(String)`, creating mutual recursion with no depth limit [ref_id=1]. The `processor.js` layer calls `root.toString()` internally on every `processSync()` / `process()` call when `updateSelector` is set, making this reachable through normal library usage [ref_id=1].
What the fix does
No patch has been published for this vulnerability as of the advisory date [ref_id=1]. The vendor states that "DoS on server-side on user-generated CSS is low risk for us (since most users compile own CSS with PostCSS)" [ref_id=1]. The advisory recommends that downstream projects either limit the depth of AST nodes they accept before calling `toString()`, or avoid calling `toString()` on untrusted ASTs without a depth guard [ref_id=1]. A proper fix would introduce a depth limit or iterative (non-recursive) serialization in `Container.toString()` and `Pseudo.toString()` to break the mutual recursion [ref_id=1].
Preconditions
- inputThe application must call toString() on a deeply nested AST node, either directly or indirectly via processSync()/process() with updateSelector:true
- inputThe AST must have approximately 887 or more levels of nested Pseudo/Container nodes
- authNo authentication is required; the attack can be triggered remotely by supplying a malicious CSS file or programmatic AST
Reproduction
PoC 1 — Programmatic AST construction + toString(): const selectorParser = require('postcss-selector-parser'); function buildDeepTree(depth) { const { pseudo, selector, tag } = selectorParser; let current = selector({ nodes: [tag({ value: 'a' })] }); for (let i = 0; i
Generated on May 24, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- gist.github.com/bx33661/581e3a38134601c04e19b4dfc9b459b9mitreexploit
- vuldb.com/submit/813080mitrethird-party-advisory
- vuldb.com/vuln/365321mitrevdb-entrytechnical-description
- vuldb.com/vuln/365321/ctimitresignaturepermissions-required
News mentions
0No linked articles in our index yet.