Update dependency immutable to v3.8.4 [SECURITY] - #32
renovate[bot] wants to merge 1 commit into
Conversation
|
Skipping PR review because a bot author is detected. If you want to trigger CodeAnt AI, comment |
There was a problem hiding this comment.
AI Code Review by LlamaPReview
🎯 TL;DR & Recommendation
Recommendation: Request Changes
This PR patches a critical Prototype Pollution vulnerability but introduces major API breaking changes that could break existing example code without verification.
🌟 Strengths
- Fixes a severe security vulnerability (CVE-2026-29063).
| Priority | File | Category | Impact Summary | Anchors |
|---|---|---|---|---|
| P0 | package.json | Security | Patches critical Prototype Pollution vulnerability (CVE-2026-29063). | |
| P1 | package.json | Architecture | Major API breaking changes could invalidate educational examples. | symbol:toJS, path:examples/07-semigroups-examples.js |
| P2 | package-lock.json | Maintainability | Standard dependency lockfile update; no functional impact. | |
| P2 | N/A | Testing | Speculative risk of regressions; verification needed. | symbol:toJS, path:examples/06-semigroups-types.js |
🔍 Notable Themes
- Security vs. Stability Trade-off: The upgrade is necessary for security but risks breaking existing functionality due to API changes.
- Lack of Test Coverage: No functional tests are run to verify that examples still work correctly with the new version.
📈 Risk Diagram
This diagram illustrates the risk of API breaking changes affecting example code execution.
sequenceDiagram
participant EC as Example Code
participant IV as Immutable Library v4
EC->>IV: Call toJS() or other API
note over IV: R1(P1): Breaking API changes may cause incorrect behavior
IV-->>EC: Potentially unexpected output
⚠️ **Unanchored Suggestions (Manual Review Recommended)**
The following suggestions could not be precisely anchored to a specific line in the diff. This can happen if the code is outside the changed lines, has been significantly refactored, or if the suggestion is a general observation. Please review them carefully in the context of the full file.
📁 File: package-lock.json
The package-lock.json has been updated as part of the dependency version bump. This is a standard and necessary change to lock the transitive dependencies to versions compatible with immutable@^4.0.0. No issues are introduced by this change itself.
Related Code:
📁 File: N/A
Speculative: While the primary security fix is applied, the major version upgrade introduces a significant risk of regressions due to API changes. Without a test suite or verification, we cannot be confident the educational content remains valid.
Related Code:
N/A
💡 Have feedback? We'd love to hear it in our GitHub Discussions.
✨ This review was generated by LlamaPReview Advanced, which is free for all open-source projects. Learn more.
| "creed": "^1.4.0", | ||
| "data.either": "^1.4.0", | ||
| "data.task": "^3.1.2", | ||
| "fantasy-identities": "0.0.1", |
There was a problem hiding this comment.
[Contextual Comment]
This comment refers to code near real line 27. Anchored to nearest_changed(31) line 31.
P0 | Confidence: High
- Security: The change patches CVE-2026-29063, fixing a critical Prototype Pollution vulnerability. - Architecture: Upgrading to immutable v4 introduces numerous breaking API changes that could cause the example code to behave unexpectedly or fail.
d5d3939 to
469fbf9
Compare
469fbf9 to
f5ab799
Compare
f5ab799 to
0efcb27
Compare
0efcb27 to
aebe3fc
Compare
aebe3fc to
4b288ee
Compare
|
Important Review skippedBot user detected. To trigger a single review, invoke the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This PR contains the following updates:
3.8.2→3.8.4Immutable is vulnerable to Prototype Pollution
CVE-2026-29063 / GHSA-wf6x-7x77-mvgw
More information
Details
Impact
What kind of vulnerability is it? Who is impacted?
A Prototype Pollution is possible in immutable via the mergeDeep(), mergeDeepWith(), merge(), Map.toJS(), and Map.toObject() APIs.
Affected APIs
mergeDeep(target, source)ObjectSeq, assignsmerged[key]mergeDeepWith(merger, target, source)merge(target, source)Map.toJS()object[k] = vintoObject()with no__proto__guardMap.toObject()toObject()implementationMap.mergeDeep(source)Patches
Has the problem been patched? What versions should users upgrade to?
Workarounds
Is there a way for users to fix or remediate the vulnerability without upgrading?
Proof of Concept
PoC 1 — mergeDeep privilege escalation
PoC 2 — All affected APIs
Verified output against immutable@5.1.4:
References
Are there any links users can visit to find out more?
Severity
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Immutable: Hash-collision algorithmic complexity denial of service in Immutable.Map/Set
CVE-2026-59880 / GHSA-xvcm-6775-5m9r
More information
Details
Summary
Immutable.MapandImmutable.Setkeep 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 yields2^ndistinct strings sharing one hash (40 characters ⇒ >1,000,000 colliding keys).All such keys route to a single
HashCollisionNode, whoseget/updatewalk the entire bucket testingis(). 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 collidingkeys 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
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 publichash()is unchanged (no breaking change), andis()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
Severity
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Immutable.js
List32-bit trie overflow → unrecoverable DoSCVE-2026-59879 / GHSA-v56q-mh7h-f735
More information
Details
Summary
List#set,List#setSize,List#setIn,List#updateIn(and the functionalset/setIn/updateIn) mishandle an index or size in the range[2 ** 30, 2 ** 31):Listthe operation enters an uncatchable infinite loop (a tight CPU spin; a surroundingtry/catchnever regains control). Only killing the worker recovers it.List(≥ 32 elements — i.e. any array of ≥ 32 items turned into aListbyfromJS) the loop allocates without bound → heap exhaustion → the process aborts (SIGABRT, exit134, or kernel OOM-kill137). A real crash, not a recoverable error.The index may be a numeric string, so it can come straight from a request body, URL, or key-path. A single small unauthenticated request is enough.
There is also a companion silent data-corruption issue in
setSize:Impact
Availability only. A reachable configuration is any endpoint that routes untrusted input into a
Listindex or asetIn/updateInkey-path — which the extremely commonstate = fromJS(body); state.setIn(userPath, value)pattern does (config stores, document/collection editors, redux-immutable reducers, JSON-Patch endpoints, etc.).No confidentiality or integrity impact, no RCE. The companion
setSizebug can silently corrupt application state (wrong size) without crashing.Reproduction (immutable 5.1.7)
A remote 43-byte HTTP request (
{"path":["items","1073741824"],"value":"x"}) is sufficient to abort a worker that applies it viastate = state.setIn(path, value).Any index in
[2 ** 30, 2 ** 31)works (1073741824,2000000000, …). An index in[2 ** 31, 2 ** 32)does not crash — it silently wraps (clearing the List) via the same root cause.Root cause
Liststores its values in a 32-wide trie (SHIFT = 5, so each level addresses 5 more bits) and uses signed 32-bit bitwise arithmetic throughoutsetListBounds()(src/List.js):relies on
1 << (newLevel + SHIFT). A JavaScript shift count is taken mod 32, so oncenewLevel + SHIFTreaches31the term goes negative (1 << 31 === -2147483648) and at32wraps to1(1 << 35 === 8). The comparison then staystrueforever and the loop never terminates. On a populatedList, each iteration retains a newVNode([newRoot]), so the heap fills and V8 aborts; on an emptyListit spins on CPU without allocating.setSizecorruption). Thebegin |= 0/end |= 0coercion (ToInt32) silently wraps large finite values ((2 ** 31) | 0 === -2147483648,(2 ** 32 + 5) | 0 === 5), producing a wrong resulting size instead of an error.The threshold is
2 ** 30: that is the largest size for which1 << (newLevel + SHIFT)stays a valid positive 32-bit integer throughout the loops (newLevel + SHIFTstays ≤ 30).Remediation
The fix is contained to
setListBounds()insrc/List.js:Validate up front, before the lossy
| 0coercion. Compute the intended origin and capacity in full precision and throw a clear, catchableRangeErrorwhen they exceed the addressable range (MAX_LIST_SIZE = 2 ** 30).Infinity/NaNare left to the existing| 0 → 0behaviour (sosetSize(Infinity)stays0andslice(0, Infinity)still means "to the end").Stop the shift from wrapping. Replace
1 << expin the level-raising loops with a helper that uses the cheap bitwise shift while it is exact (exp ≤ 30, the common path including everypush/setSize/slice) and falls back to the non-wrapping2 ** exponly for the rare deep trees reached when a negative origin (unshift/ negative index) is normalized to a large positive capacity (expcan reach 35 there, where1 << 35would wrap to 8).This turns every hang, the misleading
"Maximum call stack size exceeded", the OOM/SIGABRT, and the silentsetSizetruncation into one descriptiveRangeError, preserves all behaviour for sizes< 2 ** 30, and keeps the hotpushpath on the fast bitwise shift (the2 ** expbranch is never reached by non-negative operations).Is the new limit a breaking change?
No working code is affected. A
Listcould never actually hold≥ 2 ** 30values before — the attempt hung, crashed, or silently corrupted the size. The limit was already implicit in the 32-bit trie; the fix only makes it explicit and catchable, mirroring native JS arrays (new Array(2 ** 32)→RangeError: Invalid array length). The single observable behaviour change is thatsetSize(hugeValue), which used to return a silently wrong size, now throws.2 ** 30≈ 1.07 billion entries (~8 GB of pointers alone), far beyond any practical use.Mitigations (for users who cannot upgrade immediately)
Listindex orsetIn/updateInkey-path segment against a sane maximum before passing it to immutable.≥ 2 ** 30.--max-old-space-size) so an abort is contained.Severity
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Release Notes
immutable-js/immutable-js (immutable)
v3.8.4Compare Source
What's Changed
New Contributors
Full Changelog: immutable-js/immutable-js@v3.8.3...v3.8.4
v3.8.3Compare Source
Fix Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution') in immutable
Configuration
📅 Schedule: (UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.