Security hardening: validate()/assert()/is() return the live object → read-unstable properties (getters/Proxy) can bypass validation
Hi — a constructive heads-up (private vuln reporting isn't enabled and there's no SECURITY.md, so raising it here with the fix; happy to move it private if you enable reporting).
Issue. validate(value, Struct) returns [err, output] with output === value on success (and assert/is inspect the live object). If a validated
property is a non-idempotent read (accessor getter, Proxy get-trap, mutable-
state-derived), the value checked can differ from the value used next — a
validation TOCTOU bypassing allowlist/authorization checks.
PoC ([email protected]):
const S = object({ role: enums(["readonly","guest"]) });
let n = 0;
const input = { get role(){ return ++n === 1 ? "readonly" : "admin"; } };
const [err, output] = validate(input, S); // err undefined (valid)
output === input; // true
output.role === "admin"; // app uses a value validation never sawReachability (honest). Not reachable from a plain JSON body; bites when validating objects with accessor getters, Proxy-wrapped objects, or state-derived getters across the validate→use gap. Medium.
Fix. Return a materialized, read-stable coercion (read each property once into
a fresh object), as joi (value) and zod (data) do; or document that
validate returns the caller's live object with no read-stability guarantee.
Reported by 0sec (https://0sec.ai). No ask — just handing you the finding + fix.
Source: ianstormtaylor/superstruct