Skip to content

Fix diffJson silently dropping an own __proto__ key (#696) - #697

Open
youdie006 wants to merge 1 commit into
kpdecker:masterfrom
youdie006:fix/diffjson-proto-key
Open

Fix diffJson silently dropping an own __proto__ key (#696)#697
youdie006 wants to merge 1 commit into
kpdecker:masterfrom
youdie006:fix/diffjson-proto-key

Conversation

@youdie006

Copy link
Copy Markdown

Fixes #696.

Problem

diffJson reports two objects that differ only in an own __proto__ property as identical:

diffJson(JSON.parse('{"__proto__":"old"}'), JSON.parse('{"__proto__":"new"}'))
// => [ { count: 1, value: "{}", added: false, removed: false } ]   // no diff!

Root cause

canonicalize (src/diff/json.ts) builds its output object with {}. That object inherits Object.prototype, so canonicalizedObj["__proto__"] = value invokes the __proto__ setter instead of creating an own data property. The key is silently discarded, both sides canonicalize to {}, and diffJson sees no difference. Objects parsed from JSON (e.g. JSON.parse) can legitimately carry an own enumerable __proto__ property, so this is silent data loss.

Fix

Build the canonicalized object with Object.create(null), so a "__proto__" key is stored as an ordinary property. This is the approach suggested (unverified) by the reporter in #696; this PR verifies it and adds test coverage. canonicalize output is only ever JSON.stringify-d or key-walked, both of which work on null-prototype objects, and the existing #canonicalize tests (which read Object.keys(...)) still pass.

Test

Added a regression test in test/diff/json.js diffing two JSON-parsed objects with an own __proto__ property. Red-green verified: before the fix the assertion fails (single unchanged "{}" hunk); after, it produces the expected removed/added __proto__ lines. The full mocha suite passes (one unrelated pre-existing timeout on the enormous-hunk perf test in test/patch/create.js, which passes with a higher --timeout); eslint is clean.


Disclosure: prepared with AI assistance (Claude); I reviewed it and verified the red-green test and the full test suite.

canonicalize built its output with `{}`, so assigning a canonicalized
value to an own "__proto__" key invoked the Object.prototype __proto__
setter instead of creating a data property. The key was dropped, and
diffJson reported two objects that differ only in their __proto__
property as identical (a single unchanged "{}" hunk).

Build the canonicalized object with Object.create(null) so a "__proto__"
key is stored as an ordinary property. Add a regression test covering
diffJson over objects parsed from JSON with an own __proto__ property.

Fixes kpdecker#696

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 392ff6b95c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/diff/json.ts
// Use a null-prototype object so that an own "__proto__" key is stored as
// a normal property instead of triggering the Object.prototype setter,
// which would silently drop the key from the canonicalized output.
canonicalizedObj = Object.create(null);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve canonicalize's ordinary-object return type

canonicalize is re-exported from the package’s public entry point in src/index.ts, so returning a null-prototype object for every ordinary input is a breaking behavioral change unrelated to the __proto__ fix: consumers can no longer call inherited methods such as hasOwnProperty or toString, and prototype-sensitive equality checks now fail. Preserve the existing ordinary-object result and define the __proto__ property safely (for example, with Object.defineProperty) instead of changing every returned object's prototype.

Useful? React with 👍 / 👎.

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.

diffJson misbehaves when object has __proto__

1 participant