Security hardening: validate()/assert()/is() return the live object → read-unstable properties (getters/Proxy) can bypass validation

Author: peaktwilightCreated Jul 14, 2026Updated Jul 14, 2026

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]):

javascript
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 saw

Reachability (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