CVE-2026-126066: fast-xml-parser: Quadratic CPU consumption scanning unterminated stop-node tags
Summary
With the non-default stopNodes option, a stop node's body with many "<a " fragments and no closing ">" anywhere after makes readStopNodeData() rescan the entire remaining input from every "<". The failed lookahead is neither cached nor bounded, so n fragments cost O(n²) before the parser throws. Availability-only (CPU exhaustion), needs the non-default stopNodes option — not benchmarked yet, see Proof of Concept.
Details
CWE-407 — Inefficient Algorithmic Complexity
Code - OrderedObjParser.js:520 — parseXml() calls readStopNodeData() after a matching stop node opens - OrderedObjParser.js:810 — calls readTagExp() for every non-special < - OrderedObjParser.js:688-722 — tagExpWithClosingIndex() scans to end-of-input and returns undefined when no > exists - OrderedObjParser.js:812-818 — on failure, i isn't advanced, so the next < repeats the same full scan - OrderedObjParser.js:423 — the main parse loop throws immediately on the same failure; only the stop-node path retries
Root cause: a failed closing-> lookahead is neither cached nor bounded — n copies of <a with no later > means n scans over the remaining input, O(n²) work, before it finally throws.
Fix 1. Throw immediately in readStopNodeData() when readTagExp() returns undefined 2. Or break the loop on the first failed scan 3. Or find the next > once and reuse/bound that result 4. Add a test asserting near-linear scaling when payload size doubles
Proof of concept
Not benchmarked — static-analysis finding, confidence 0.5.
To confirm: configure stopNodes matching a root element. Build input as '<stop>' + '<a '.repeat(n). Time parse() until it throws Unexpected end of <tag>. Start around n=10,000 and double it — quadratic should show ~4x time per doubling, linear ~2x. Control: same input with a > present, or stopNodes unset.
Impact
CPU-exhaustion DoS in Node.js services parsing untrusted XML with stopNodes enabled. A ~100KB payload is estimated at ~10⁹ character operations (roughly a second or more of blocked event loop) — analytical estimate, not a benchmark. No confidentiality/integrity impact. CVSS 3.1: AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L (3.7, Low).
Other sources
fast-xml-parser allows users to process XML from JS object without C/C++ based libraries or callbacks. Prior to 5.12.0, when the non-default stopNodes option is enabled, readStopNodeData() in OrderedObjParser.js calls readTagExp() for each less-than character in an unterminated stop-node body and repeatedly rescans the remaining input while looking for a closing greater-than character. An attacker who supplies XML containing many tag fragments without a later closing character can cause quadratic rescanning before parsing fails, blocking a Node.js event loop and causing CPU-exhaustion denial of service. This issue is fixed in version 5.12.0.
— MITRE
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
npm/fast-xml-parserto a version that resolves this vulnerability.Fixed in 5.12.0 - Upgrade
Upgrade
fast-xml-parserto a version that resolves this vulnerability.Fixed in 5.12.0 - Configuration
Leave the non-default stopNodes option unset when it is not required.
fast-xml-parser stopNodes = unset