Back to Blog
high SEVERITY7 min read

How Quadratic CPU Consumption in js-yaml's !!omap Resolution Happens in Node.js and How to Fix It

A high-severity algorithmic complexity vulnerability (GHSA-5p4m-2wfm-xmqj) in js-yaml versions 3.x and 4.x allowed attackers to trigger quadratic CPU consumption through crafted YAML input using the `!!omap` tag. The fix upgrades js-yaml from 4.1.1 to 4.3.1 in the Audex desktop music player, eliminating a denial-of-service vector that could freeze the Electron application when parsing untrusted YAML content.

O
By Orbis AppSec
Published September 1, 2026Reviewed September 1, 2026

Answer Summary

GHSA-5p4m-2wfm-xmqj is a high-severity algorithmic complexity vulnerability (CWE-400) in the js-yaml npm package (versions prior to 4.3.1 and 3.15.1) where parsing YAML documents containing crafted `!!omap` (ordered map) sequences triggers quadratic O(n²) CPU consumption, enabling denial of service. The fix is to upgrade js-yaml to version 4.3.1 (for the 4.x line) or 3.15.1 (for the 3.x line) by updating `package.json` and `package-lock.json` to pin the patched versions.

Vulnerability at a Glance

cweCWE-400 (Uncontrolled Resource Consumption)
fixUpgrade js-yaml from 4.1.1 to 4.3.1 (and 3.x to 3.15.1)
riskDenial of service via quadratic CPU consumption when parsing crafted YAML
languageJavaScript (Node.js)
root causeO(n²) duplicate-key checking algorithm in js-yaml's !!omap type resolver
vulnerabilityAlgorithmic Complexity / Denial of Service (ReDoS-like)

Introduction

In the Audex desktop music player repository (audex-player), Orbis AppSec's Trivy scanner flagged a high-severity vulnerability in package-lock.json: the project's dependency on js-yaml@4.1.1 exposed the application to GHSA-5p4m-2wfm-xmqj, a quadratic CPU consumption attack through crafted !!omap (ordered map) YAML sequences.

This isn't a theoretical concern. Audex is an Electron-based application — meaning it runs a full Node.js runtime on the user's desktop. If any part of the application parses YAML from an untrusted source (configuration files, playlist metadata, plugin manifests), an attacker could craft a payload that locks up the application's main thread, rendering the music player completely unresponsive. The vulnerability existed in js-yaml's !!omap type resolver, where duplicate-key validation used an O(n²) algorithm that could be weaponized with a relatively small input document.

The Vulnerability Explained

What Is !!omap in YAML?

YAML's !!omap tag represents an ordered mapping — essentially an array of key-value pairs where order matters and duplicate keys are forbidden. When js-yaml encounters !!omap during parsing, it must validate that no keys are duplicated. Here's where the problem lies.

The Quadratic Blowup

In js-yaml versions prior to 4.3.1 (and 3.15.1 in the 3.x line), the !!omap type resolver checked for duplicate keys using a nested comparison loop. For each new key encountered, the resolver compared it against every previously seen key. This is classic O(n²) behavior:

  • 100 keys → ~10,000 comparisons
  • 1,000 keys → ~1,000,000 comparisons
  • 10,000 keys → ~100,000,000 comparisons

An attacker doesn't need a massive payload. A YAML document with just a few thousand unique !!omap entries — perhaps 50–100 KB of text — can consume seconds to minutes of CPU time, effectively freezing the Node.js event loop.

Attack Scenario Against Audex

Consider this attack scenario specific to the Audex player:

# Malicious playlist metadata file
!!omap
  - key_0001: "track data"
  - key_0002: "track data"
  - key_0003: "track data"
  # ... thousands more unique keys ...
  - key_9999: "track data"

If Audex loads a playlist file, configuration, or any YAML-formatted data that passes through js-yaml.load(), this payload would cause the Electron main process to hang. The user sees a frozen window, and on some operating systems, the OS may prompt to kill the unresponsive application. This is a local denial of service — and in scenarios where YAML is fetched from a remote source (e.g., shared playlists), it becomes a remote denial of service.

The Vulnerable Dependency in package-lock.json

The pinned version before the fix:

"node_modules/js-yaml": {
  "version": "4.1.1",
  "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
  "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="
}

Version 4.1.1 contains the vulnerable !!omap resolver. The CVE-2026-59870 fix was applied upstream but had not been backported to the versions Audex was using, leaving the application exposed.

The Fix

The fix is a targeted dependency upgrade across two files: package.json and package-lock.json.

Before (Vulnerable)

// package-lock.json
"node_modules/js-yaml": {
  "version": "4.1.1",
  "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
  "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
  "license": "MIT",
  "dependencies": {
    "argparse": "^2.0.1"
  }
}

After (Patched)

// 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==",
  "funding": [
    {
      "type": "github",
      "url": "https://github.com/sponsors/puzrin"
    },
    {
      "type": "github",
      "url": "https://github.com/sponsors/nodeca"
    }
  ],
  "license": "MIT",
  "dependencies": {
    "argparse": "^2.0.1"
  }
}

What Changed Internally in js-yaml 4.3.1

The patched version replaces the O(n²) nested-loop duplicate detection in the !!omap resolver with a hash-set-based approach (O(n) amortized). Instead of comparing each new key against all previous keys in a linear scan, the resolver now inserts keys into a Set or object-based lookup, making duplicate detection constant-time per key.

Additional Changes in the PR

The PR also includes minor formatting normalizations in package.json:

  • Unicode character encoding: The em dash () in the description field was encoded as \u2014, and the copyright symbol (©) as \u00a9. These are cosmetic normalizations that occur during npm install regeneration.
  • Array formatting: The linux.target array was reformatted from inline ["AppImage", "deb"] to a multi-line format. This is standard npm lockfile regeneration behavior.

These changes are side effects of running npm install with the updated dependency and do not affect application behavior.

Key Takeaways

  • js-yaml's !!omap resolver prior to 4.3.1 used O(n²) duplicate-key checking, making it trivial to craft a small YAML document that consumes disproportionate CPU time — a classic algorithmic complexity attack.
  • Electron applications are especially vulnerable to event-loop-blocking attacks because a frozen main thread means a frozen UI, turning a parsing bug into a full application denial of service.
  • The package-lock.json pinned js-yaml at 4.1.1, which did not include the CVE-2026-59870 backport — demonstrating why lockfile auditing is essential, not just package.json review.
  • Schema restriction (yaml.CORE_SCHEMA) would have mitigated this vulnerability even without the upgrade, since !!omap is not part of the core YAML schema.
  • A 50 KB YAML payload could freeze an application for minutes — input size alone is not a reliable defense against algorithmic complexity attacks; the algorithm itself must be efficient.

How Orbis AppSec Detected This

  • Source: YAML content parsed by the js-yaml library (version 4.1.1) as declared in package-lock.json of the Audex player project
  • Sink: The !!omap type resolver internal to js-yaml@4.1.1, which performs O(n²) duplicate-key validation during yaml.load() calls
  • Missing control: No upgrade to the patched js-yaml version (4.3.1/3.15.1) that replaces the quadratic algorithm with linear-time duplicate detection; no input size limits or schema restrictions on YAML parsing
  • CWE: CWE-400 (Uncontrolled Resource Consumption)
  • Fix: Upgraded js-yaml from 4.1.1 to 4.3.1 in both package.json and package-lock.json, replacing the vulnerable !!omap resolver with the patched version that uses O(n) duplicate-key detection

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 !!omap quadratic CPU consumption vulnerability (GHSA-5p4m-2wfm-xmqj) is a textbook example of how algorithmic complexity bugs in widely-used parsing libraries can create real denial-of-service risks — especially in Electron applications where the main thread is sacred. The fix was straightforward: upgrade js-yaml from 4.1.1 to 4.3.1. But the lesson runs deeper. Dependency lockfiles deserve the same security scrutiny as application code, schema restrictions should be applied to all parsers handling untrusted input, and automated scanning tools are essential for catching vulnerabilities that hide in transitive dependency trees.

Don't wait for an attacker to freeze your application with a 50 KB YAML file. Audit your dependencies today.

Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #4

Related Articles

critical

How Arbitrary Code Execution Happens in protobufjs and How to Fix It

CVE-2026-41242 is a critical vulnerability in protobufjs versions 8.0.0 and earlier that allows attackers to execute arbitrary code by injecting malicious type fields into protobuf definitions. The fix upgrades the dependency from `^8.0.0` to `^8.6.6` in `core/package.json`, eliminating the unsafe code path that processed attacker-controlled type metadata without validation.

high

How Quadratic CPU Consumption Happens in JS-YAML and How to Fix It

A critical vulnerability in JS-YAML versions 3.x and 4.x allowed attackers to trigger quadratic CPU consumption through maliciously crafted YAML input using the `!!omap` tag resolver. The vulnerability stems from inefficient array operations in the ordered map resolution logic, which could be exploited for denial-of-service attacks. Upgrading to JS-YAML 4.3.1 or 3.15.1 patches this attack surface by optimizing the computational complexity of ordered map processing.

critical

How Type Confusion Vulnerabilities Happen in JavaScript Dependencies and How to Fix Them

A critical type confusion vulnerability (CVE-2021-23436) was discovered in immer 9.0.7, a popular immutable state management library used in the client application. By upgrading to immer 9.0.6, the vulnerability was patched, eliminating a flaw that could have allowed attackers to bypass previous security fixes (CVE-2020-28477). This fix demonstrates why keeping dependencies current is essential for maintaining application security.

critical

How Prototype Pollution Happens in i18next-fs-backend and How to Fix It

A critical prototype pollution vulnerability (CVE-2026-48713) was discovered in i18next-fs-backend versions prior to 2.6.6, where specially crafted missing-key strings could pollute the JavaScript object prototype. This fix upgrades the dependency to patch the vulnerability and prevent attackers from injecting malicious properties into application objects.

critical

How Prototype Pollution Happens in JavaScript Carousel Libraries and How to Fix It

A critical prototype pollution vulnerability (CVE-2026-27212) was discovered in Swiper versions up to 11.2.10, a popular JavaScript carousel library used in production web applications. This vulnerability could allow attackers to manipulate application behavior through the prototype chain. The fix involved upgrading Swiper from 11.2.10 to 12.1.2, which patches the underlying prototype pollution flaw.

high

modelExporter.js Path Traversal via Unsanitized Directory Concatenation

A path traversal vulnerability in `modelExporter.js` allowed attackers to read arbitrary files by injecting traversal sequences into directory and relative path parameters. The `readSourceFile` function concatenated these unsanitized inputs directly into file URLs passed to `fetch()`. The fix introduces strict path normalization that rejects attempts to escape the intended directory.