Back to Blog
high SEVERITY7 min read

How Denial of Service happens in js-yaml (Node.js) and how to fix it

A high-severity Denial of Service vulnerability (CVE-2026-59869) in the js-yaml parser allowed specially crafted YAML documents to exhaust CPU and memory when loaded by apps/dsa-desktop. The fix upgrades js-yaml to a patched release via the package-lock.json dependency tree, closing off the unbounded resource consumption path before it reached production.

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

Answer Summary

CVE-2026-59869 is a Denial of Service vulnerability (CWE-400, Uncontrolled Resource Consumption) in the js-yaml library used by Node.js/JavaScript applications, where crafted YAML documents with abusive anchors, aliases, or deeply nested structures can cause excessive CPU and memory usage during parsing. The fix is a dependency upgrade of js-yaml to a patched version in apps/dsa-desktop/package-lock.json that enforces internal limits on document complexity, preventing malicious YAML from triggering resource exhaustion.

Vulnerability at a Glance

cweCWE-400 (Uncontrolled Resource Consumption)
fixUpgrade js-yaml to a patched version in apps/dsa-desktop/package-lock.json that bounds resource usage during document loading
riskA malicious or untrusted YAML file can freeze or crash a service that parses it, denying availability to legitimate users
languageJavaScript / Node.js
root causeOlder js-yaml versions lacked adequate limits on anchor/alias expansion and nested structure depth during parsing
vulnerabilityDenial of Service via crafted YAML documents (js-yaml)

Introduction

The apps/dsa-desktop application relies on js-yaml, one of the most widely used YAML parsing libraries in the Node.js ecosystem, to load configuration files and other YAML-formatted data. It's the kind of dependency that gets pulled in transitively by build tools, CI configs, and app-level config loaders — often without anyone explicitly reviewing its security posture. That's exactly the risk: a flaw buried deep in apps/dsa-desktop/package-lock.json doesn't announce itself in application code, but it still runs every time a YAML document is parsed.

CVE-2026-59869 affects js-yaml versions that lacked sufficient limits when resolving certain YAML constructs — specifically, documents that abuse anchors (&), aliases (*), and deeply nested collections. When js-yaml loads such a document, older versions could be tricked into performing exponential amounts of work or allocating far more memory than the size of the input file would suggest. For an application like apps/dsa-desktop that may load YAML from configuration files, imported project data, or other semi-trusted sources, this is a real availability risk.

The Vulnerability Explained

YAML has a feature that makes it powerful but also dangerous if not carefully bounded: anchors and aliases. An anchor (&name) lets you tag a node, and an alias (*name) lets you reference that node elsewhere in the document, effectively reusing the same data multiple times without repeating it in the file. This is convenient for compact configuration, but it's also the exact mechanism behind "YAML bombs" — the YAML equivalent of the classic XML "billion laughs" attack.

A minimal illustration of the pattern that triggers this class of bug looks like this:

a: &a ["lol","lol","lol","lol","lol","lol","lol","lol","lol"]
b: &b [*a,*a,*a,*a,*a,*a,*a,*a,*a]
c: &c [*b,*b,*b,*b,*b,*b,*b,*b,*b]
d: &d [*c,*c,*c,*c,*c,*c,*c,*c,*c]
e: &e [*d,*d,*d,*d,*d,*d,*d,*d,*d]

Each layer references the layer below it nine times. A file that's only a few hundred bytes on disk can expand into millions of elements once fully resolved in memory — and each additional layer multiplies the blow-up exponentially. In a vulnerable js-yaml version, calling yaml.load() (or the equivalent loader used by whatever config-loading code sits behind apps/dsa-desktop) on a document like this doesn't just slow things down; it can peg the event loop and balloon memory usage until the process is killed or becomes unresponsive to every other request or task it's supposed to handle.

Attack scenario: Imagine apps/dsa-desktop accepts a project configuration file, a plugin manifest, or an imported settings bundle in YAML format — something a user might upload, sync from a remote source, or receive from a teammate. An attacker crafts a small YAML file using nested anchor/alias expansion (or deeply nested mappings/sequences that stress the recursive parser) and gets it processed by the vulnerable js-yaml version pinned in the lockfile. The parsing call hangs or the process runs out of memory, and the desktop app — or any backend service sharing that dependency — becomes unavailable. No authentication bypass, no data theft; just a single crafted file taking down availability, which is exactly the profile of CWE-400 (Uncontrolled Resource Consumption).

The Fix

The remediation here is a dependency-level fix: bumping the pinned js-yaml version in apps/dsa-desktop/package-lock.json to a release that hardens the parser against these YAML bomb patterns. This mirrors the same remediation approach used elsewhere in this repository — for example, the companion fix in this PR upgrades axios from 1.13.4/0.30.3 to 1.13.5/newer to resolve CVE-2026-25639 (a prototype-pollution DoS via __proto__ in mergeConfig). Both fixes share the same shape: a transitive dependency shipped a known-safe defensive check that the older pinned version was missing, and the lockfile is updated so the resolved package tree pulls in the patched code instead of the vulnerable one.

Conceptually, the change to the lockfile entry looks like the pattern below (shown here using the same diff style as the axios upgrade included in this PR):

     "node_modules/js-yaml": {
-      "version": "<vulnerable-version>",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-<vulnerable-version>.tgz",
-      "integrity": "sha512-...",
+      "version": "<patched-version>",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-<patched-version>.tgz",
+      "integrity": "sha512-...",
       "license": "MIT",
       "bin": {
         "js-yaml": "bin/js-yaml.js"
       }
     },

Nothing in application code needs to change: no calling convention, no schema, no API. That's the point of a well-scoped SCA (Software Composition Analysis) fix — the vulnerable logic lives entirely inside the third-party package, so resolving it to a version where the maintainers have already added the missing resource limits is sufficient. Any code in apps/dsa-desktop that calls yaml.load() or yaml.safeLoad() continues to work identically for legitimate documents; only the pathological anchor/alias/nesting patterns that previously caused runaway resource consumption are now rejected or bounded.

Prevention & Best Practices

  • Pin and patch YAML parsers aggressively. js-yaml, like most parsing libraries handling arbitrary structured input, has had multiple DoS-class advisories over the years. Treat it the same way you'd treat a JSON or XML parser exposed to untrusted input — keep it current.
  • Never assume config files are "trusted enough." Configuration YAML that originates from user uploads, plugin systems, third-party integrations, or synced project files should be treated as untrusted input, even if it's "just config."
  • Add resource guards around parsing. Where possible, enforce a maximum file size before parsing, wrap the parse call with a timeout, and consider running YAML parsing for untrusted sources in a worker or sandboxed process so a hang doesn't take down the main event loop.
  • Prefer restrictive schemas. js-yaml's DEFAULT_SCHEMA supports more YAML tag types than most applications need. Using a minimal schema (e.g., JSON_SCHEMA) where possible reduces the surface for both DoS and object-injection-style attacks.
  • Automate dependency scanning. Tools like Trivy, npm audit, and Snyk catch exactly this class of issue by matching pinned versions in package-lock.json against known CVE databases — which is how this vulnerability was surfaced in the first place.

Key Takeaways

  • CVE-2026-59869 shows that YAML parsing is not a "free" operation — anchor/alias expansion in js-yaml can turn a tiny file into a resource-exhaustion attack.
  • The vulnerable dependency was pinned in apps/dsa-desktop/package-lock.json, meaning the fix required zero application-code changes — just a version bump to a patched js-yaml release.
  • Any code path in apps/dsa-desktop that calls into js-yaml to load configuration, project, or import data should be treated as a potential DoS entry point if the source of that YAML isn't fully trusted.
  • This fix follows the same remediation pattern as the accompanying axios upgrade in this PR (CVE-2026-25639) — both are lockfile-scoped dependency bumps that close known DoS vectors without touching business logic.
  • Regularly auditing package-lock.json for known-vulnerable transitive dependencies is as important as reviewing first-party code for security bugs.

How Orbis AppSec Detected This

  • Source: A crafted YAML document (e.g., a configuration, import, or project file) passed into js-yaml's load functions from within apps/dsa-desktop.
  • Sink: js-yaml's internal loader logic responsible for resolving anchors, aliases, and nested structures in the vulnerable pinned version.
  • Missing control: No enforced limit on anchor/alias expansion or structural nesting depth in the outdated js-yaml version, allowing exponential resource consumption from a small input.
  • CWE: CWE-400 (Uncontrolled Resource Consumption).
  • Fix: Upgraded the pinned js-yaml dependency in apps/dsa-desktop/package-lock.json to a patched version that enforces internal safeguards against YAML bomb–style documents.

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

Denial of Service vulnerabilities in parsing libraries like js-yaml are easy to overlook because they don't leak data or grant unauthorized access — they simply make availability fragile in the face of untrusted input. CVE-2026-59869 is a reminder that "just parsing a config file" is still an attack surface, and that transitive dependencies deserve the same version discipline as first-party code. The fix in apps/dsa-desktop/package-lock.json — upgrading to a patched js-yaml release — closes this gap with no functional risk to legitimate YAML documents, while eliminating the path for a crafted file to exhaust CPU or memory. Pair dependency upgrades like this with resource guards (size limits, timeouts, restrictive schemas) around any parser that touches untrusted input, and keep automated SCA scanning in your CI pipeline so the next vulnerable version doesn't sit unpatched.

References

  • CWE-400: Uncontrolled Resource Consumption — https://cwe.mitre.org/data/definitions/400.html
  • CWE-674: Uncontrolled Recursion — https://cwe.mitre.org/data/definitions/674.html
  • OWASP Denial of Service Cheat Sheet — https://cheatsheetseries.owasp.org/cheatsheets/Denial_of_Service_Cheat_Sheet.html
  • js-yaml official documentation and security advisories — https://github.com/nodeca/js-ya

Frequently Asked Questions

What is a js-yaml Denial of Service vulnerability?

It's a flaw where js-yaml's parser can be forced to consume excessive CPU or memory when loading a specially crafted YAML document, for example through recursive alias/anchor references or deeply nested collections, causing the parsing process to hang or crash.

How do you prevent Denial of Service in js-yaml in Node.js?

Always run the latest patched version of js-yaml, avoid calling `yaml.load()` on fully untrusted input without size/depth limits, and prefer `yaml.safeLoad`/schema restrictions along with input size caps and timeouts around parsing.

What CWE is js-yaml Denial of Service?

It maps to CWE-400 (Uncontrolled Resource Consumption), and related YAML parsing DoS issues can also fall under CWE-674 (Uncontrolled Recursion) or CWE-1333 (Inefficient Regular Expression Complexity) depending on the internal cause.

Is upgrading the library alone enough to prevent this Denial of Service?

Upgrading closes the specific known parsing weaknesses, but defense-in-depth like enforcing maximum file size, parse timeouts, and running parsers in isolated/resource-limited contexts is recommended for any code that parses untrusted YAML.

Can static analysis detect this Denial of Service vulnerability?

Yes — software composition analysis (SCA) tools like Trivy, npm audit, or Snyk can flag known-vulnerable versions of js-yaml pinned in package-lock.json, which is exactly how this issue was surfaced.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #2256

Related Articles

critical

How a vulnerable websocket-driver dependency happens in Node.js lockfiles and how to fix it

A Trivy scan flagged `websocket-driver@0.7.4` in this repository's `bun.lock` as affected by CVE-2026-54466, a critical issue in a WebSocket protocol handler that parses untrusted HTTP upgrade requests and frame data. The fix upgrades the package to `0.7.5` and adds an explicit `websocket-driver` entry to the lockfile's override block so every transitive consumer — webpack-dev-server, sockjs, faye-websocket — resolves to the patched build instead of the pinned vulnerable one.

high

How Dependabot Missing Cooldown Periods Enable Supply Chain Attacks and How to Fix It

A critical security vulnerability in `.github/dependabot.yml` was exposing a Node.js library to supply chain attacks by automatically updating to newly published packages without a safety delay. By adding a 7-day cooldown period to each package ecosystem configuration, the project now protects against malicious or unstable package versions that could affect downstream consumers.

high

How Exponential-Time Complexity Causes Denial of Service in brace-expansion and How to Fix It

A critical vulnerability in brace-expansion versions 1.1.13 and earlier allowed attackers to cause denial of service through crafted brace pattern inputs. The fix upgrades to patched versions 1.1.16, 2.1.2, and 5.0.7, eliminating the exponential-time complexity that made exploitation possible.

high

How unrestricted file upload via extension-only validation happens in Deno/JavaScript and how to fix it

The review image upload handler in this Deno-based app trusted the client-supplied filename extension to decide whether a file was a "safe" image, without ever inspecting the actual file bytes. The fix adds magic-byte signature verification for PNG, JPEG, GIF, and WEBP formats before the file is written to disk, closing the door on disguised executables and malicious payloads.

high

How Missing Dependabot Cooldown Periods Enable Supply Chain Attacks in CI/CD Pipelines and How to Fix Them

We fixed a high-severity supply chain security gap in `.github/dependabot.yml` where missing cooldown periods allowed immediate adoption of newly published packages. The fix adds `cooldown: default-days: 7` to all package ecosystems, creating a critical security buffer against typosquatting and malicious dependency attacks.

high

How Dependabot Missing Cooldown Vulnerability Happens in GitHub Actions and How to Fix It

Dependabot configurations without cooldown periods can automatically propose updates from newly published packages within hours—potentially including malicious or unstable versions. This vulnerability in `.github/dependabot.yml` was fixed by adding a `cooldown` block with `default-days: 7` to delay updates and allow time for community vetting.