CVE-2026-59869: js-yaml Merge-Key Chain Quadratic CPU DoS
A specially crafted YAML document with chained merge-key anchors forces js-yaml to burn quadratic CPU time, letting an attacker crash a Node.js service with a sub-100 KB file.
The problem
js-yaml's merge-key (`<<`) handler re-enumerates every inherited key from scratch each time a mapping is merged. A chain of N mappings each merging the previous one produces roughly 1+2+…+N key visits, O(N²) work for O(N) input.
The vulnerability affects all 3.x builds from 3.0.0 through 3.14.x and all 4.x builds through 4.2.x. An unauthenticated remote attacker who can send YAML to any endpoint calling `js-yaml.load()` can cause a denial of service. At N=4000 (under 100 KB of input) parse time exceeds one second on typical hardware.
Proof of concept
A working proof-of-concept for CVE-2026-59869 in js-yaml, with the exact payload below.
# N=4000 lines, each merging the previous anchor -- triggers >1s parse time
a0: &a0 { k0: 0 }
a1: &a1 { <<: *a0, k1: 1 }
a2: &a2 { <<: *a1, k2: 2 }
a3: &a3 { <<: *a2, k3: 3 }
a4: &a4 { <<: *a3, k4: 4 }
# ... repeat through a3999
b: *a3999Each merge-key resolution in the unpatched loader walks the full flattened key list of the referenced alias. Because every anchor in the chain includes all keys from all previous anchors, the walk length grows by one for each step, producing classic triangular-number growth (CWE-407: Inefficient Algorithmic Complexity).
The patch introduces a `maxTotalMergeKeys` option (default 10,000) that accumulates a running count of every key touched during `<<` resolution across the entire `load()` call and throws once the limit is exceeded. This converts the unbounded O(N²) scan into a hard-stop O(limit) check, closing both the chained-anchor pattern and any future variants that exploit the same code path.
The fix
Upgrade to js-yaml 3.15.0 (for 3.x users) or 4.3.0 (for 4.x users). Both releases backport the `maxTotalMergeKeys` loader option with a safe default of 10,000 total merged keys per parse call. If you cannot upgrade immediately, reject or size-limit untrusted YAML input at the application layer before passing it to `load()`.
Related research
- high · 7.5CVE-2026-49293CVE-2026-49293: js-toml CPU Exhaustion via Quadratic BigInt Parsing
- high · 7.5CVE-2026-13311CVE-2026-13311: shell-quote Quadratic Complexity DoS in parse()
- high · 7.5CVE-2026-50272CVE-2026-50272: dd-trace Unbounded W3C Baggage Extraction DoS
- high · 7.5CVE-2026-49866CVE-2026-49866: @libp2p/gossipsub CPU DoS via Oversized IHAVE and IWANT Control Messages