Backend review fixes (stages B1/B2): salted master KDF, AAD-bound ciphertexts, hardened destructive endpoints
...
- crypto: master-key derivation is now scrypt (n=2^15) with a per-install
salt (GNEXUS_CREDS_MASTER_KEY_SALT); plain sha256 kept as the legacy
path so pre-salt data still decrypts; salt is mandatory in production
- crypto: encrypted field envelopes are now bound by aad to
"<user_id>:<secret_id>:<version_id>" (pre-generated version ids) so a
ciphertext transplanted between rows of the same user fails auth
instead of silently decrypting; unbound legacy envelopes decrypt
through a single fallback and are re-bound by scripts/migrate-crypto.py
- secrets.notes widened 140 -> 255 (model, schemas, alembic 0002)
- list_secrets: sort_by restricted to a column whitelist (422 otherwise);
limit clamp aligned with the API layer (1..200)
- update_secret: tag normalization moved to schemas.normalize_tags; the
metadata audit now records the tags diff BEFORE the rows change (the
old value used to be read after the rows were cleared)
- DELETE /account-data now requires Scope.admin (ui channel unaffected)
plus the sensitive rate limiter, and audits the deleted count
- POST /import caps payload.secrets at 1000
- POST /admin/restore takes a typed payload and resolves the filename
against the backup listing; restore_backup itself refuses files
outside the backup dir
- production settings: master_key_salt required, wildcard cors_origins
rejected
- tests: 62 -> 78 (crypto KDF/aad, sort whitelist, notes cap, import cap,
account-data scopes, restore validation, config prod rules)
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
23 hours ago