fix(shared): prevent clone() from re-parenting the cloned object - #1790
Conversation
clone() copied properties with plain assignment, so a source object with an own `__proto__` key (routine from JSON.parse or NullProtoObj, both treated as plain objects here) invoked Object.prototype's `__proto__` setter on the result instead of creating a property. The clone came back re-parented onto the source's payload, or silently lost the key when the value was not an object. Copy through Object.defineProperty so every key becomes a real own property.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
clone()re-parenting fix: the plain-object loop inpackages/shared/src/object.tsnow copies properties via the existingdefineOwnPropertyhelper (Object.defineProperty) instead of plain assignment, so an own__proto__data key becomes a real own property instead of triggeringObject.prototype's__proto__setter. Applied to both the string-key and symbol loops.- New
clone with __proto__ propertytest coveringJSON.parseandNullProtoObjsources.
The fix is minimal and reuses the established defineOwnProperty helper (already used by set). Because defineOwnProperty sets writable/enumerable/configurable, the behavior is identical to plain assignment for ordinary properties — the only change is that __proto__-style data keys no longer re-parent the result or get silently dropped. I verified the new test genuinely fails on the previous code: with assignment, the clone's prototype becomes the payload and __proto__ is absent as an own property, so both prototype and value assertions catch the bug.
openrouter/~deepseek/deepseek-v4-flash-latest (free via Pullfrog for OSS) | 𝕏
More templates
@orpc/ai-sdk
@orpc/arktype
@orpc/bun
@orpc/client
@orpc/cloudflare
@orpc/contract
@orpc/experimental-effect
@orpc/evlog
@orpc/hibernation
@orpc/json-schema
@orpc/nest
@orpc/next
@orpc/openapi
@orpc/opentelemetry
@orpc/pinia-colada
@orpc/pino
@orpc/publisher
@orpc/ratelimit
@orpc/server
@orpc/shared
@orpc/swr
@orpc/tanstack-query
@orpc/trpc
@orpc/valibot
@orpc/zod
commit: |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |

clone()copied properties with plain assignment, so a source object carrying an own__proto__key went throughObject.prototype's__proto__setter instead of getting a property. The clone came back re-parented onto the source's payload, or silently lost the key when its value was not an object. Copying throughObject.definePropertymakes every key a real own property.This is reachable with ordinary input:
JSON.parseandNullProtoObjboth produce own__proto__keys, andisPlainObjecttreats both as plain objects.Fixes
__proto__key now yields a clone whose prototype is stillObject.prototype, with__proto__present as a normal own property.__proto__values (e.g.2) are preserved instead of being dropped by the setter.Object.prototypeitself was never mutated, so this was clone corruption rather than global prototype pollution.Testing
New
clone with __proto__ propertycase covers both theJSON.parseandNullProtoObjsources and fails on the previous code (the clone's prototype had become the payload). Fullpackages/sharedsuite passes at 302 tests, lint clean.