mirror of
https://github.com/Such-Software/smirk-backend-core.git
synced 2026-10-17 15:31:38 +02:00
From a 5-lens adversarial review of the admin surface (6 confirmed findings,
collapsing to 4 fixes; 2 dismissed after verification):
- HIGH: last-key-revoke TOCTOU (lockout). The count(live)<=1 floor was checked in
the handler, then revoke ran separately — two concurrent revokes could both pass
and empty the allowlist. Moved the floor INTO revoke_admin_key_full under a
per-tx advisory lock (admin_keys_mutate_lock); returns RevokeKeyOutcome
{Revoked,NotFound,WouldEmptyAllowlist}.
- LOW: ADMIN_MAX_KEYS cap TOCTOU. Same check-then-mutate race on add. Cap is now
counted INSIDE create_admin_key_audited under the same lock; returns
AddKeyOutcome {Created,CapReached}. rotate takes the lock too, so all
add/revoke/rotate serialize.
- LOW: integrity MAC didn't cover activation_deadline — a DB-write attacker could
null/extend the deadline to revive an expired pending key without breaking the
MAC. Added activation_deadline to AdminKeyMacInput + the canonical input +
every compute/verify site; new KAT binds it.
- LOW: get_active_admin_key (and create_*) logged the full pubkey via #[instrument]
(params weren't skipped), defeating the prefix-only / no-social-graph design.
Skipped pubkey/input from those spans.
Dismissed (both skeptics not-a-bug): audit-chain JSONB `details` round-trip — the
handlers always pass details:None and serde's sorted-key default makes the
string case round-trip (the L1 chain test proves it). Documented v1 deferrals
(login timing-oracle hardening, per-pubkey backoff, hard non-loopback bind guard)
remain tracked, not regressions.
All gates green: 138 lib + 138 integration, no openapi drift, secret-scan, leak.