Safeguard
Open Source

immer npm: A Security Review and Safe Usage Guide

The immer npm package makes immutable state updates painless, but its older versions carried prototype pollution flaws. Here is its security history and how to depend on it safely.

Yukti Singhal
Platform Engineer
6 min read

The immer npm package is a small, widely trusted library for writing immutable state updates in a mutable style, and it is safe to use today provided you are on version 9.0.6 or later, because its earlier releases carried a chain of prototype pollution vulnerabilities. If you install immer npm, the version number is the whole ballgame: the library's own code is well designed, but three related CVEs in the 8.x era show why staying current is not optional. This review walks through what immer does, the specific flaws that were found and fixed, and the practices that keep it safe in production.

Immer powers immutable updates behind the scenes in a lot of React and Redux applications. You write what looks like ordinary mutation against a draft object, and immer produces a new immutable state without you hand-rolling spread operators. That ergonomic win made it enormously popular, which is exactly why its vulnerability history was worth paying attention to.

What immer does and why it is everywhere

The core idea is the "draft." You call produce, mutate the draft freely inside the callback, and immer hands back a new state where nothing you did leaked into the original:

import { produce } from "immer";

const nextState = produce(baseState, (draft) => {
  draft.user.name = "Ada";
  draft.items.push({ id: 3 });
});
// baseState is untouched; nextState is a new immutable object

Redux Toolkit uses immer internally, which means a huge number of applications ship immer transitively without ever adding it to their own package.json. Run npm ls immer and you will likely find it resolved through @reduxjs/toolkit or a UI framework. That transitive presence is the first reason to care about its version: you may be exposed to an old copy you did not knowingly install.

The prototype pollution chain in immer's history

Immer's security issues all revolve around prototype pollution, where crafted input manipulates the __proto__ chain to inject properties onto base object prototypes, potentially altering application behavior in ways an attacker can exploit. The interesting part is that immer had to fix the same underlying class of bug more than once, because each fix was bypassed by a cleverer input shape.

CVE-2020-28477 was the original prototype pollution flaw, in the patch-application path. It was addressed in the 8.0.1 release line.

CVE-2021-3757 was a follow-up that bypassed the first fix by wrapping path elements in arrays, using nested paths like a __proto__ element inside an array to slip past the earlier guard.

CVE-2021-23436 was a type confusion issue that provided another route around CVE-2020-28477 when user-provided path keys were arrays. This one was fixed in immer 9.0.6.

The clean summary: anything before 9.0.6 has known prototype pollution exposure, and 9.0.6 is the floor. Modern immer is well past that on the 10.x line, so in practice you want to be on the latest 10.x release, and any pin below 9.0.6 should be treated as a finding.

How do you use immer safely?

Version currency handles the known CVEs. A few usage habits handle the rest.

Do not feed untrusted, deeply-structured input into immer's patch APIs. The vulnerabilities all lived in path-based patch application, where an attacker who controls the path array can attempt prototype traversal. If you use applyPatches with patches derived from user input, validate and sanitize the path structure first, and never accept raw __proto__, constructor, or prototype keys in a path.

Freeze the prototype as defense in depth. Regardless of immer's version, calling Object.freeze(Object.prototype) at startup blocks the entire prototype pollution class from mutating the base prototype:

Object.freeze(Object.prototype);

Lean on immer's own auto-freeze in development. Immer freezes produced state by default in development builds, which surfaces accidental mutations early. Keep that on; it catches bugs that would otherwise become subtle production issues.

Keeping immer patched, especially transitively

Because immer is so often a transitive dependency, the operational challenge is noticing when a copy buried in your tree falls behind.

Audit the whole tree. npm audit flags known advisories against resolved versions, and a dedicated software composition analysis scan resolves transitive copies and tells you which parent, usually Redux Toolkit or a component library, is holding you on an old immer. A tool such as Safeguard can surface that transitive exposure even when immer never appears in your own manifest.

Force upgrades with overrides when needed. If a parent package pins an old immer and has not shipped an update, npm's overrides field lets you force a safe version:

{
  "overrides": {
    "immer": "10.1.1"
  }
}

Test after overriding, since you are running the parent against an immer version it may not have tested with. Immer has been careful about backward compatibility within major lines, but the patch-API surface is exactly the area that changed to fix these CVEs, so exercise any code that applies patches.

Is immer a safe dependency?

Yes, with the version caveat. Immer is small, has no runtime dependencies of its own, and is actively maintained by a well-known author. Its vulnerability history is real, and the fact that the same bug class recurred is a useful reminder that prototype pollution is hard to stamp out completely, but every issue discussed here has a fix, and current releases have been clean. The risk is not the library in the abstract; it is an old transitive copy you did not know you were shipping. Find it, upgrade it, and immer earns its place.

FAQ

Is the immer npm package safe to use?

Yes, on version 9.0.6 or later, and ideally the latest 10.x release. Immer is actively maintained and has no runtime dependencies. Versions before 9.0.6 carry known prototype pollution vulnerabilities and should be upgraded.

Which immer versions had security vulnerabilities?

Immer had a chain of prototype pollution and type confusion issues: CVE-2020-28477 (fixed in the 8.0.1 line), CVE-2021-3757 (a bypass of the first fix), and CVE-2021-23436, a type confusion issue fixed in 9.0.6. Anything before 9.0.6 is exposed.

I never installed immer directly. Why is it in my project?

Immer is a transitive dependency of Redux Toolkit and many UI libraries, which use it internally for immutable updates. Run npm ls immer to see how it is pulled in. This is why an outdated transitive copy is easy to miss.

How do I upgrade a transitive immer that a parent package pins?

Use npm's overrides field in package.json to force a safe version across the tree, then test any code that uses immer's patch APIs, since that surface changed to fix the prototype pollution issues.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.