You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
finding(core): DataScopeManager lacks the sibling evaluator's two guards — a constructor field name admits every row, and comparisons coerce null / numeric strings #7751
Carved out of the contract review of PR #7748 (card objectui#7378) by the domain:ui PM seat. Pre-existing — PR #7748 neither introduced nor widened these; it aligned the default arm and the reviewer measured these two while comparing the pair.
Split out from the liveness-decision card objectui#7750 on purpose: these are independently actionable today, whichever way that decision goes, and burying a live hardening gap inside a retire-or-canonicalise decision is how it sits unfixed while the decision stalls.
The two gaps, measured by probe against the head source
packages/core/src/data-scope/DataScopeManager.ts vs the sibling packages/permissions/src/evaluator.ts, which is the repo's other evaluator of the same kind and is the one the #7378 triage used as its decisive control.
1. ⭐ No field-name guard — a prototype key admits everything
The sibling refuses__proto__ / constructor / prototype and reads members with hasOwnProperty. DataScopeManager does neither, so a scope rule naming a prototype key evaluates against the prototype chain rather than the record — and on a ne comparison that means every row passes.
⚠️ Same failure direction as #7378 — admit, silently — on the same permission boundary. That is why it is filed rather than noted.
The sibling requires typeof === 'number' on both sides before comparing. This evaluator does not, so null and numeric strings fall through the comparison arms.
What this card is
Bring the two evaluators to one hardening standard, and pin the standard so they cannot diverge a third time.
⚠️ Open questions the implementer must answer rather than assume:
Is the sibling's shape the target, or is there a reason DataScopeManager should coerce? ⛔ Do not assume the sibling is right merely because it is stricter — measure what each one's callers depend on.
The two evaluators also differ in spelling: ne/nin here vs neq/not_in there, and the sibling implements is_null/is_not_null which this one does not. ⛔ Do not fold that in — spelling is the liveness-decision card's question, not this one's.
Carved out of the contract review of PR #7748 (card objectui#7378) by the
domain:uiPM seat. Pre-existing — PR #7748 neither introduced nor widened these; it aligned thedefaultarm and the reviewer measured these two while comparing the pair.Split out from the liveness-decision card objectui#7750 on purpose: these are independently actionable today, whichever way that decision goes, and burying a live hardening gap inside a retire-or-canonicalise decision is how it sits unfixed while the decision stalls.
The two gaps, measured by probe against the head source
packages/core/src/data-scope/DataScopeManager.tsvs the siblingpackages/permissions/src/evaluator.ts, which is the repo's other evaluator of the same kind and is the one the #7378 triage used as its decisive control.1. ⭐ No field-name guard — a prototype key admits everything
The sibling refuses
__proto__/constructor/prototypeand reads members withhasOwnProperty.DataScopeManagerdoes neither, so a scope rule naming a prototype key evaluates against the prototype chain rather than the record — and on anecomparison that means every row passes.2. Unguarded comparisons coerce
The sibling requires
typeof === 'number'on both sides before comparing. This evaluator does not, sonulland numeric strings fall through the comparison arms.What this card is
Bring the two evaluators to one hardening standard, and pin the standard so they cannot diverge a third time.
DataScopeManagershould coerce? ⛔ Do not assume the sibling is right merely because it is stricter — measure what each one's callers depend on.yes).ne/ninhere vsneq/not_inthere, and the sibling implementsis_null/is_not_nullwhich this one does not. ⛔ Do not fold that in — spelling is the liveness-decision card's question, not this one's.Refs: objectui#7378 · PR #7748 · objectui#7750 (the liveness decision) ·
packages/permissions/src/evaluator.ts:158-159.Generated by Claude Code