Skip to content
GitHub
Logic Errors

Bare `in` operator in a string type guard admits Object.prototype keys

Bare in operator in a string type guard admits Object.prototype keys

Problem

A guard narrowing loosely-typed wire strings — value in WIRE_TO_OUTCOME over a plain object literal — walks the prototype chain, so "toString" passes. The subsequent map lookup returns Object.prototype.toString (a function, not undefined), and downstream dereferencing crashes the component render instead of taking the designed neutral fallback.

Solution

// Before
return value in WIRE_TO_OUTCOME;
// After
return Object.hasOwn(WIRE_TO_OUTCOME, value);

Pin it with a test: expect(isDecisionStatus("toString")).toBe(false) (packages/ui/src/four-state.dom.test.tsx).

Prevention

Any membership guard over a plain-object record keyed by external input uses Object.hasOwn (ES2022) or a Set. in is only safe on Object.create(null) maps or when prototype keys are impossible by construction.

References

  • SPEC-603 review fix commit 7034d76 (packages/ui/src/four-state.tsx).