fix(config): use Object.hasOwn instead of in operator in unsetConfigValueAtPath#99148
fix(config): use Object.hasOwn instead of in operator in unsetConfigValueAtPath#99148zenglingbiao wants to merge 4 commits into
Conversation
|
Thanks for the context here. I swept through the related work, and this is now duplicate or superseded. Keep open: current main still has the inherited-property false positive, while this PR is a focused helper/test fix with sufficient terminal proof and no blocking findings. Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Canonical path: Close this PR as superseded by #99152. So I’m closing this here and keeping the remaining discussion on #99152. Review detailsBest possible solution: Close this PR as superseded by #99152. Do we have a high-confidence way to reproduce the issue? Yes. Source inspection shows current main reaches Is this the best way to solve the issue? Yes. Fixing the shared helper's leaf existence check is narrower and cleaner than adding caller-side special cases, and it matches sibling config mutation code that already uses own-property checks. Security review: Security review cleared: The diff narrows an object-key existence check and adds tests; it does not change dependencies, workflows, credentials, or other supply-chain surfaces. AGENTS.md: found and applied where relevant. What I checked:
Likely related people:
Codex review notes: model internal, reasoning high; reviewed against d0b84a955cff. |
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
bb05c0a to
c7bfa66
Compare
c7bfa66 to
e302b10
Compare
|
@clawsweeper re-review |
|
🦞🧹 Reason: re-review requires an open issue or PR. |
|
@clawsweeper re-review |
|
🦞✅ Reason: structured ClawSweeper close marker: close-required (sha=e302b106bda7371ee7975443a61c11d072d19109) Closed:
|
|
@clawsweeper re-review — This PR fixes unsetConfigValueAtPath in src/config/config-paths.ts. The merged #99152 only fixed restoreOriginalValueOrThrow in src/config/redact-snapshot.ts. These are different functions in different files — current main still has on config-paths.ts:63. This PR is not superseded by #99152. |
|
Reopening: the prior supersession close was incorrect. #99152 changed a different function and does not fix this behavior; current The reviewed invariant is slightly broader than this fork branch: config path reads, writes, and unsets must traverse own serialized data only. Because this fork has maintainer edits disabled, I cannot push the complete repair to the contributor branch. I am preparing a maintainer-owned replacement from this commit, preserving @zenglingbiao's authorship, and will link the exact landed commit here before closing this PR. |
|
The maintainer-owned replacement discussed above has landed:
The final repair covers own-property reads, writes, and removals, including inherited parent/leaf data properties, accessors, and non-writable descriptors. Final proof was 106 focused tests, Testbox |
Summary
Use
Object.hasOwn()instead of theinoperator inunsetConfigValueAtPath()to prevent prototype chain pollution when checking whether a config key exists before deletion.What Problem This Solves
When calling
unsetConfigValueAtPath(root, ["foo", "toString"])on a config object that does not have atoStringkey, the function returnstrue(claiming success) because"toString" in cursormatchesObject.prototype.toString.The function lies to its caller — it reports "key found and deleted" when the key never existed. This can cause config management tools (CLI
config unset, config merge logic) to silently skip error handling for non-existent keys.Code evidence: In
src/config/config-paths.ts:63,if (!(leafKey in cursor))checks the prototype chain.cursoris a plainPathNodeobject (Record<string, unknown>), andleafKeycomes from user-provided dot-notation paths likefoo.toString.Root Cause
inoperator checks the entire prototype chain, not just own propertiesObject.hasOwn()(ES2022+) correctly checks only the object's own propertiesObject.hasOwnis already used 15+ times across the codebase — this was a missed instanceWhy This Fix
Object.hasOwnusage in the codebaseEvidence
unsetConfigValueAtPath({foo:{bar:"v"}}, ["foo","toString"])returnstrue(wrong — key doesn't exist)false(correct — key is not an own property)Real Behavior Proof
Before (on main)
After (with fix)
Tests and Validation
src/config/config-paths.test.tscovers 3 prototype keys + normal deletion + parent pruningnode --import tsxproof script