fix: round-trip __proto__ keys as own properties (#159)#192
Open
spokodev wants to merge 1 commit into
Open
Conversation
unpack renamed a `__proto__` object key to `__proto_` to avoid prototype
pollution, which silently corrupted the key and dropped its value
(`unpack(pack({['__proto__']: 1}))` came back with a `__proto_` key).
As suggested in kriszyp#159, `Object.defineProperty` sets it as a real own
property without touching the prototype — matching how `JSON.parse` and
`structuredClone` handle the same key.
Applied to every object-reading path: the two map-as-object readers, the
record structure reader, and its optimized (compiled) variant, which now
emits a computed `["__proto__"]` key instead of the prototype setter.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the
__proto__part of #159.Problem
A
__proto__object key does not survive a round-trip — it is silently renamed to__proto_and its value is misplaced:unpackrewrites the key to__proto_(in four places) to avoid prototype pollution fromobject['__proto__'] = …. It avoids pollution, but at the cost of corrupting the data.Fix
Per the suggestion in #159, set the key with
Object.defineProperty, which creates a genuine own__proto__property without invoking the prototype setter — the same resultJSON.parseandstructuredCloneproduce. A smallsetObjectKeyhelper handles the three manual readers (the twomapsAsObjectspaths and the record structure reader); the optimized compiled reader emits a computed["__proto__"]key (which is an own property, not the setter), so the fast path is preserved rather than disabled.Verification
Round-tripping
__proto__now preserves it as an own property across every reader — the record path, the optimized record path (after the read threshold), themapsAsObjects/map readers, and the default top-levelunpack— and the prototype is never mutated (({}).pollutedstaysundefinedeven when the value is an object). A test covering these paths is added totests/test.js.