Summary
suspend_api_key writes status=SUSPENDED without looking at the key's current status. Calling POST /api/api-keys/{key_id}/suspend on a key that is already REVOKED turns it into a SUSPENDED key (while revokedAt stays set), so a terminal state is overwritten and the key's listed status is wrong.
Affected code
autogpt_platform/backend/backend/data/auth/api_key.py — suspend_api_key (L244-269), route POST /{key_id}/suspend in backend/api/features/api_keys/routes.py. Related: revoke_api_key (L170) re-stamps revokedAt on an already revoked key, and update_api_key_permissions (L287) edits revoked keys.
Trigger / steps
- Create an API key, revoke it (
DELETE /api/api-keys/{id}).
POST /api/api-keys/{id}/suspend.
GET /api/api-keys/{id}.
Expected vs actual
- Expected: 4xx (a revoked key cannot be suspended), status stays REVOKED.
- Actual: 200, status is SUSPENDED and
revoked_at is still set. validate_api_key only accepts ACTIVE keys, so the key stays unusable today; but the UI/audit trail now reports a revoked key as merely suspended, and any future "reactivate" action for suspended keys would resurrect a key the owner had revoked.
Severity: low
No authentication bypass today (only ACTIVE keys validate); it is state corruption on a security-relevant record.
How I confirmed it
Ran the real create_api_key → revoke_api_key → suspend_api_key → update_api_key_permissions → revoke_api_key against a local Postgres with all migrations applied: after suspend the status is SUSPENDED with revoked_at still set; permissions can still be changed on the revoked/suspended key; revoking again overwrites revokedAt. Did not call the HTTP routes.
Fix feasible: yes
Reject suspend/permission-update when status is REVOKED (and make revoke idempotent) using a conditional update_many(where={"id": ..., "status": ACTIVE}).
Summary
suspend_api_keywritesstatus=SUSPENDEDwithout looking at the key's current status. CallingPOST /api/api-keys/{key_id}/suspendon a key that is already REVOKED turns it into a SUSPENDED key (whilerevokedAtstays set), so a terminal state is overwritten and the key's listed status is wrong.Affected code
autogpt_platform/backend/backend/data/auth/api_key.py—suspend_api_key(L244-269), routePOST /{key_id}/suspendinbackend/api/features/api_keys/routes.py. Related:revoke_api_key(L170) re-stampsrevokedAton an already revoked key, andupdate_api_key_permissions(L287) edits revoked keys.Trigger / steps
DELETE /api/api-keys/{id}).POST /api/api-keys/{id}/suspend.GET /api/api-keys/{id}.Expected vs actual
revoked_atis still set.validate_api_keyonly accepts ACTIVE keys, so the key stays unusable today; but the UI/audit trail now reports a revoked key as merely suspended, and any future "reactivate" action for suspended keys would resurrect a key the owner had revoked.Severity: low
No authentication bypass today (only ACTIVE keys validate); it is state corruption on a security-relevant record.
How I confirmed it
Ran the real
create_api_key→revoke_api_key→suspend_api_key→update_api_key_permissions→revoke_api_keyagainst a local Postgres with all migrations applied: after suspend the status isSUSPENDEDwithrevoked_atstill set; permissions can still be changed on the revoked/suspended key; revoking again overwritesrevokedAt. Did not call the HTTP routes.Fix feasible: yes
Reject suspend/permission-update when status is REVOKED (and make revoke idempotent) using a conditional
update_many(where={"id": ..., "status": ACTIVE}).