Skip to content

fix: preserve __proto__ object keys#20

Open
rupayon123 wants to merge 4 commits into
promplate:mainfrom
rupayon123:fix/proto-key-parsing
Open

fix: preserve __proto__ object keys#20
rupayon123 wants to merge 4 commits into
promplate:mainfrom
rupayon123:fix/proto-key-parsing

Conversation

@rupayon123

Copy link
Copy Markdown

Fixes #8

This keeps "__proto__" as an own enumerable property instead of assigning through the prototype setter. I added a focused Vitest regression for parse('{"__proto__":0}').

I couldn’t run the suite locally here, so CI is the check.

@bolt-new-by-stackblitz

Copy link
Copy Markdown

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@vercel

vercel Bot commented Jun 16, 2026

Copy link
Copy Markdown

@rupayon123 is attempting to deploy a commit to the promplate Team on Vercel.

A member of the Team first needs to authorize it.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request updates the JSON parser to use Object.defineProperty instead of direct assignment when parsing object keys, ensuring that special keys like __proto__ are preserved as own enumerable properties. A test case was added to verify this behavior. The reviewer pointed out that using Object.defineProperty for every key introduces a significant performance overhead and suggested optimizing this by only using it when the key is exactly "__proto__".

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/index.ts Outdated
try {
const value = parseAny();
obj[key] = value;
Object.defineProperty(obj, key, { value, writable: true, enumerable: true, configurable: true });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Using Object.defineProperty for every single key in every parsed object introduces a significant performance overhead compared to direct property assignment (obj[key] = value). Since "proto" is the only standard accessor property on Object.prototype that behaves as a setter, we can optimize this by only using Object.defineProperty when the key is exactly "proto", and using direct assignment for all other keys.

if (key === "__proto__") {
                        Object.defineProperty(obj, key, { value, writable: true, enumerable: true, configurable: true });
                    } else {
                        obj[key] = value;
                    }

@sourcery-ai sourcery-ai 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.

Hey - I've found 1 issue, and left some high level feedback:

  • Using Object.defineProperty for every parsed key is a behavior and performance change compared to obj[key] = value; consider special-casing only the "__proto__" key (e.g., if (key === '__proto__') { defineProperty... } else { obj[key] = value; }) to minimize impact on the common path.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Using `Object.defineProperty` for every parsed key is a behavior and performance change compared to `obj[key] = value`; consider special-casing only the `"__proto__"` key (e.g., `if (key === '__proto__') { defineProperty... } else { obj[key] = value; }`) to minimize impact on the common path.

## Individual Comments

### Comment 1
<location path="src/index.ts" line_range="109" />
<code_context>
                 try {
                     const value = parseAny();
-                    obj[key] = value;
+                    Object.defineProperty(obj, key, { value, writable: true, enumerable: true, configurable: true });
                 } catch (e) {
                     if (Allow.OBJ & allow) return obj;
</code_context>
<issue_to_address>
**issue (bug_risk):** Using `Object.defineProperty` here changes behavior vs simple assignment, especially with prototype accessors, and adds overhead in a hot path.

With inherited accessors this is observable: if `obj`’s prototype defines a setter for `key`, `obj[key] = value` would call that setter, but `defineProperty` instead creates an own data property and completely bypasses it. Unless you explicitly intend to ignore prototype setters or customize descriptors, the direct assignment is safer and more appropriate here.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/index.ts Outdated
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 26534675-9a96-4375-ad70-643ceaec33eb

📥 Commits

Reviewing files that changed from the base of the PR and between 419b5a4 and 12f1eb3.

📒 Files selected for processing (1)
  • src/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/index.ts

📝 Walkthrough

Changes

src/index.ts

  • Updated parseObj() to special-case the __proto__ key: use Object.defineProperty(obj, key, { value, writable: true, enumerable: true, configurable: true }) instead of obj[key] = value, ensuring __proto__ is preserved as an own enumerable property.

src/proto.test.ts

  • Added a Vitest regression test asserting parse('{"__proto__":0}') produces an object where __proto__ is an own enumerable property with value: 0 and writable/enumerable/configurable set to true.

Walkthrough

In src/index.ts, object member assignment inside the partial JSON parser is changed from obj[key] = value to Object.defineProperty(obj, key, { value, writable: true, enumerable: true, configurable: true }). This prevents the JavaScript engine from intercepting assignment to __proto__ (which mutates the prototype chain rather than creating an own property), ensuring keys like __proto__ are stored as regular own enumerable properties. A new test file src/proto.test.ts is added to verify that parsing {"__proto__":0} produces an object with __proto__ as an own enumerable property with value: 0.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~5 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title directly describes the main fix: preserving proto as an object key during parsing.
Description check ✅ Passed The description clearly explains the fix and references the linked issue, relating directly to the changeset.
Linked Issues check ✅ Passed The code changes fully satisfy issue #8 requirements: proto key handling now uses Object.defineProperty with correct descriptors.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the proto parsing issue and adding a regression test.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Author

Pushed the small follow-up from the review: only __proto__ uses Object.defineProperty now, and normal keys are back to direct assignment.

I couldn’t run the suite from here, so CI is still the check.

Copy link
Copy Markdown
Author

Local validation update:

  • vitest run passes on this branch: 2 files, 7 tests.

Same build caveat as #19: the local tsc build is blocked by existing package/dev dependency typing setup around Vite/Vitest Node typings, so CI should remain the build source of truth.

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.

Edge case with proto

1 participant