From bd9ce08c01689efc7ec3d29fbca2da0bc4f363ee Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 27 Mar 2026 16:46:31 +0000 Subject: [PATCH 1/2] fix: prevent prototype pollution in node-cache mget() Use Object.create(null) for mget() result objects in both NodeCache and NodeCacheStore so that __proto__ keys are treated as plain properties instead of polluting Object.prototype. Fixes #1612 https://claude.ai/code/session_01MhMFGu517ERhcLM4M5DsM8 --- packages/node-cache/src/index.ts | 5 ++++- packages/node-cache/src/store.ts | 5 ++++- packages/node-cache/test/index.test.ts | 8 ++++++++ packages/node-cache/test/store.test.ts | 8 ++++++++ 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/packages/node-cache/src/index.ts b/packages/node-cache/src/index.ts index d52aeded..13be31b1 100644 --- a/packages/node-cache/src/index.ts +++ b/packages/node-cache/src/index.ts @@ -267,7 +267,10 @@ export class NodeCache extends Hookified { public mget( keys: Array, ): Record { - const result: Record = {}; + const result: Record = Object.create(null) as Record< + string, + V | undefined + >; for (const key of keys) { const value = this.get(key); diff --git a/packages/node-cache/src/store.ts b/packages/node-cache/src/store.ts index 93ea4736..7e2e1600 100644 --- a/packages/node-cache/src/store.ts +++ b/packages/node-cache/src/store.ts @@ -118,7 +118,10 @@ export class NodeCacheStore extends Hookified { public async mget( keys: Array, ): Promise> { - const result: Record = {}; + const result: Record = Object.create(null) as Record< + string, + V | undefined + >; for (const key of keys) { const value = await this._keyv.get(key.toString()); if (value === undefined) { diff --git a/packages/node-cache/test/index.test.ts b/packages/node-cache/test/index.test.ts index 332dc124..621fcb92 100644 --- a/packages/node-cache/test/index.test.ts +++ b/packages/node-cache/test/index.test.ts @@ -93,6 +93,14 @@ describe("NodeCache", () => { expect(list[key2]).toBe(value2); }); + test("should not pollute Object.prototype via mget with __proto__ key", () => { + cache.set("__proto__", { polluted: true }); + const result = cache.mget(["__proto__"]); + // biome-ignore lint/suspicious/noExplicitAny: testing prototype pollution + expect((Object.prototype as any).polluted).toBeUndefined(); + expect(result.__proto__).toEqual({ polluted: true }); + }); + test("should take a key", () => { const key = faker.string.uuid(); const val = faker.lorem.word(); diff --git a/packages/node-cache/test/store.test.ts b/packages/node-cache/test/store.test.ts index 89318059..6c963df7 100644 --- a/packages/node-cache/test/store.test.ts +++ b/packages/node-cache/test/store.test.ts @@ -72,6 +72,14 @@ describe("NodeCacheStore", () => { const result1 = await store.mget(["test1", "test2"]); expect(result1).toEqual({ test1: "value1", test2: "value2" }); }); + test("should not pollute Object.prototype via mget with __proto__ key", async () => { + const store = new NodeCacheStore(); + await store.set("__proto__", { polluted: true }); + const result = await store.mget(["__proto__"]); + // biome-ignore lint/suspicious/noExplicitAny: testing prototype pollution + expect((Object.prototype as any).polluted).toBeUndefined(); + expect(result.__proto__).toEqual({ polluted: true }); + }); test("should be able to set multiple keys", async () => { const store = new NodeCacheStore(); const data = [ From 49adf8fc5a53b0971b7d9ffcbaa37691e9eff0de Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 27 Mar 2026 16:56:32 +0000 Subject: [PATCH 2/2] fix: strengthen prototype pollution tests with own-property assertions Replace result.__proto__ value check with Object.getPrototypeOf(result) === null and Object.hasOwn(result, "__proto__") assertions that actually verify the Object.create(null) fix rather than passing vacuously. https://claude.ai/code/session_01MhMFGu517ERhcLM4M5DsM8 --- packages/node-cache/test/index.test.ts | 3 ++- packages/node-cache/test/store.test.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/node-cache/test/index.test.ts b/packages/node-cache/test/index.test.ts index 621fcb92..de67af9e 100644 --- a/packages/node-cache/test/index.test.ts +++ b/packages/node-cache/test/index.test.ts @@ -98,7 +98,8 @@ describe("NodeCache", () => { const result = cache.mget(["__proto__"]); // biome-ignore lint/suspicious/noExplicitAny: testing prototype pollution expect((Object.prototype as any).polluted).toBeUndefined(); - expect(result.__proto__).toEqual({ polluted: true }); + expect(Object.getPrototypeOf(result)).toBeNull(); + expect(Object.hasOwn(result, "__proto__")).toBe(true); }); test("should take a key", () => { diff --git a/packages/node-cache/test/store.test.ts b/packages/node-cache/test/store.test.ts index 6c963df7..b37160ee 100644 --- a/packages/node-cache/test/store.test.ts +++ b/packages/node-cache/test/store.test.ts @@ -78,7 +78,8 @@ describe("NodeCacheStore", () => { const result = await store.mget(["__proto__"]); // biome-ignore lint/suspicious/noExplicitAny: testing prototype pollution expect((Object.prototype as any).polluted).toBeUndefined(); - expect(result.__proto__).toEqual({ polluted: true }); + expect(Object.getPrototypeOf(result)).toBeNull(); + expect(Object.hasOwn(result, "__proto__")).toBe(true); }); test("should be able to set multiple keys", async () => { const store = new NodeCacheStore();