Back to Blog
high SEVERITY6 min read

How Quadratic Complexity Denial of Service happens in JavaScript YAML parsing and how to fix it

A high-severity dependency vulnerability in `js-yaml`, used transitively by `electron-builder`/`electron-updater` inside the DSA Desktop app, allowed attacker-controlled YAML with `!!omap` tags to trigger quadratic-time CPU consumption during parsing. The fix pins `js-yaml` to the patched `4.3.1` (and `3.15.1` for the legacy branch) release via both `package.json` overrides and `package-lock.json`.

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published September 7, 2026•Reviewed September 7, 2026

Answer Summary

This vulnerability is a Denial-of-Service (CWE-407, Inefficient Algorithmic Complexity) in the `js-yaml` npm package, where resolving the `!!omap` YAML type used a quadratic-time algorithm that could be abused with crafted YAML input to exhaust CPU. It affects `apps/dsa-desktop`, which pulls `js-yaml` transitively through `electron-builder`/`electron-updater`. The fix upgrades `js-yaml` from `4.1.1` to the patched `4.3.1` release (and `3.15.1` on the legacy line) and pins the version via an npm `overrides` entry so all transitive resolutions use the safe version.

Vulnerability at a Glance

cweCWE-407 (Inefficient Algorithmic Complexity)
fixUpgrade `js-yaml` to 4.3.1 (and 3.15.1) and pin the version with an npm `overrides` entry
riskMalicious or malformed YAML processed by build/update tooling could cause CPU exhaustion and hang the process
languageJavaScript / Node.js (npm dependency)
root causejs-yaml 4.1.1 (and pre-3.15.1) used an O(n²) algorithm when resolving `!!omap` mappings
vulnerabilityQuadratic CPU Consumption / Algorithmic Complexity Denial of Service in js-yaml `!!omap` resolution

Introduction

The apps/dsa-desktop application ships as an Electron desktop app that relies on electron-builder and electron-updater to manage build configuration and auto-update metadata — both of which parse YAML files under the hood using the js-yaml package. A scan by Orbis AppSec flagged apps/dsa-desktop/package-lock.json for pulling in js-yaml@4.1.1, a version affected by GHSA-5p4m-2wfm-xmqj, a high-severity quadratic CPU consumption issue in how js-yaml resolves the !!omap YAML type.

This isn't a bug in application code — it's a transitive dependency risk. js-yaml is one of the most widely used YAML parsers in the JavaScript ecosystem, and it's pulled in indirectly by build tooling, CI configuration loaders, and update mechanisms like electron-updater. If any part of the build or update pipeline parses YAML that isn't fully trusted (for example, YAML fetched from a remote update feed, a CI artifact, or a config file supplied by a third party), an attacker-crafted document could trigger pathological CPU usage.

The Vulnerability Explained

The root cause lives inside js-yaml's type resolution logic for the !!omap tag — YAML's "ordered map" type. In the affected versions (including 4.1.1, which was pinned in this repo before the fix), the resolver used an algorithm whose runtime scales quadratically, O(n²), with the number of entries in the map being resolved.

Before the fix, package-lock.json looked like this:

"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"
  }
}

This version was resolved and installed without any override, meaning any code path in electron-builder/electron-updater that calls into js-yaml to parse configuration or update metadata would run with the vulnerable resolver.

How this could be exploited

Picture an attacker who can influence YAML content consumed anywhere along the build or auto-update pipeline for apps/dsa-desktop — for instance, a malicious or compromised electron-builder config, a spoofed update feed response, or a crafted CI artifact that gets parsed as YAML. If that YAML contains a deeply structured !!omap block, the parser's quadratic-time resolution means that even a moderate document (say, a few thousand ordered-map entries) can force js-yaml — and by extension, the Node.js process running the build or the desktop app's update checker — to burn CPU cycles disproportionately to the input size.

In a build server context, this could stall CI pipelines. In a desktop-app update context, it could hang the update-check process, degrading the user experience or creating a denial-of-service condition against the app's own tooling. The Orbis AppSec assessment notes this was "present in dependency tree, not confirmed reachable" — meaning it wasn't proven to be actively exploitable in this codebase's current call paths, but the exploit primitive existed and could be chained by automated tooling if a reachable code path is introduced later.

The Fix

The fix is a targeted dependency upgrade, touching exactly two files: apps/dsa-desktop/package.json and apps/dsa-desktop/package-lock.json.

1. Upgrading the resolved version in 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==",
+      "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"
       }

This locks the resolved package to js-yaml@4.3.1, which contains the backported fix for the !!omap quadratic-time resolution — restoring linear-time behavior for ordered map parsing.

2. Pinning the version with an npm overrides entry in package.json:

   "overrides": {
-    "builder-util-runtime": "9.7.0"
+    "builder-util-runtime": "9.7.0",
+    "js-yaml": "4.3.1"
   }

This second change is just as important as the version bump itself. Because js-yaml is a transitive dependency — pulled in indirectly through electron-builder and electron-updater — simply bumping the lockfile entry isn't enough to guarantee future installs stay safe. Any nested dependency could still declare a semver range that resolves back down to a vulnerable 4.x release. Adding "js-yaml": "4.3.1" to the overrides block forces npm to resolve every occurrence of js-yaml in the dependency tree to the patched version, regardless of what version range upstream packages request.

Key Takeaways

  • js-yaml@4.1.1, previously locked in apps/dsa-desktop/package-lock.json, contained a quadratic-time algorithm in its !!omap type resolver (GHSA-5p4m-2wfm-xmqj / CVE-2026-59870).
  • The fix upgrades to js-yaml@4.3.1 (with 3.15.1 for consumers still on the 3.x line) and hard-pins the version via package.json's overrides field.
  • js-yaml was present as a transitive dependency of electron-builder/electron-updater, illustrating how build tooling can silently drag vulnerable parsers into a desktop application's dependency tree.
  • Even when a flaw is "not confirmed reachable" in current code paths, removing the exploit primitive proactively — as was done here — reduces the attack surface available to automated exploit chaining tools.
  • Pinning transitive dependencies with overrides/resolutions is a reusable pattern already established in this codebase (builder-util-runtime) and should be applied consistently to other security-sensitive transitive packages.

How Orbis AppSec Detected This

  • Source: Untrusted YAML documents processed by electron-builder/electron-updater tooling (e.g., build configuration or update-feed metadata) that reach js-yaml's type resolution logic.
  • Sink: js-yaml's internal !!omap type resolver, invoked whenever js-yaml.load()/safeLoad() parses a YAML document containing an ordered-map tag, bundled transitively in apps/dsa-desktop/package-lock.json.
  • Missing control: No pinned/patched version of js-yaml was enforced across the dependency tree — the vulnerable 4.1.1 release was resolved with no overrides entry to force an upgrade.
  • CWE: CWE-407 (Inefficient Algorithmic Complexity), commonly grouped under Denial-of-Service issues.
  • Fix: Upgraded js-yaml to 4.3.1 (3.15.1 on the legacy line) and added an overrides entry in package.json to force the patched version across all transitive resolutions.

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

This vulnerability is a good reminder that security risk doesn't only come from code you write — it also comes from the dependency graph beneath your build and update tooling. A quadratic-time flaw in js-yaml's !!omap resolution sat quietly in apps/dsa-desktop's lockfile, reachable only through the transitive chain from electron-builder/electron-updater. The fix was small but precise: bump js-yaml to the patched 4.3.1 release and enforce that version tree-wide with an npm overrides entry, following the same pattern already used for builder-util-runtime. Treat dependency advisories — even DoS-class ones — with the same urgency as injection or RCE bugs, and use version-pinning mechanisms to make sure a patched version actually sticks across your entire dependency tree.

Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #2254

Related Articles

critical

Lampa Desktop Auto-Update Heuristic Bypass: Execution of Unverified

Lampa Desktop's auto-update mechanism downloaded JavaScript and CSS from `raw.githubusercontent.com` using only heuristic validation—file size thresholds and string pattern matching—that attackers could trivially satisfy. The fix introduces cryptographic integrity verification by cross-referencing Git blob hashes from the GitHub Contents API, ensuring downloaded code matches the repository's authoritative state before execution.

high

adm-zip 0.6.0 Preserves SUID Bits From ZIPs: CVE-2026-102282

The `adm-zip` dependency resolved to 0.6.0 in this project's dependency tree, a version affected by CVE-2026-102282: during extraction it applies the Unix permission bits stored in each ZIP entry's external file attributes verbatim, including the setuid (`04000`), setgid (`02000`), and sticky bits. An attacker who controls an archive passed to `extractAllTo()` or `extractEntryTo()` can therefore have the extractor create a setuid binary owned by whatever user the extraction process runs as. The

high

requestInput() Type Confusion: NaN and Object Bypass in JavaScript

The `requestInput()` utility function lacked validation on its `type` parameter and failed to handle `NaN` results from float conversions, creating a type confusion weakness. An attacker could supply malformed inputs that propagate unhandled `NaN` values or unexpected object types through the type system. The fix adds explicit guards against `NaN` type parameters and rejects non-primitive type values.

critical

No Rate Limit on /api/uploads/presign Enables DoS

The `/api/uploads/presign` endpoint accepted unlimited concurrent requests to generate storage presigned URLs, giving an attacker a free lever to exhaust storage-provider quotas and server resources. The fix adds an `express-rate-limit` middleware capping each client to 30 requests per minute on that route.

high

CVE-2026-54673: builder-util-runtime Leaks Auth Headers on Redirect

electron-updater and electron-builder rely on builder-util-runtime to fetch update manifests and artifacts over HTTP. A flaw in that shared HTTP executor allowed credential headers attached to the original update-feed request to be re-sent after a redirect, exposing them to any host the redirect pointed to. The project fixes this by upgrading builder-util-runtime to 9.7.0 and collapsing a duplicate, older copy of the package that electron-updater had pinned on its own.

high

image-size 1.2.1 DoS: Zero-Valued Dimensions in Image Buffer Parser

A high-severity denial-of-service vulnerability in image-size 1.2.1 allows attackers to crash Node.js services using malicious image buffers with zero-valued dimensions. The fix removes the vulnerable `queue` dependency and tightens dimension validation in version 2.0.3.