From efd8fbe688e495db90602d7990ef371ad61a13f5 Mon Sep 17 00:00:00 2001 From: Yarchik Date: Wed, 17 Jun 2026 16:06:22 +0100 Subject: [PATCH] fix: keep `__proto__` keys as own properties `__proto__` as an identifier or string key in an object literal is the prototype setter, not an own property. So an object with an own `__proto__` key serialized to `{__proto__:value}`, and `eval` applied it as the prototype and dropped the entry. Emit `__proto__` as a computed key (`{['__proto__']:value}`) so it round-trips as an own property. --- src/index.spec.ts | 5 +++++ src/quote.ts | 3 +++ 2 files changed, 8 insertions(+) diff --git a/src/index.spec.ts b/src/index.spec.ts index e03d411..44e77c7 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -134,6 +134,11 @@ describe("javascript-stringify", () => { "should not quote Object.prototype keys", test({ constructor: 1, toString: 2 }, "{constructor:1,toString:2}"), ); + + it( + "should keep `__proto__` keys as own properties", + testRoundTrip("{['__proto__']:42}"), + ); }); describe("functions", () => { diff --git a/src/quote.ts b/src/quote.ts index 225b158..71f955a 100644 --- a/src/quote.ts +++ b/src/quote.ts @@ -77,6 +77,9 @@ export function isValidVariableName(name: PropertyKey): name is string { * Quote JavaScript key access. */ export function quoteKey(key: PropertyKey, next: Next) { + // `__proto__` as an identifier or string key is the prototype setter rather + // than an own property; emit it as a computed key so it round-trips. + if (key === "__proto__") return `[${next(key)}]`; return isValidVariableName(key) ? key : next(key); }