Back to Blog
high SEVERITY6 min read

How Quadratic CPU Consumption Vulnerabilities Happen in JavaScript YAML Parsers and How to Fix Them

A high-severity denial-of-service vulnerability in js-yaml versions 3.x and 4.x allowed attackers to trigger quadratic CPU consumption through specially crafted YAML documents using the !!omap tag. This fix upgrades js-yaml from 4.1.1 to 4.3.1 and from 3.14.2 to 3.15.1, eliminating the algorithmic complexity attack vector that could freeze Node.js applications processing untrusted YAML input.

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

Answer Summary

GHSA-5p4m-2wfm-xmqj is a denial-of-service vulnerability in the js-yaml JavaScript library affecting versions 3.x (before 3.15.1) and 4.x (before 4.3.1). The vulnerability allows quadratic CPU consumption when parsing YAML documents containing the !!omap (ordered map) tag due to inefficient duplicate key detection. The fix requires upgrading js-yaml to version 4.3.1 or 3.15.1, which implements linear-time duplicate checking algorithms.

Vulnerability at a Glance

cweCWE-407
fixUpgrade js-yaml to 4.3.1 (or 3.15.1 for 3.x branch)
riskApplication freeze or crash when processing malicious YAML input
languageJavaScript
root causeQuadratic time complexity in !!omap duplicate key resolution
vulnerabilityAlgorithmic Complexity / Denial of Service

Introduction

In a Node.js project's package-lock.json, we discovered a high-severity denial-of-service vulnerability lurking in the js-yaml dependency. The project was using js-yaml version 4.1.1 as a direct dependency and version 3.14.2 as a transitive dependency through @istanbuljs/load-nyc-config. Both versions contained GHSA-5p4m-2wfm-xmqj, a vulnerability that allows attackers to freeze applications by sending specially crafted YAML documents.

This isn't a theoretical risk—any application that parses YAML from untrusted sources (configuration files, API payloads, user uploads) could be completely locked up by a relatively small malicious input. The vulnerability stems from how js-yaml resolves the !!omap (ordered map) YAML tag, using an algorithm with quadratic time complexity that an attacker can exploit.

The Vulnerability Explained

What is the !!omap Tag?

YAML supports ordered maps through the !!omap tag, which guarantees that keys maintain their insertion order. When js-yaml parses an !!omap, it must verify that all keys are unique—a fundamental requirement for valid ordered maps.

The Quadratic Complexity Problem

The vulnerable versions of js-yaml (4.1.1 and 3.14.2) used an inefficient algorithm for duplicate key detection. Instead of using a hash-based lookup (O(1) per check, O(n) total), the implementation compared each new key against all previously seen keys (O(n) per check, O(n²) total).

Here's what the problematic pattern looks like conceptually:

// Vulnerable approach (simplified)
function checkDuplicates(keys) {
  for (let i = 0; i < keys.length; i++) {
    for (let j = 0; j < i; j++) {
      if (keys[i] === keys[j]) {
        throw new Error('Duplicate key');
      }
    }
  }
}

With 1,000 keys, this performs ~500,000 comparisons. With 10,000 keys, it performs ~50,000,000 comparisons. An attacker can craft a YAML document that triggers this worst-case behavior.

Attack Scenario

Consider this application that processes user-submitted configuration:

const yaml = require('js-yaml'); // version 4.1.1

app.post('/api/config', (req, res) => {
  try {
    const config = yaml.load(req.body.yamlContent);
    // Process configuration...
    res.json({ success: true });
  } catch (e) {
    res.status(400).json({ error: 'Invalid YAML' });
  }
});

An attacker could submit a YAML document like:

--- !!omap
- key_00001: value
- key_00002: value
- key_00003: value
# ... thousands more unique keys ...
- key_50000: value

Even though all keys are unique (no actual duplicates), the quadratic duplicate-checking algorithm would consume massive CPU time verifying this. A document with 50,000 keys could lock up the Node.js event loop for minutes, effectively causing a denial of service for all users.

Real-World Impact

For this specific project, the vulnerability existed in two places:

  1. Direct dependency: js-yaml@4.1.1 used for parsing application configuration
  2. Dev dependency: js-yaml@3.14.2 pulled in by @istanbuljs/load-nyc-config for code coverage configuration

While the dev dependency only affects the build/test environment, the direct dependency poses a production risk if the application processes any YAML from external sources.

The Fix

The fix upgrades both vulnerable js-yaml instances to patched versions that implement efficient O(n) duplicate detection.

Before (Vulnerable)

// package.json
"dependencies": {
  "js-yaml": "^4.1.1",
  // ...
}

// package-lock.json
"node_modules/js-yaml": {
  "version": "4.1.1",
  "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
  // ...
}

"node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": {
  "version": "3.14.2",
  "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
  // ...
}

After (Fixed)

// package.json
"dependencies": {
  "js-yaml": "^4.3.1",
  // ...
}

// package-lock.json
"node_modules/js-yaml": {
  "version": "4.3.1",
  "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",
  "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
  // ...
}

"node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": {
  "version": "3.15.1",
  "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.1.tgz",
  "integrity": "sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==",
  // ...
}

Why Both Files Changed

  1. package.json: Updates the declared dependency range from ^4.1.1 to ^4.3.1, ensuring new installs get the patched version
  2. package-lock.json: Locks the exact resolved versions (4.3.1 and 3.15.1) and updates integrity hashes, ensuring reproducible builds with the secure versions

The patched versions replace the O(n²) comparison loop with a hash-based Set lookup:

// Fixed approach (simplified)
function checkDuplicates(keys) {
  const seen = new Set();
  for (const key of keys) {
    if (seen.has(key)) {
      throw new Error('Duplicate key');
    }
    seen.add(key);
  }
}

This reduces 50,000,000 operations back down to 50,000—a 1000x improvement that eliminates the denial-of-service vector.

Prevention & Best Practices

1. Keep Dependencies Updated

Use automated dependency scanning tools to catch vulnerable packages:

# npm audit
npm audit

# Or use dedicated security scanners
trivy fs --scanners vuln .

2. Implement Parsing Timeouts

Even with patched libraries, defense in depth matters:

const { Worker } = require('worker_threads');

function parseYamlWithTimeout(content, timeoutMs = 5000) {
  return new Promise((resolve, reject) => {
    const worker = new Worker(`
      const yaml = require('js-yaml');
      const { parentPort } = require('worker_threads');
      parentPort.on('message', (content) => {
        parentPort.postMessage(yaml.load(content));
      });
    `, { eval: true });

    const timeout = setTimeout(() => {
      worker.terminate();
      reject(new Error('YAML parsing timeout'));
    }, timeoutMs);

    worker.on('message', (result) => {
      clearTimeout(timeout);
      resolve(result);
    });

    worker.postMessage(content);
  });
}

3. Limit Input Size

Reject oversized YAML documents before parsing:

const MAX_YAML_SIZE = 1024 * 1024; // 1MB

app.post('/api/config', (req, res) => {
  if (req.body.yamlContent.length > MAX_YAML_SIZE) {
    return res.status(413).json({ error: 'YAML document too large' });
  }
  // Parse YAML...
});

4. Use Dependabot or Renovate

Configure automated dependency updates to catch security patches quickly:

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "daily"
    open-pull-requests-limit: 10

Key Takeaways

  • js-yaml versions before 4.3.1 and 3.15.1 contain a DoS vulnerability in !!omap resolution that can freeze your application
  • Transitive dependencies matter: The vulnerability existed in both direct and dev dependencies through @istanbuljs/load-nyc-config
  • Algorithmic complexity attacks bypass input validation: The malicious YAML is syntactically valid, so schema validation won't help
  • Both package.json AND package-lock.json must be updated to ensure the fix is applied consistently across environments
  • Defense in depth is essential: Even with patched libraries, implement timeouts and size limits for parsing untrusted input

How Orbis AppSec Detected This

  • Source: YAML content from external input (configuration files, API payloads)
  • Sink: yaml.load() call in application code using vulnerable js-yaml versions
  • Missing control: No patched version of js-yaml that implements efficient duplicate key detection
  • CWE: CWE-407 (Inefficient Algorithmic Complexity)
  • Fix: Upgraded js-yaml from 4.1.1 to 4.3.1 and from 3.14.2 to 3.15.1 in package.json and package-lock.json

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 quadratic CPU consumption vulnerability (GHSA-5p4m-2wfm-xmqj) demonstrates how algorithmic complexity issues can turn seemingly innocent parsing operations into denial-of-service vectors. By upgrading to js-yaml 4.3.1 (or 3.15.1 for the 3.x branch), you eliminate this attack surface while maintaining full backward compatibility for valid YAML documents.

Remember that dependency security is an ongoing process. Automated scanning tools like Trivy, combined with proactive dependency management, help ensure vulnerabilities like this are caught and fixed before they can be exploited. Always treat YAML parsing of untrusted input as a potential attack vector and implement appropriate safeguards.

References

Frequently Asked Questions

What is algorithmic complexity vulnerability?

An algorithmic complexity vulnerability occurs when an algorithm's worst-case performance is significantly worse than expected, allowing attackers to craft inputs that consume excessive CPU time or memory, causing denial of service.

How do you prevent algorithmic complexity attacks in JavaScript?

Use libraries with proven O(n) or O(n log n) algorithms for parsing, implement input size limits, set timeouts for parsing operations, and keep dependencies updated to patched versions.

What CWE is algorithmic complexity vulnerability?

CWE-407 (Inefficient Algorithmic Complexity) covers vulnerabilities where algorithms have worst-case complexity that can be exploited to cause denial of service.

Is input validation enough to prevent YAML DoS attacks?

No, input validation alone cannot prevent algorithmic complexity attacks because the malicious payload may appear syntactically valid. You must use patched library versions with efficient algorithms.

Can static analysis detect algorithmic complexity vulnerabilities?

Static analysis tools can detect known vulnerable library versions through dependency scanning (like Trivy), but detecting novel algorithmic complexity issues in custom code requires specialized analysis.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #753

Related Articles

high

How Sensitive Data Exposure happens in Zotero plugins and how to fix it

A high-severity data exposure vulnerability in `Zotero.ts` automatically transmitted complete document metadata—including private notes, attachment paths, and tags—to external LLM services without user consent. The fix replaces broad `item.toJSON()` serialization with explicit field selection, sending only essential bibliographic data.

high

How missing dependency update cooldowns happen in GitHub Dependabot configurations and how to fix it

A semgrep scan flagged `.github/dependabot.yml` for lacking a cooldown period, meaning Dependabot would immediately propose updates to brand-new package versions across npm, Bundler, and Docker ecosystems. The fix adds a `cooldown: default-days: 7` block to every `package-ecosystem` entry, forcing a one-week waiting period before newly published releases are considered — reducing exposure to malicious or unstable package drops.

high

How dependabot-missing-cooldown happens in GitHub Actions/Node.js and how to fix it

The repository's `.github/dependabot.yml` had no cooldown period configured, meaning Dependabot could immediately propose updates to newly published package versions with zero time for the community to flag malware or instability. The fix adds a `cooldown` block with `default-days: 7` to both the `npm` and `github-actions` ecosystems, forcing a 7-day waiting period before new releases are surfaced as update PRs.

high

How Path Traversal Happens in TensorFlow's Data Service and How to Fix It

TensorFlow's data service dispatcher validated dataset IDs against forward-slash traversal attacks but overlooked backslash characters on non-Windows platforms, allowing attackers to escape the root directory. A targeted fix adds explicit backslash validation across all platforms, closing a high-severity path traversal vulnerability in the snapshot management system.

critical

How Unbounded WebSocket Message Handling Causes Resource Exhaustion in Node.js and How to Fix It

The WebSocketCrossServerAdapter class in a popular Node.js WebSocket library lacked any rate limiting on inbound messages, allowing attackers to flood Redis nodes and WebSocket servers with high-volume traffic. The fix introduces a configurable `rateLimit` option that caps messages per connection per second, preventing resource exhaustion while preserving legitimate functionality.

critical

How Remote Code Execution Happens in Handlebars Template Compilation and How to Fix It

CVE-2026-33937 is a critical remote code execution vulnerability in Handlebars.js that allows attackers to execute arbitrary code by passing maliciously crafted Abstract Syntax Tree (AST) objects to the compile() function. The vulnerability was patched in version 4.7.9, and we've upgraded to protect against this threat vector.