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
{{ message }}
Repository navigation
security(approvals): expires_at is stored but not enforced — a past-expiry approval can still be approved and resumed #18412
Found by the Opus review of #18404 (#13421 S1). The approval expires_at field is stored but not enforced at decision time. In services/approval_gate_service.py, _transition (~L380-417) does not check it, so a past-expiry approval can still be approved and resumed. #18404's body says so, but nothing tracked it.
Acceptance criteria
_transition refuses to approve or resume a record whose expires_at <= now_utc(), and moves it to an expired state.
Optionally, a sweeper expires stale pending records so they leave GET /approval-gates/pending.
Tests: a past-expiry approve is refused; a just-before-expiry approve succeeds.
Partial delivery in #18404: decision-time enforcement (a past-expiry approve is refused, the row moves to EXPIRED and its source is resumed as expired), expired rows hidden from the pending list, and a service-level expire_stale() with a once-only test. NOT delivered: the periodic scheduler that calls expire_stale(). It is a lifespan change (shutdown-order tests apply) and no emitter sets an expires_at until S2, so it is left open here.
Activity
mrveiss commentedon Oct 12, 2026
Partial delivery in #18404: decision-time enforcement (a past-expiry approve is refused, the row moves to EXPIRED and its source is resumed as expired), expired rows hidden from the pending list, and a service-level
expire_stale()with a once-only test. NOT delivered: the periodic scheduler that callsexpire_stale(). It is a lifespan change (shutdown-order tests apply) and no emitter sets anexpires_atuntil S2, so it is left open here.fix(approvals): atomic decision claim, owner-scoped pending list, enf…