{"id":"CVE-2026-126066","kind":"vulnerability","aliases":["CVE-2026-126066","GHSA-474x-hrwr-fgx4"],"related":[],"title":"fast-xml-parser: Quadratic CPU consumption scanning unterminated stop-node tags","description":"### Summary\n\nWith 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.\n\n### Details\n\n**CWE-407** — Inefficient Algorithmic Complexity\n\n**Code**\n- `OrderedObjParser.js:520` — `parseXml()` calls `readStopNodeData()` after a matching stop node opens\n- `OrderedObjParser.js:810` — calls `readTagExp()` for every non-special `<`\n- `OrderedObjParser.js:688-722` — `tagExpWithClosingIndex()` scans to end-of-input and returns `undefined` when no `>` exists\n- `OrderedObjParser.js:812-818` — on failure, `i` isn't advanced, so the next `<` repeats the same full scan\n- `OrderedObjParser.js:423` — the main parse loop throws immediately on the same failure; only the stop-node path retries\n\n**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.\n\n**Fix**\n1. Throw immediately in `readStopNodeData()` when `readTagExp()` returns `undefined`\n2. Or break the loop on the first failed scan\n3. Or find the next `>` once and reuse/bound that result\n4. Add a test asserting near-linear scaling when payload size doubles\n\n### Proof of concept\n\nNot benchmarked — static-analysis finding, confidence 0.5.\n\nTo 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.\n\n### Impact\n\nCPU-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).","summary":"fast-xml-parser versions up to 5.11.2 take quadratic time to parse a stop-node body containing many unterminated \"<a \" fragments when the non-default stopNodes option is set. An attacker who can send XML to such a parser can stall the event loop and exhaust CPU, causing denial of service with no data exposure.","severity":"low","cvss":3.7,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"fast-xml-parser","fixed":["5.12.0"],"affected":"<= 5.11.2","ecosystem":"npm"}],"ecosystems":["npm"],"tags":["cwe-407","fast-xml-parser","node.js","package","xml-parsing"],"not_affected_if":["default-config","trusted-input"],"how_to_check":"- Check the installed version with `npm ls fast-xml-parser`; affected versions are `5.11.2` and earlier.\n- Search the code for `stopNodes` in calls to the parser; the bug only applies when that option is set.\n- Check whether that parser handles XML from untrusted sources.","fix":"- `fast-xml-parser` `5.12.0`","status":"open","published_at":"2026-11-10T21:09:44+00:00","first_seen_at":"2026-11-10T21:12:45+00:00","triaged_at":"2026-11-10T21:14:29+00:00","updated_at":"2026-11-10T21:14:29+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-11-10T21:14:29Z","model":"claude-haiku-5-5","output":{"fix":"- `fast-xml-parser` `5.12.0`","reach":"internet","title":"fast-xml-parser: crafted XML with stopNodes burns CPU quadratically","impact":"low","summary":"fast-xml-parser versions up to 5.11.2 take quadratic time to parse a stop-node body containing many unterminated \"<a \" fragments when the non-default stopNodes option is set. An attacker who can send XML to such a parser can stall the event loop and exhaust CPU, causing denial of service with no data exposure.","audience":"developers","category":"package","nicknames":[],"who_cares":["node.js","fast-xml-parser","xml-parsing"],"confidence":0.8,"fix_status":"available","needs_auth":"no","worst_case":"Parser blocks the event loop, causing CPU exhaustion denial of service","analyst_note":"","how_to_check":"- Check the installed version with `npm ls fast-xml-parser`; affected versions are `5.11.2` and earlier.\n- Search the code for `stopNodes` in calls to the parser; the bug only applies when that option is set.\n- Check whether that parser handles XML from untrusted sources.","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["default-config","trusted-input"],"who_is_affected":"Only applications that call fast-xml-parser with the non-default stopNodes option and parse XML from untrusted input are exposed. Projects that leave stopNodes unset, or parse only trusted XML, can skip it.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"yes","severity_if_unknown":"low"},"cost_usd":0.000825,"input_tokens":5484,"output_tokens":554,"severity_before":"low"},"analyst_status":null,"analyst_note":null,"search":"'-126066':16A,24A '-2026':15A,23A '-407':30B,163C '-722':187C '-818':202C '0.5':320C '000':347C '1':268C '10':346C,389C '100kb':384C '2':277C '2x':359C '3':287C '3.1':410C '3.7':420C '4':297C '474x':19A '4x':354C '5.11.2':47B 'add':298C 'advanc':208C 'algorithm':165C 'analysi':317C 'analyt':401C 'anywher':113C 'around':344C 'assert':301C 'attack':73B 'av':411C 'avail':143C 'availability-on':142C 'benchmark':155C,314C,405C 'block':398C 'bodi':57B,110C 'bound':131C,245C 'break':279C 'build':329C 'cach':129C,243C 'call':170C,179C 'caus':90B,236C 'charact':390C 'close':239C 'code':167C 'complex':166C 'concept':160C,312C 'confid':319C 'confidentiality/integrity':407C 'configur':323C 'confirm':322C 'consumpt':7A 'contain':58B 'control':360C 'copi':247C 'cost':135C 'cpu':6A,89B,145C,371C 'cpu-exhaust':370C 'cve':14A,22A 'cvss':409C 'cwe':29B,162C 'data':96B 'default':67B,103C,151C 'denial':91B 'detail':161C 'dos':373C 'doubl':309C,349C,357C 'element':328C 'enabl':382C 'end':192C,341C 'end-of-input':191C 'entir':119C 'estim':387C,402C 'event':85B,399C 'everi':123C,182C 'exhaust':88B,146C,372C 'exist':200C 'exposur':97B 'fail':125C,238C,285C 'failur':204C,227C 'fast':2A,26A,32B,41B 'fast-xml-pars':1A,31B,40B 'fast-xml-parser':25A 'fgx4':21A 'final':265C 'find':289C,318C 'first':284C 'fix':267C 'fragment':62B,134C 'full':215C 'ghsa':18A 'ghsa-474x-hrwr-fgx4':17A 'h/pr':413C 'hrwr':20A 'immedi':223C,270C 'impact':369C,408C 'ineffici':164C 'input':121C,194C,259C,330C,362C 'isn':206C 'l':419C 'later':252C 'linear':304C,358C 'lookahead':126C,240C 'loop':86B,221C,281C,400C 'low':421C 'main':219C 'make':115C 'mani':59B,112C 'match':174C,325C 'mean':253C 'n':133C,137C,246C,254C,261C,334C,345C 'n/a':418C 'n/ac':412C 'n/i':417C 'n/s':415C 'n/ui':414C 'near':303C 'near-linear':302C 'need':147C 'neither':128C,242C 'next':211C,291C 'node':12A,56B,108C,176C,232C 'node.js':35B,375C 'non':66B,102C,150C,184C 'non-default':65B,101C,149C 'non-speci':183C 'o':136C,260C 'open':177C 'oper':391C 'option':69B,105C,153C 'orderedobjparser.js:423':217C 'orderedobjparser.js:520':168C 'orderedobjparser.js:688':186C 'orderedobjparser.js:810':178C 'orderedobjparser.js:812':201C 'packag':36B 'pars':39B,52B,220C,336C,377C 'parser':4A,28A,34B,43B,81B,140C 'parsexml':169C 'path':233C 'payload':307C,385C 'per':356C 'present':365C 'proof':158C,310C 'quadrat':5A,49B,351C 'readstopnodedata':116C,171C,272C 'readtagexp':180C,274C 'remain':120C,258C 'repeat':212C,333C 'rescan':117C 'result':296C 'retri':234C 'return':196C,275C 'reuse/bound':294C 'root':235C,327C 'rough':392C 'scale':305C 'scan':8A,189C,216C,255C,286C 'second':394C 'see':157C 'send':76B 'servic':93B,376C 'set':71B 'show':353C 'size':308C 'special':185C 'stall':83B 'start':343C 'static':316C 'static-analysi':315C 'stop':11A,55B,107C,175C,231C 'stop-nod':10A,54B,230C 'stopnod':68B,104C,152C,324C,367C,381C 'summari':98C 'tag':13A 'tagexpwithclosingindex':188C 'take':48B 'test':300C 'throw':141C,222C,266C,269C,339C 'time':50B,335C,355C 'u/c':416C 'undefin':197C,276C 'unexpect':340C 'unset':368C 'untermin':9A,60B 'untrust':378C 'version':44B 'work':262C 'xml':3A,27A,33B,38B,42B,77B,379C 'xml-pars':37B 'yet':156C","nicknames":[],"mitigations":[],"who_affected":"Only applications that call fast-xml-parser with the non-default stopNodes option and parse XML from untrusted input are exposed. Projects that leave stopNodes unset, or parse only trusted XML, can skip it.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["fastxmlparser"],"ptokens":["fast","parser","xml"],"labels":["cwe407","fastxmlparser","low","nodejs","npm","package","xmlparsing"],"campaign_type":null,"search_a":"'-126066':16A,24A '-2026':15A,23A '474x':19A 'consumpt':7A 'cpu':6A 'cve':14A,22A 'fast':2A,26A 'fast-xml-pars':1A 'fast-xml-parser':25A 'fgx4':21A 'ghsa':18A 'ghsa-474x-hrwr-fgx4':17A 'hrwr':20A 'node':12A 'parser':4A,28A 'quadrat':5A 'scan':8A 'stop':11A 'stop-nod':10A 'tag':13A 'untermin':9A 'xml':3A,27A","search_b":"'-407':30B '5.11.2':47B 'attack':73B 'bodi':57B 'caus':90B 'contain':58B 'cpu':89B 'cwe':29B 'data':96B 'default':67B 'denial':91B 'event':85B 'exhaust':88B 'exposur':97B 'fast':32B,41B 'fast-xml-pars':31B,40B 'fragment':62B 'loop':86B 'mani':59B 'node':56B 'node.js':35B 'non':66B 'non-default':65B 'option':69B 'packag':36B 'pars':39B,52B 'parser':34B,43B,81B 'quadrat':49B 'send':76B 'servic':93B 'set':71B 'stall':83B 'stop':55B 'stop-nod':54B 'stopnod':68B 'take':48B 'time':50B 'untermin':60B 'version':44B 'xml':33B,38B,42B,77B 'xml-pars':37B","search_c":"'-407':163C '-722':187C '-818':202C '0.5':320C '000':347C '1':268C '10':346C,389C '100kb':384C '2':277C '2x':359C '3':287C '3.1':410C '3.7':420C '4':297C '4x':354C 'add':298C 'advanc':208C 'algorithm':165C 'analysi':317C 'analyt':401C 'anywher':113C 'around':344C 'assert':301C 'av':411C 'avail':143C 'availability-on':142C 'benchmark':155C,314C,405C 'block':398C 'bodi':110C 'bound':131C,245C 'break':279C 'build':329C 'cach':129C,243C 'call':170C,179C 'caus':236C 'charact':390C 'close':239C 'code':167C 'complex':166C 'concept':160C,312C 'confid':319C 'confidentiality/integrity':407C 'configur':323C 'confirm':322C 'control':360C 'copi':247C 'cost':135C 'cpu':145C,371C 'cpu-exhaust':370C 'cvss':409C 'cwe':162C 'default':103C,151C 'detail':161C 'dos':373C 'doubl':309C,349C,357C 'element':328C 'enabl':382C 'end':192C,341C 'end-of-input':191C 'entir':119C 'estim':387C,402C 'event':399C 'everi':123C,182C 'exhaust':146C,372C 'exist':200C 'fail':125C,238C,285C 'failur':204C,227C 'final':265C 'find':289C,318C 'first':284C 'fix':267C 'fragment':134C 'full':215C 'h/pr':413C 'immedi':223C,270C 'impact':369C,408C 'ineffici':164C 'input':121C,194C,259C,330C,362C 'isn':206C 'l':419C 'later':252C 'linear':304C,358C 'lookahead':126C,240C 'loop':221C,281C,400C 'low':421C 'main':219C 'make':115C 'mani':112C 'match':174C,325C 'mean':253C 'n':133C,137C,246C,254C,261C,334C,345C 'n/a':418C 'n/ac':412C 'n/i':417C 'n/s':415C 'n/ui':414C 'near':303C 'near-linear':302C 'need':147C 'neither':128C,242C 'next':211C,291C 'node':108C,176C,232C 'node.js':375C 'non':102C,150C,184C 'non-default':101C,149C 'non-speci':183C 'o':136C,260C 'open':177C 'oper':391C 'option':105C,153C 'orderedobjparser.js:423':217C 'orderedobjparser.js:520':168C 'orderedobjparser.js:688':186C 'orderedobjparser.js:810':178C 'orderedobjparser.js:812':201C 'pars':220C,336C,377C 'parser':140C 'parsexml':169C 'path':233C 'payload':307C,385C 'per':356C 'present':365C 'proof':158C,310C 'quadrat':351C 'readstopnodedata':116C,171C,272C 'readtagexp':180C,274C 'remain':120C,258C 'repeat':212C,333C 'rescan':117C 'result':296C 'retri':234C 'return':196C,275C 'reuse/bound':294C 'root':235C,327C 'rough':392C 'scale':305C 'scan':189C,216C,255C,286C 'second':394C 'see':157C 'servic':376C 'show':353C 'size':308C 'special':185C 'start':343C 'static':316C 'static-analysi':315C 'stop':107C,175C,231C 'stop-nod':230C 'stopnod':104C,152C,324C,367C,381C 'summari':98C 'tagexpwithclosingindex':188C 'test':300C 'throw':141C,222C,266C,269C,339C 'time':335C,355C 'u/c':416C 'undefin':197C,276C 'unexpect':340C 'unset':368C 'untrust':378C 'work':262C 'xml':379C 'yet':156C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-11-03T12:06:56+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-474x-hrwr-fgx4","url":"https://github.com/advisories/GHSA-474x-hrwr-fgx4","severity":"low","published_at":"2026-11-10T21:09:44+00:00","modified_at":"2026-11-10T21:09:45+00:00","first_seen_at":"2026-11-10T21:12:45+00:00","fetched_at":"2026-11-10T21:17:48+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-126066","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-126066","severity":null,"published_at":"2026-11-10T21:19:24+00:00","modified_at":"2026-11-10T21:19:24+00:00","first_seen_at":"2026-11-10T21:46:15+00:00","fetched_at":"2026-11-10T21:46:16+00:00","note":null}],"documents":[{"id":118544,"url":"https://api.github.com/repos/NaturalIntelligence/fast-xml-parser/security-advisories/GHSA-474x-hrwr-fgx4","title":"Maintainer's advisory: api.github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: api.github.com"},{"id":118545,"url":"https://github.com/NaturalIntelligence/fast-xml-parser/security/advisories/GHSA-474x-hrwr-fgx4","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":"2026-11-03T12:06:56+00:00","relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"},{"id":118546,"url":"https://github.com/NaturalIntelligence/fast-xml-parser/commit/0c3659348364959da62954b73728fb3bd05ebcb4","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":118547,"url":"https://github.com/NaturalIntelligence/fast-xml-parser/releases/tag/v5.12.0","title":"Release: github.com","kind":"release","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Release: github.com"}],"disclosures":[],"exploit_sources":[],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}