From 72c849023ad2b89070693aec8b90a00619473d35 Mon Sep 17 00:00:00 2001 From: Zach Date: Tue, 20 Jan 2026 12:05:42 -0700 Subject: [PATCH] enhance prototype pollution protection by disallowing 'constructor' and 'prototype' as map keys --- src/Decoder.ts | 4 ++-- test/prototype-pollution.test.ts | 36 ++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/Decoder.ts b/src/Decoder.ts index bba8804..5b3c884 100644 --- a/src/Decoder.ts +++ b/src/Decoder.ts @@ -651,8 +651,8 @@ export class Decoder { continue DECODE; } } else if (state.type === STATE_MAP_KEY) { - if (object === "__proto__") { - throw new DecodeError("The key __proto__ is not allowed"); + if (object === "__proto__" || object === "constructor" || object === "prototype") { + throw new DecodeError(`The key ${object} is not allowed`); } state.key = this.mapKeyConverter(object); diff --git a/test/prototype-pollution.test.ts b/test/prototype-pollution.test.ts index 46e293e..29c1b53 100644 --- a/test/prototype-pollution.test.ts +++ b/test/prototype-pollution.test.ts @@ -19,4 +19,40 @@ describe("prototype pollution", () => { }, DecodeError); }); }); + + context("constructor exists as a map key", () => { + it("raises DecodeError in decoding", () => { + const o = { + foo: "bar", + }; + // override constructor as an enumerable property + Object.defineProperty(o, "constructor", { + value: new Date(0), + enumerable: true, + }); + const encoded = encode(o); + + throws(() => { + decode(encoded); + }, DecodeError); + }); + }); + + context("prototype exists as a map key", () => { + it("raises DecodeError in decoding", () => { + const o = { + foo: "bar", + }; + // override prototype as an enumerable property + Object.defineProperty(o, "prototype", { + value: new Date(0), + enumerable: true, + }); + const encoded = encode(o); + + throws(() => { + decode(encoded); + }, DecodeError); + }); + }); });