VYPR
High severityNVD Advisory· Published Jan 19, 2021· Updated Sep 17, 2024

Prototype Pollution

CVE-2020-28477

Description

This affects all versions of package immer.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Immer prior to 8.0.1 is vulnerable to prototype pollution via crafted patch operations, allowing attackers to inject properties into Object.prototype.

Vulnerability

Immer, a popular JavaScript library for immutable state management, is susceptible to a prototype pollution vulnerability in all versions before 8.0.1 [1][2]. The bug resides in the applyPatches function, which does not sufficiently sanitize special path elements like __proto__, prototype, or constructor when applying patches to objects [1][3]. An attacker can craft a patch with a path such as ["__proto__", "polluted"], causing the library to modify Object.prototype and pollute all objects in the runtime [1][4].

Exploitation

To exploit this issue, an attacker needs to control the patches passed to applyPatches. This is possible in applications that accept patch data from untrusted sources, such as collaborative editing environments or real-time sync features [2][4]. The attack requires no authentication beyond the ability to supply crafted patch objects. By providing a patch operation with the "add" op and a path traversing __proto__ or prototype, the attacker can inject arbitrary properties into the global object prototype [1][3].

Impact

Successful prototype pollution can lead to severe consequences, including denial of service (e.g., triggering JavaScript exceptions) or, in many frameworks, remote code execution by hijacking property lookups that control application logic [4]. Patches targeting __proto__ or prototype can silently alter the behavior of all objects, potentially compromising authentication, authorization, or data flow [2][4].

Mitigation

The fix was released in Immer version 8.0.1, which introduces validation that rejects patches using reserved attributes like __proto__, prototype, and constructor [1][3]. Users are strongly advised to upgrade to at least 8.0.1 [2]. For applications that cannot upgrade immediately, any code path that passes external patch data to applyPatches should be reviewed and restricted to trusted sources only.

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
immernpm
>= 7.0.0, < 8.0.18.0.1

Affected products

32

Patches

1
da2bd4fa0edc

fix: Fixed security issue #738: prototype pollution possible when applying patches CVE-2020-28477

https://github.com/immerjs/immerMichel WeststrateJan 20, 2021via ghsa
3 files changed · +123 3
  • src/plugins/patches.ts+12 2 modified
    @@ -26,7 +26,8 @@ import {
     	ArchtypeArray,
     	die,
     	isDraft,
    -	isDraftable
    +	isDraftable,
    +	ArchtypeObject
     } from "../internal"
     
     export function enablePatches() {
    @@ -211,7 +212,16 @@ export function enablePatches() {
     
     			let base: any = draft
     			for (let i = 0; i < path.length - 1; i++) {
    -				base = get(base, path[i])
    +				const parentType = getArchtype(base)
    +				const p = path[i]
    +				// See #738, avoid prototype pollution
    +				if (
    +					(parentType === ArchtypeObject || parentType === ArchtypeArray) &&
    +					(p === "__proto__" || p === "constructor")
    +				)
    +					die(24)
    +				if (typeof base === "function" && p === "prototype") die(24)
    +				base = get(base, p)
     				if (typeof base !== "object") die(15, path.join("/"))
     			}
     
    
  • src/utils/errors.ts+2 1 modified
    @@ -38,7 +38,8 @@ const errors = {
     	},
     	23(thing: string) {
     		return `'original' expects a draft, got: ${thing}`
    -	}
    +	},
    +	24: "Patching reserved attributes like __proto__, prototype and constructor is not allowed"
     } as const
     
     export function die(error: keyof typeof errors, ...args: any[]): never {
    
  • __tests__/patch.js+109 0 modified
    @@ -12,6 +12,8 @@ enableAllPlugins()
     
     jest.setTimeout(1000)
     
    +const isProd = process.env.NODE_ENV === "production"
    +
     function runPatchTest(base, producer, patches, inversePathes) {
     	let resultProxies, resultEs5
     
    @@ -1147,3 +1149,110 @@ test("#676 patching Date objects", () => {
     	)
     	expect(rebuilt.date).toEqual(new Date("2020-11-10T08:08:08.003Z"))
     })
    +
    +test("do not allow __proto__ polution - 738", () => {
    +	const obj = {}
    +
    +	// @ts-ignore
    +	expect(obj.polluted).toBe(undefined)
    +	expect(() => {
    +		applyPatches({}, [
    +			{op: "add", path: ["__proto__", "polluted"], value: "yes"}
    +		])
    +	}).toThrow(
    +		isProd
    +			? "24"
    +			: "Patching reserved attributes like __proto__, prototype and constructor is not allowed"
    +	)
    +	// @ts-ignore
    +	expect(obj.polluted).toBe(undefined)
    +})
    +
    +test("do not allow __proto__ polution using arrays - 738", () => {
    +	const obj = {}
    +	const ar = []
    +
    +	// @ts-ignore
    +	expect(obj.polluted).toBe(undefined)
    +	// @ts-ignore
    +	expect(ar.polluted).toBe(undefined)
    +	expect(() => {
    +		applyPatches(
    +			[],
    +			[{op: "add", path: ["__proto__", "polluted"], value: "yes"}]
    +		)
    +	}).toThrow(
    +		isProd
    +			? "24"
    +			: "Patching reserved attributes like __proto__, prototype and constructor is not allowed"
    +	)
    +	// @ts-ignore
    +	expect(obj.polluted).toBe(undefined)
    +	// @ts-ignore
    +	expect(ar.polluted).toBe(undefined)
    +})
    +
    +test("do not allow prototype polution - 738", () => {
    +	const obj = {}
    +
    +	// @ts-ignore
    +	expect(obj.polluted).toBe(undefined)
    +	expect(() => {
    +		applyPatches(Object, [
    +			{op: "add", path: ["prototype", "polluted"], value: "yes"}
    +		])
    +	}).toThrow(
    +		isProd
    +			? "24"
    +			: "Patching reserved attributes like __proto__, prototype and constructor is not allowed"
    +	)
    +	// @ts-ignore
    +	expect(obj.polluted).toBe(undefined)
    +})
    +
    +test("do not allow constructor polution - 738", () => {
    +	const obj = {}
    +
    +	// @ts-ignore
    +	expect(obj.polluted).toBe(undefined)
    +	const t = {}
    +	applyPatches(t, [{op: "replace", path: ["constructor"], value: "yes"}])
    +	expect(typeof t.constructor).toBe("function")
    +	// @ts-ignore
    +	expect(Object.polluted).toBe(undefined)
    +})
    +
    +test("do not allow constructor.prototype polution - 738", () => {
    +	const obj = {}
    +
    +	// @ts-ignore
    +	expect(obj.polluted).toBe(undefined)
    +	expect(() => {
    +		applyPatches({}, [
    +			{op: "add", path: ["constructor", "prototype", "polluted"], value: "yes"}
    +		])
    +	}).toThrow(
    +		isProd
    +			? "24"
    +			: "Patching reserved attributes like __proto__, prototype and constructor is not allowed"
    +	)
    +	// @ts-ignore
    +	expect(Object.polluted).toBe(undefined)
    +})
    +
    +test("maps can store __proto__, prototype and constructor props", () => {
    +	const obj = {}
    +	const map = new Map()
    +	map.set("__proto__", {})
    +	map.set("constructor", {})
    +	map.set("prototype", {})
    +	const newMap = applyPatches(map, [
    +		{op: "add", path: ["__proto__", "polluted"], value: "yes"},
    +		{op: "add", path: ["constructor", "polluted"], value: "yes"},
    +		{op: "add", path: ["prototype", "polluted"], value: "yes"}
    +	])
    +	expect(newMap.get("__proto__").polluted).toBe("yes")
    +	expect(newMap.get("constructor").polluted).toBe("yes")
    +	expect(newMap.get("prototype").polluted).toBe("yes")
    +	expect(obj.polluted).toBe(undefined)
    +})
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

8

News mentions

0

No linked articles in our index yet.