CVE-2021-23436: Access of Resource Using Incompatible Type ('Type Confusion') (CWE-843)
value: This affects the package immer before 9.0.6. A type confusion vulnerability can lead to a bypass of CVE-2020-28477 when the user-provided keys used in the path parameter are arrays. In particular, this bypass is possible because the condition (p === "__proto__" || p === "constructor") in applyPatches_ returns false if p is ['__proto__'] (or ['constructor']). The === operator (strict equality operator) returns false if the operands have different type.
CWE description: The product allocates or initializes a resource such as a pointer, object, or variable using one type, but it later accesses that resource using a type that is incompatible with the original type.
CVSS v3 base score: 9.8
Published: 2021-09-01T18:15Z
Repository: https://github.com/immerjs/immer
Language: JavaScript
Files changed: __tests__/patch.js, src/plugins/patches.ts
Commit date: 2021-08-31 19:59:35 +0100
Commit message: fix(security): Follow up on CVE-2020-28477 where `path: [["__proto__"], "x"]` could still pollute the prototype

Diff: diff --git a/__tests__/patch.js b/__tests__/patch.js
index 1885094..8bacae1 100644
--- a/__tests__/patch.js
+++ b/__tests__/patch.js
@@ -1256,10 +1256,28 @@ test("maps can store __proto__, prototype and constructor props", () => {
 	expect(newMap.get("constructor").polluted).toBe("yes")
 	expect(newMap.get("prototype").polluted).toBe("yes")
 	expect(obj.polluted).toBe(undefined)
 })
 
+test("CVE-2020-28477 (https://snyk.io/vuln/SNYK-JS-IMMER-1019369) follow up", () => {
+	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("#648 assigning object to itself should not change patches", () => {
 	const input = {
 		obj: {
 			value: 200
 		}
diff --git a/src/plugins/patches.ts b/src/plugins/patches.ts
index e0cdc09..c4a7b4a 100644
--- a/src/plugins/patches.ts
+++ b/src/plugins/patches.ts
@@ -205,11 +205,11 @@ export function enablePatches() {
 			const {path, op} = patch
 
 			let base: any = draft
 			for (let i = 0; i < path.length - 1; i++) {
 				const parentType = getArchtype(base)
-				const p = path[i]
+				const p = "" + path[i]
 				// See #738, avoid prototype pollution
 				if (
 					(parentType === Archtype.Object || parentType === Archtype.Array) &&
 					(p === "__proto__" || p === "constructor")
 				)

Vulnerable code: 				const p = path[i]
Fixed code: test("CVE-2020-28477 (https://snyk.io/vuln/SNYK-JS-IMMER-1019369) follow up", () => {
	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)
})

				const p = "" + path[i]