Skip to content

fix(audit): map identity_unlinked and reauthenticate to a log_type - #2683

Open
thanderoy wants to merge 1 commit into
supabase:masterfrom
thanderoy:fix/audit-log-type-missing-actions
Open

fix(audit): map identity_unlinked and reauthenticate to a log_type#2683
thanderoy wants to merge 1 commit into
supabase:masterfrom
thanderoy:fix/audit-log-type-missing-actions

Conversation

@thanderoy

@thanderoy thanderoy commented Aug 7, 2026

Copy link
Copy Markdown

Why

NewAuditLogEntry builds every audit payload with "log_type": ActionLogTypeMap[action] (audit_log_entry.go#L110). Indexing a Go map with an absent key returns the zero value, so any action missing from ActionLogTypeMap is persisted with log_type: "" instead of failing loudly.

Two declared actions were missing from the map, and both are on live code paths:

Action Declared Emitted from
identity_unlinked audit_log_entry.go#L44 identity.go#L56
user_reauthenticate_requested audit_log_entry.go#L32 reauthenticate.go#L41

The unlink and reauthenticate requests themselves complete correctly — only the audit record is affected. But those entries can't be filtered or attributed by log_type the way every other action can.

Fixes #2679, which reports the identity_unlinked case. user_reauthenticate_requested has the same root cause and is fixed here too.

How

Both actions map to user, consistent with how the other user-facing actions are already classified (user_modified, user_updated_password, passkey_created / passkey_updated / passkey_deleted).

The remaining risk is recurrence: the failure mode is silent by construction, and nothing today connects declaring an action to mapping it. So this adds internal/models/audit_log_entry_test.go with two tests — every declared action resolves to a non-empty log_type, and the map holds no action the package doesn't declare (which catches a stale entry after a rename). Both are pure unit tests; no database, no fixtures.

Verified the test detects the original defect: reverting just the two map entries fails it on exactly identity_unlinked and user_reauthenticate_requested, and it passes with them restored.

Notes

  • Payload shape changes for these two actions. New entries carry log_type: "user" where they previously carried "". This restores the intended behaviour rather than changing an interface, but flagging it since anything filtering audit logs on the empty value will see different results.
  • No backfill migration. Existing rows keep log_type: "". Happy to add one if you'd prefer historical entries corrected — it seemed out of scope for a fix this size, and I didn't want to assume a rewrite of existing audit records is wanted.
  • allAuditActions is hand-maintained. A new action has to be added to that slice as well as the map, so the test does not catch an action that's declared but never listed. The airtight alternative is a single exported registry the map and tests both derive from — a larger change to this file's shape, so I kept this PR minimal. Glad to follow up with it if you want that direction.
  • Ran make static, gosec, go vet, and gofmt against internal/models — all clean. Pre-existing staticcheck hints elsewhere in the file (interface{}any, QF1012) are untouched to keep the diff scoped.

NewAuditLogEntry builds every payload with ActionLogTypeMap[action]. A
missing key yields the zero value, so an unmapped action is persisted
with an empty log_type rather than failing loudly.

IdentityUnlinkAction and UserReauthenticateAction were both declared and
both in use (internal/api/identity.go, internal/api/reauthenticate.go)
but absent from the map, so their audit entries were written with
log_type "" and could not be filtered alongside every other action. Map
both to user, consistent with the passkey and user_modified actions.

Add a test asserting the map covers every declared action, and that it
holds no actions the package does not declare. The failure mode here is
silent by construction, so it needs a test to stay fixed.
@thanderoy
thanderoy requested a review from a team as a code owner August 7, 2026 09:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Audit log entries for identity_unlinked are written with an empty log_type

1 participant