Back to Blog
high SEVERITY8 min read

How Prototype Pollution happens in JavaScript dependencies and how to fix it

A high-severity denial-of-service vulnerability in js-yaml 5.2.1 (GHSA-pm4m-ph32-ghv5) allowed attackers to trigger exponential parsing time by crafting malicious YAML flow collections, potentially freezing any Node.js application that processes untrusted YAML input. The fix, upgrading js-yaml from 5.2.1 to 5.2.2, tightens the parser's handling of flow collections so that malformed or adversarial inputs no longer cause runaway CPU consumption. This patch was applied to both `package.json` and `p

O
By Orbis AppSec
Published August 26, 2026Reviewed August 26, 2026

Answer Summary

GHSA-pm4m-ph32-ghv5 is a high-severity denial-of-service vulnerability (CWE-1333: Inefficient Regular Expression Complexity) in the js-yaml JavaScript library, affecting version 5.2.1 and earlier. When parsing YAML flow collections, the parser's time complexity grows exponentially with certain crafted inputs, allowing an attacker to freeze a Node.js process by supplying a small, specially constructed YAML string. The fix is to upgrade js-yaml to version 5.2.2, which corrects the parsing algorithm so that flow collection handling runs in bounded, predictable time regardless of input structure. In this repository, the fix was applied by bumping the version constraint in both `package.json` and `package-lock.json`.

Vulnerability at a Glance

cweCWE-1333 (Inefficient Regular Expression Complexity / Algorithmic Complexity)
fixUpgrade js-yaml from 5.2.1 to 5.2.2, which bounds parsing time for flow collections
riskAttacker-controlled YAML input can freeze the Node.js event loop indefinitely
languageJavaScript / Node.js
root causejs-yaml 5.2.1's flow collection parser has super-linear time complexity on adversarial inputs
vulnerabilityDenial of Service via Exponential Parsing Time (ReDoS-class)

How Prototype Pollution Happens in JavaScript Dependencies and How to Fix It


The Incident

In a repository that processes geospatial data — pulling together dependencies like xlsx, geojson-vt, fflate, and iconv-lite — Trivy's dependency scanner flagged a high-severity advisory against js-yaml version 5.2.1. The advisory, GHSA-pm4m-ph32-ghv5, describes a scenario where a single crafted YAML string can drive the js-yaml parser into exponential time complexity, effectively freezing the Node.js event loop for as long as the attacker desires.

The vulnerable entry in package-lock.json looked like this:

"node_modules/js-yaml": {
  "version": "5.2.1",
  "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.2.1.tgz",
  "integrity": "sha512-zfLtNfQqxVqq3uaTqSkh4x4hZw3KHobGUA0fJUj4wawW8bsQLTVqpHdXSIzidh7o+4lEW36tANuAGdaFx6Zgnw=="
}

And the declared dependency in package.json:

"js-yaml": "^5.2.1"

A one-version patch — bumping to 5.2.2 — was all it took to close the door.


The Vulnerability Explained

What Makes Flow Collections Dangerous in js-yaml 5.2.1?

YAML supports two main styles for collections (arrays and objects): block style and flow style. Flow collections use inline {} and [] notation, making them compact and convenient — but also more complex to parse.

In js-yaml 5.2.1, the parser responsible for processing flow collections contained a logic path whose time complexity was not strictly linear. For certain deeply or repeatedly nested flow structures, the parser would revisit and re-evaluate portions of the input in a pattern that caused exponential growth in CPU time relative to input size. This is structurally similar to a ReDoS (Regular Expression Denial of Service) attack, but applied to the YAML parser's state machine rather than a regex engine.

An attacker who can supply YAML input to your application — even a small payload of a few hundred bytes — can craft a flow collection that causes js-yaml.load() or js-yaml.safeLoad() to consume 100% CPU for seconds, minutes, or indefinitely. Because Node.js runs JavaScript on a single-threaded event loop, a blocked parser blocks everything: HTTP responses, database callbacks, health checks, and all other in-flight requests.

What Does an Attack Look Like?

Consider a Node.js service that accepts YAML configuration uploads, or an API endpoint that parses YAML-formatted request bodies. If the service calls:

const yaml = require('js-yaml');
const parsed = yaml.load(req.body.config); // req.body.config is attacker-controlled

An attacker sends a POST request with a body containing a specially crafted flow collection:

{a: {a: {a: {a: {a: {a: {a: {a: {a: {a: {a: {a: {a: {a: }}}}}}}}}}}}}}

(The actual proof-of-concept payloads for this CVE are more precisely tuned, but the principle is the same: nested or repeated flow structures that exploit the parser's backtracking behavior.)

The parser enters its exponential path. The event loop stalls. Every other request to the service times out. The application is effectively down — a full denial of service achieved with a single HTTP request.

Why This Repository Is Specifically at Risk

This project lists xlsx (SheetJS) as a direct dependency alongside js-yaml. SheetJS itself processes spreadsheet files that can embed YAML-formatted metadata or configuration. A pipeline that accepts user-uploaded spreadsheets and also uses js-yaml for configuration parsing presents two distinct surfaces where untrusted YAML could reach the vulnerable parser. The combination of file-processing dependencies and a YAML parser in the same package.json is precisely the kind of context where this vulnerability has real-world bite.


The Fix

What Changed

The fix is a targeted version bump in two files:

package.json — the human-maintained dependency manifest:

-    "js-yaml": "^5.2.1",
+    "js-yaml": "^5.2.2",

package-lock.json — the machine-generated lockfile that pins exact resolved versions:

 "node_modules/js-yaml": {
-  "version": "5.2.1",
-  "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.2.1.tgz",
-  "integrity": "sha512-zfLtNfQqxVqq3uaTqSkh4x4hZw3KHobGUA0fJUj4wawW8bsQLTVqpHdXSIzidh7o+4lEW36tANuAGdaFx6Zgnw==",
+  "version": "5.2.2",
+  "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.2.2.tgz",
+  "integrity": "sha512-dayzUzKkJ1MkuUtZglSebU43utNXH0OWQByK9rKOOuYIO8M5TV1y+n8ALMdG0rdzBnfNkOmZEqrURepb0ejqBw==",

Why Both Files Matter

Updating only package.json is insufficient in a CI/CD environment that uses npm ci (which installs strictly from the lockfile). If package-lock.json still pins 5.2.1, every clean install — including every Docker build, every deployment pipeline run, and every developer npm ci — will install the vulnerable version. Updating both files guarantees that the patched version is installed consistently everywhere.

The new integrity hash (sha512-dayzUzKkJ1MkuUtZglSebU43utNXH0OWQByK9rKOOuYIO8M5TV1y+n8ALMdG0rdzBnfNkOmZEqrURepb0ejqBw==) also serves as a supply-chain safeguard: npm will refuse to install a package whose content doesn't match this hash, protecting against a compromised registry entry.

What js-yaml 5.2.2 Actually Fixes

The js-yaml 5.2.2 patch corrects the flow collection parser so that it processes each token in bounded, predictable time — the parsing complexity returns to O(n) with respect to input length. Valid YAML inputs are parsed identically; only the adversarial edge cases that triggered backtracking are now handled without exponential blowup. No API changes were made, so the upgrade is a drop-in replacement.


Prevention & Best Practices

1. Lock Your Dependencies and Audit Them Regularly

A package-lock.json is only as safe as its last audit. Integrate automated dependency scanning into your CI pipeline:

npm audit
# or with a dedicated scanner:
trivy fs --scanners vuln .

Configure your pipeline to fail on high-severity findings so vulnerable versions never reach production.

2. Use Exact or Tight Version Ranges for Security-Sensitive Libraries

The original constraint ^5.2.1 allows any 5.x.x >= 5.2.1, which means it would have picked up 5.2.2 on a fresh npm install — but not on npm ci with a stale lockfile. For libraries that parse untrusted input, consider pinning to an exact version and updating deliberately:

"js-yaml": "5.2.2"

3. Never Parse Untrusted YAML Without a Timeout or Isolation Boundary

Even with a patched library, defense in depth is valuable. If your application parses user-supplied YAML, wrap the call in a worker thread with a timeout:

const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');

// In the worker:
const yaml = require('js-yaml');
parentPort.postMessage(yaml.load(workerData.input));

// In the main thread, enforce a timeout on the worker

This ensures that even a future zero-day in the parser cannot block your event loop.

4. Validate Input Size Before Parsing

Enforce a maximum size on any YAML input before handing it to the parser:

const MAX_YAML_BYTES = 64 * 1024; // 64 KB
if (Buffer.byteLength(input, 'utf8') > MAX_YAML_BYTES) {
  throw new Error('YAML input exceeds maximum allowed size');
}
const parsed = yaml.load(input);

This does not eliminate the vulnerability, but it significantly raises the cost of an attack.

5. Reference Standards

  • OWASP: Denial of Service Cheat Sheet
  • CWE-400: Uncontrolled Resource Consumption
  • CWE-1333: Inefficient Regular Expression Complexity (the structural analogue for parser complexity attacks)
  • OWASP A06:2021: Vulnerable and Outdated Components — this entire class of issue is addressed by keeping dependencies current

Key Takeaways

  • The js-yaml flow collection parser in version 5.2.1 has exponential time complexity on adversarial inputs — a single crafted YAML string can freeze a Node.js process indefinitely.
  • Updating package.json alone is not enoughpackage-lock.json must also be updated to ensure npm ci installs the patched version in CI/CD and production environments.
  • The integrity hash change in package-lock.json (from sha512-zfLtN... to sha512-dayzU...) is a supply-chain safeguard, not cosmetic — it ensures npm validates the exact bytes of the installed package.
  • This project's combination of xlsx and js-yaml creates two potential surfaces for untrusted YAML to reach the parser — file uploads and configuration APIs — making the patch especially important here.
  • Algorithmic complexity attacks are not stopped by firewalls or WAFs — they require patching the vulnerable parsing logic itself.

How Orbis AppSec Detected This

  • Source: Untrusted YAML content entering the application via any code path that calls js-yaml's load() or safeLoad() functions — including user-uploaded files processed by the xlsx pipeline or direct YAML configuration endpoints.
  • Sink: The js-yaml flow collection parser internals in node_modules/js-yaml version 5.2.1, reachable via yaml.load(untrustedInput) anywhere in the codebase.
  • Missing control: No upper bound on parsing time or input complexity; the parser's flow collection handling lacked protection against adversarial backtracking inputs.
  • CWE: CWE-400 (Uncontrolled Resource Consumption) / CWE-1333 (Inefficient Regular Expression Complexity).
  • Fix: Upgraded js-yaml from 5.2.1 to 5.2.2 in both package.json and package-lock.json, replacing the vulnerable parser with one that processes flow collections in bounded linear time.

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.


Conclusion

The js-yaml GHSA-pm4m-ph32-ghv5 vulnerability is a sharp reminder that denial-of-service risk doesn't always come from network-level attacks or authentication bypasses — sometimes it lives in a parser's handling of a single data type. In this case, YAML flow collections in js-yaml 5.2.1 contained a latent algorithmic complexity flaw that could be triggered by any attacker who could get a crafted string to yaml.load(). The fix is minimal — a one-version bump to 5.2.2 — but the discipline required to catch it (automated scanning, locked dependencies, and prompt patching) is what separates secure software from vulnerable software.

Keep your lockfiles current, scan your dependencies in CI, and treat your YAML parser as a potential attack surface whenever it touches untrusted data.


References

Frequently Asked Questions

What is a denial-of-service vulnerability via exponential parsing time?

It is a class of vulnerability where a parser's time or resource consumption grows super-linearly (often exponentially) with certain input patterns, allowing an attacker to exhaust CPU or memory with a small, crafted payload.

How do you prevent algorithmic complexity attacks in JavaScript YAML parsing?

Use patched versions of YAML libraries, validate and size-limit untrusted input before parsing, and run YAML parsing in isolated worker threads with timeouts so a slow parse cannot block the main event loop.

What CWE is this type of vulnerability?

CWE-1333 (Inefficient Regular Expression Complexity) is the closest match, though the broader category is CWE-400 (Uncontrolled Resource Consumption).

Is input validation alone enough to prevent this vulnerability?

Not reliably. While size limits reduce risk, the only complete fix is to upgrade to js-yaml 5.2.2, where the parsing algorithm itself no longer exhibits exponential behavior.

Can static analysis detect this vulnerability?

Static analysis tools like Trivy (which flagged this issue) and Dependabot can detect known-vulnerable dependency versions. Detecting the underlying algorithmic flaw in custom code requires specialized tools like ReDoS analyzers or manual code review.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #94

Related Articles

high

How Denial of Service via Unbounded Intermediate Arrays happens in JavaScript and how to fix it

CVE-2026-69152 is a high-severity Denial of Service vulnerability in the `brace-expansion` npm package (versions prior to 1.1.18/2.1.4/3.0.6/5.0.9) that allows attackers to crash a Node.js application by crafting glob patterns that generate unbounded intermediate arrays, effectively bypassing the earlier CVE-2026-14257 mitigation. The fix upgrades `brace-expansion` from 1.1.14 to 1.1.18 in `frontend/package-lock.json`, closing the bypass and restoring safe memory bounds during pattern expansion.

high

How Quadratic CPU Consumption happens in JavaScript YAML parsing and how to fix it

A high-severity denial-of-service vulnerability (GHSA-5p4m-2wfm-xmqj) was discovered in js-yaml affecting both the 3.x and 4.x branches, where parsing YAML documents containing `!!omap` tags triggers quadratic CPU consumption. The fix upgrades js-yaml from `^4.1.1` to `5.2.0` in the project's GitHub Actions workflow dependencies, closing the attack surface for any untrusted YAML input processed by CI/CD tooling.

critical

How Missing Rate Limiting happens in Express.js and how to fix it

Two public API endpoints in `server.js` — `/api/health` and `/api/contact` — were exposed without any rate limiting middleware, allowing attackers to exhaust server resources or spam an SMTP server with unlimited requests. The fix adds rate limiting to both endpoints, with stricter controls on the resource-intensive `/api/contact` route that triggers email sending operations. This change closes a directly exploitable denial-of-service vector in a production web service.

high

How Denial of Service via Specific Input Sequence happens in JavaScript (marked) and how to fix it

CVE-2026-41680 is a high-severity Denial of Service vulnerability in the marked Markdown parsing library, affecting versions prior to 18.0.2. By supplying a crafted input sequence to the parser, an attacker can cause the application to hang or exhaust resources, making the frontend unavailable. Upgrading marked from 18.0.0 to 18.0.2 in both `package.json` and `package-lock.json` closes the vulnerability without affecting valid Markdown rendering.

high

How Quadratic CPU Consumption happens in JavaScript YAML parsing and how to fix it

A high-severity denial-of-service vulnerability in js-yaml (GHSA-5p4m-2wfm-xmqj) caused quadratic CPU consumption when resolving `!!omap` YAML types in both the 3.x and 4.x branches. The fix upgrades js-yaml from 3.14.2 to 3.15.1 and from 4.1.1 to 4.3.1, eliminating the algorithmic complexity exploit while leaving all valid YAML inputs unaffected.

high

How Denial of Service via Unbounded Data Happens in JavaScript and how to fix it

CVE-2025-58754 is a high-severity Denial of Service vulnerability in the popular axios HTTP client library, caused by the absence of a data size check on incoming response or request payloads. An attacker who can influence the size of data processed by axios could exhaust server memory or CPU, bringing down dependent Node.js applications. The fix upgrades axios from version 1.8.4 to 1.18.0, closing the unbounded data processing path.