diff --git a/src/index.ts b/src/index.ts index ea6977d..bd2c7a2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -106,7 +106,16 @@ const _parseJSON = (jsonString: string, allow: number) => { index++; // skip colon try { const value = parseAny(); - obj[key] = value; + if (key === "__proto__") { + Object.defineProperty(obj, key, { + value, + writable: true, + enumerable: true, + configurable: true, + }); + } else { + obj[key] = value; + } } catch (e) { if (Allow.OBJ & allow) return obj; else throw e; diff --git a/tests/examples.test.js b/tests/examples.test.js index 86c7c02..b7bec45 100644 --- a/tests/examples.test.js +++ b/tests/examples.test.js @@ -22,6 +22,32 @@ test("obj", () => { expect(parse('{"": "', OBJ)).toEqual({}); expect(parse('{"": "', OBJ | STR)).toEqual({ "": "" }); + const protoValue = { polluted: true }; + const parsed = parse('{"__proto__":{"polluted":true},"safe":1}'); + expect(Object.prototype.hasOwnProperty.call(parsed, "__proto__")).toBe(true); + expect(parsed.__proto__).toEqual(protoValue); + expect(parsed.safe).toBe(1); + expect(Object.prototype.polluted).toBeUndefined(); + + const parsedPrimitive = parse('{"__proto__":1,"safe":1}'); + expect(Object.prototype.hasOwnProperty.call(parsedPrimitive, "__proto__")).toBe(true); + expect(parsedPrimitive.__proto__).toBe(1); + expect(parsedPrimitive.safe).toBe(1); + expect(Object.prototype.polluted).toBeUndefined(); + + const nested = parse('{"child":{"__proto__":{"polluted":true}},"items":[{"__proto__":1}]}'); + expect(Object.prototype.hasOwnProperty.call(nested.child, "__proto__")).toBe(true); + expect(Object.prototype.hasOwnProperty.call(nested.items[0], "__proto__")).toBe(true); + expect(nested.child.__proto__).toEqual(protoValue); + expect(nested.items[0].__proto__).toBe(1); + expect(Object.prototype.polluted).toBeUndefined(); + + const parsedWithFlags = parse('{"__proto__":{"polluted":true},"safe":1}', OBJ | STR); + expect(Object.prototype.hasOwnProperty.call(parsedWithFlags, "__proto__")).toBe(true); + expect(parsedWithFlags.__proto__).toEqual(protoValue); + expect(parsedWithFlags.safe).toBe(1); + expect(Object.prototype.polluted).toBeUndefined(); + expect(() => parse("{", STR)).toThrow(PartialJSON); expect(() => parse('{"', STR)).toThrow(PartialJSON); expect(() => parse('{""', STR)).toThrow(PartialJSON);