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
feat(objectql): mask password fields on the generic read path (#2036)
A `password`-typed field on a non-auth object round-tripped plaintext
through the generic CRUD engine — neither hashed nor masked the way
`secret` is. Someone modeling a `password` field on a custom object
reasonably expects credential-grade handling; they silently got plaintext
storage and plaintext reads.
Mask `password` to SECRET_MASK on the generic read path (find/findOne, and
$expand which re-enters find), mirroring `secret`. Unlike `secret`, a
password value stays plaintext at rest — no encryption, no sys_secret row,
no CryptoProvider required. An echoed mask is dropped on write so a form
round-trip does not overwrite the stored value. Objects marked
`managedBy: 'better-auth'` are exempt so the auth subsystem's own reads
(which go through the engine) still see the stored value; a platform-objects
pin asserts no shipped identity object even declares a `password` field
today. `ObjectSchema.create()` now warns (non-fatally, deduped per object)
when a `password` field is declared on a non-auth object, steering authors
to `secret` or the auth subsystem.
Decision and rationale recorded in ADR-0100; the aggregate() masking gap
(pre-existing for `secret` too) is noted there as a follow-up.
- objectql: add collectMaskedReadFields; wire mask/echoed-mask paths
- spec: author-time warning in ObjectSchema.create(); correct field.zod docs
- tests: password-masking units, warning units, platform-objects pin,
dogfood field-zoo f_password upgraded present -> masked
- examples: field-zoo label 'Password (one-way hash)' -> '(masked on read)'
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QJrKxe1jXGudFT3nUks1yN
0 commit comments