GitHub Advisoryadvisory · npm

GHSA-xvcm-6775-5m9r Immutable: Hash-collision algorithmic complexity denial of service in Immutable.Map/Set

HIGH Published 2026-07-21 Modified 2026-09-03 Official page ↗
Aliases
CVE-2026-59880
Ecosystem
npm
Packages
immutable
Severity
High
CVSS vector
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
Weaknesses
CWE-400, CWE-407
Reviewed
2026-07-21

Affected packages

• npm immutable: >= 4.0.0-beta.1, < 4.3.9 → fixed in 4.3.9 • npm immutable: >= 5.0.0-beta.1, < 5.1.8 → fixed in 5.1.8 • npm immutable: < 3.8.4 → fixed in 3.8.4

Description

Summary `Immutable.Map` and `Immutable.Set` keep keys that share the same 32-bit hash in a collision bucket that is scanned linearly. The string hash is public and deterministic, so an attacker who controls the keys inserted into a Map can craft many keys that all collide, degrading insertion and lookup from amortized O(1) to O(n) per operation — and O(n²) to build or read the whole set. A small, attacker-shaped payload can therefore consume disproportionate CPU and, on a single-threaded runtime such as Node.js, stall the event loop and deny service. Details The string hash uses the JVM-style polynomial `hashed = (31 * hashed + charCode) | 0`. Strings such as `"Aa"` and `"BB"` hash to the same value (`65*31+97 == 66*31+66 == 2112`), and concatenating such blocks yields `2^n` distinct strings sharing one hash (40 characters ⇒ >1,000,000 colliding keys). All such keys route to a single `HashCollisionNode`, whose `get`/`update` walk the entire bucket testing `is()`. There is no per-process salt, so the colliding set is fully precomputable from the open-source algorithm. Proof of concept Inserting N colliding keys (e.g. via `Immutable.Map(obj)` / `Immutable.fromJS(obj)`) is O(N²). Measured on one machine, ~8,000 colliding keys take ~0.7 s to build and ~0.6 s to read, scaling ×4 per doubling; ~16,000 keys exceed several seconds. Impact CPU-bound denial of service in applications that ingest attacker-controlled object keys into Immutable structures, e.g. `Immutable.Map(req.body)`, `Immutable.fromJS(req.body)`, `state.merge(userObject)` / `mergeDeep(...)`. Applications that only store attacker input as values under fixed keys are not affected. Affected versions • >= 4.0.0-beta.1, = 5.0.0-beta.1, < 5.1.8 • < 3.8.4 Patches Fixed in `5.1.8` _(adjust to the actual release)_: large collision buckets are indexed by a per-process seeded secondary hash, restoring near-linear behavior for the affected paths. The public `hash()` is unchanged (no breaking change), and `is()` remains the sole authority on key equality. Also fixed in 4.3.9 and 3.8.4. Workarounds Before passing untrusted data to Immutable.js: cap request body size, limit object key count/length, and reject high-cardinality payloads; avoid building Maps directly from untrusted object keys. References • CWE-407 (Inefficient Algorithmic Complexity), CWE-400 (Uncontrolled Resource Consumption) • OWASP API4:2023 (Unrestricted Resource Consumption)

Related

Alias · CVE (1)

  • CVE-2026-59880 Immutable.js provides many Persistent Immutable data structures. vulnerability

Weakness · CWE (2)

  • CWE-400 Uncontrolled Resource Consumption weakness
  • CWE-407 Inefficient Algorithmic Complexity weakness

References