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
fix(security): enforce static readonly fields on the UPDATE write path (#2948) (#2957)
A field's static `readonly: true` was never enforced server-side on update: the
record validator only skipped read-only columns from validation, and only the
conditional `readonlyWhen` variant was stripped from the write payload. A
non-system update could therefore overwrite any readonly column — audit stamps,
provenance, or other system-computed values. (#2946 already closed the
cross-tenant organization_id face; this is the broader in-tenant integrity face.)
engine.update now strips caller-supplied writes to statically-readonly fields
for non-system contexts, on both the single-id and multi-row paths (strips,
does not reject — symmetric with readonlyWhen). Two guards keep legit writes
intact:
- caller-supplied only: the strip runs against a snapshot of the keys the
caller sent BEFORE hooks/middleware ran, so audit-hook stamps (updated_by/
updated_at) and write-middleware stamps survive; only a client that forged a
readonly field has it dropped.
- system-context exempt: isSystem writes (import, seed replay, approvals,
lifecycle) legitimately set readonly columns and skip the strip.
Adds a stripReadonlyFields helper + unit tests and an engine-level integration
test (user forge stripped, server stamp survives, system write allowed).
objectql suite: 852 passed.
Claude-Session: https://claude.ai/code/session_019QRUvVfpvSycAHMMF2xTxs
Co-authored-by: Claude <noreply@anthropic.com>
0 commit comments