Skip to content

fix: keep __proto__ keys as own properties#47

Open
spokodev wants to merge 1 commit into
blakeembrey:mainfrom
spokodev:fix/proto-key-own-property
Open

fix: keep __proto__ keys as own properties#47
spokodev wants to merge 1 commit into
blakeembrey:mainfrom
spokodev:fix/proto-key-own-property

Conversation

@spokodev

Copy link
Copy Markdown

Problem

An own __proto__ property is dropped on a round-trip:

import { stringify } from "javascript-stringify";

const value = JSON.parse('{"__proto__": 42, "a": 1}'); // own enumerable __proto__
stringify(value); // "{__proto__:42,a:1}"

const back = eval("(" + stringify(value) + ")");
Object.getOwnPropertyDescriptor(back, "__proto__"); // undefined  ← lost

{ __proto__: 42 } in an object literal is the prototype setter, not an own property, so the entry disappears (and a __proto__ value that is an object even changes the result's prototype, which then breaks re-stringifying it).

Root cause

quoteKey emits __proto__ as a bare identifier (it is a valid identifier name), producing the prototype-setter form. A quoted string key ("__proto__") would behave the same way — only a computed key creates an own property.

Fix

Emit __proto__ as a computed key:

export function quoteKey(key: PropertyKey, next: Next) {
  if (key === "__proto__") return `[${next(key)}]`;
  return isValidVariableName(key) ? key : next(key);
}

{['__proto__']:42} is an own property and leaves the prototype untouched, matching JSON.parse/structuredClone. Other keys (including constructor, toString) are unaffected.

Test plan

  • Added a round-trip test for an own __proto__ key (fails on main, passes with the fix).
  • Full suite green (147).

`__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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant