{"id":"GHSA-xvcm-6775-5m9r","summary":"Immutable: Hash-collision algorithmic complexity denial of service in Immutable.Map/Set","details":"## Summary\n\n`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.\n\n## Details\n\nThe 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 ⇒ \u003e1,000,000 colliding keys). \nAll 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.\n\n## Proof of concept\n\nInserting N colliding keys (e.g. via `Immutable.Map(obj)` / `Immutable.fromJS(obj)`) is O(N²). Measured on one machine, ~8,000 colliding\nkeys take ~0.7 s to build and ~0.6 s to read, scaling ×4 per doubling; ~16,000 keys exceed several seconds.\n\n## Impact\n\nCPU-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.\n\n## Affected versions\n\n- \u003e= 4.0.0-beta.1, \u003c 4.3.9\n- \u003e= 5.0.0-beta.1, \u003c 5.1.8\n- \u003c 3.8.4\n\n## Patches\n\nFixed 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.\n\n## Workarounds\n\nBefore 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.\n\n## References\n\n- CWE-407 (Inefficient Algorithmic Complexity), CWE-400 (Uncontrolled Resource Consumption)\n- OWASP API4:2023 (Unrestricted Resource Consumption)","aliases":["CVE-2026-59880"],"modified":"2026-09-03T16:45:04.062388437Z","published":"2026-07-21T19:02:33Z","database_specific":{"github_reviewed_at":"2026-07-21T19:02:33Z","nvd_published_at":"2026-07-08T16:16:34Z","cwe_ids":["CWE-400","CWE-407"],"severity":"HIGH","github_reviewed":true},"references":[{"type":"WEB","url":"https://github.com/immutable-js/immutable-js/security/advisories/GHSA-xvcm-6775-5m9r"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-59880"},{"type":"WEB","url":"https://github.com/immutable-js/immutable-js/commit/3dd7e5655012597a41873e328bf9142a8901527b"},{"type":"WEB","url":"https://github.com/immutable-js/immutable-js/commit/e51d49fc612ded5ec4dfb94ff294d22074269b0f"},{"type":"PACKAGE","url":"https://github.com/immutable-js/immutable-js"},{"type":"WEB","url":"https://github.com/immutable-js/immutable-js/releases/tag/v3.8.4"},{"type":"WEB","url":"https://github.com/immutable-js/immutable-js/releases/tag/v4.3.9"},{"type":"WEB","url":"https://github.com/immutable-js/immutable-js/releases/tag/v5.1.8"}],"affected":[{"package":{"name":"immutable","ecosystem":"npm","purl":"pkg:npm/immutable"},"ranges":[{"type":"SEMVER","events":[{"introduced":"4.0.0-beta.1"},{"fixed":"4.3.9"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/07/GHSA-xvcm-6775-5m9r/GHSA-xvcm-6775-5m9r.json"}},{"package":{"name":"immutable","ecosystem":"npm","purl":"pkg:npm/immutable"},"ranges":[{"type":"SEMVER","events":[{"introduced":"5.0.0-beta.1"},{"fixed":"5.1.8"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/07/GHSA-xvcm-6775-5m9r/GHSA-xvcm-6775-5m9r.json"}},{"package":{"name":"immutable","ecosystem":"npm","purl":"pkg:npm/immutable"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"3.8.4"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/07/GHSA-xvcm-6775-5m9r/GHSA-xvcm-6775-5m9r.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V4","score":"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"}]}