Skip to content

fix: redact secrets from audit log descriptions#103

Open
christianromeni wants to merge 1 commit into
mainfrom
fix/audit-log-secret-redaction
Open

fix: redact secrets from audit log descriptions#103
christianromeni wants to merge 1 commit into
mainfrom
fix/audit-log-secret-redaction

Conversation

@christianromeni

Copy link
Copy Markdown
Contributor

Problem

The audit middleware wrote admin API request bodies into audit_logs.description, which leaked secrets in cleartext: user passwords, upstream provider API keys, MCP auth_token / oauth_client_secret, SSO client_secret, and license keys. Any org_admin with audit-log read access could read them.

Fix

  • buildDescription now replaces the values of sensitive fields with [REDACTED] instead of persisting them, recursively through nested objects and arrays of objects.
  • Field matching is case-insensitive (strings.ToLower), because Go unmarshals JSON into the request structs case-insensitively - so {"Password": "..."} would otherwise be processed by the handler but slip past redaction.
  • Sensitive keys: password, api_key, auth_token, oauth_client_secret, client_secret, key, token.
  • Non-sensitive fields keep the existing drop-empty/null/zero behaviour.

Existing data

Migration 0013_redact_audit_log_secrets clears description on existing rows whose value contains a known sensitive key (conservative LIKE match, SQLite + PostgreSQL portable, idempotent). The down migration is an intentional no-op - the original values are gone and must not be restored. Operators should rotate any secrets that were entered via the admin API before this fix.

Tests

  • Unit tests for buildDescription: top-level + nested + array redaction, case-insensitive variants (Password, API_KEY, Client_Secret), sensitive-field-with-object-value, exact-match negatives (max_tokens not touched), unchanged drop behaviour.
  • End-to-end middleware tests against real in-memory SQLite verifying no plaintext lands in the persisted description.
  • go test -race ./internal/audit/ ./internal/db/ ./internal/api/... clean.

Note: the login endpoint never went through this path (it builds its own description from the email only) - verified, no password leak there.

The audit middleware persisted admin API request bodies into
audit_logs.description, leaking passwords, upstream API keys, OAuth
secrets, auth tokens and license keys in cleartext. Any org_admin with
audit-log read access could see them.

buildDescription now replaces the values of sensitive fields with
[REDACTED] recursively (nested objects and arrays included), keyed by a
case-insensitive field-name match so variants like "Password" cannot
slip through. Non-sensitive fields keep the existing drop behaviour.

Migration 0013 clears description on existing rows that may already
contain secrets. Affected secrets should be rotated.
@snyk-io

snyk-io Bot commented Jun 10, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@codecov

codecov Bot commented Jun 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

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.

1 participant