Skip to content

fix(audit): redact credential fields the scrubber missed and never store a raw body - #102

Open
atbagan wants to merge 1 commit into
mainfrom
fix/audit-scrub-credential-keys
Open

atbagan wants to merge 1 commit into
mainfrom
fix/audit-scrub-credential-keys

Conversation

@atbagan

@atbagan atbagan commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Summary

The audit request-body scrubber (internal/audit/scrub.go, since #35) redacts an exact list of keys. Butler's own request bodies use prefixed credential keys the list does not match, so provider and identity-provider credentials are retained in the in-memory audit ring and served by GET /admin/audit to any platform viewer.

Reproduced live on butler-beta: POST /api/providers/test with {"harvesterKubeconfig":"SENTINEL..."} → the sentinel appears verbatim in the stored audit event's requestSummary.

Exposed fields include harvesterKubeconfig, nutanixPassword, proxmoxPassword, proxmoxTokenSecret, azureClientSecret, gcpServiceAccount, awsSecretAccessKey.

Change

  • Match secret-bearing keys by normalized substring at any depth (password, secret, token, kubeconfig, privatekey, serviceaccount, credential, apikey, accesskey); this subsumes the old exact list and covers the prefixed fields. CA bundles (public certs) are intentionally not redacted.
  • Scrub top-level arrays, not just objects.
  • A body that is not JSON — including a large body truncated mid-object, which was the previous silent leak path — is summarized as omitted rather than returned raw. The middleware no longer pre-truncates the body into invalid JSON; the scrubber bounds its own input (256 KB) and truncates the scrubbed result.
  • Tests assert a sentinel credential never survives, for every Butler credential field and for nested / array / truncated / oversized / top-level-array bodies.

Independent of the parity train; based on main. Not for merge as part of any train — staged for maintainer review as a security fix.

Test plan

  • go build ./...
  • go test ./...
  • go test ./internal/audit -run Scrub -v
  • after deploy: repeat the live repro and confirm the sentinel is [REDACTED]

🤖 Generated with Claude Code

https://claude.ai/code/session_01APpgCYzvB1vEntdFm3py2n

…ore a raw body

The audit request-body scrubber redacted an exact list of keys
(password, token, secret, kubeconfig, ...). Butler's own request bodies
use prefixed keys the list did not match (harvesterKubeconfig,
nutanixPassword, proxmoxTokenSecret, azureClientSecret,
gcpServiceAccount, awsSecretAccessKey), so a provider or identity
provider request retained its credential in the audit event, which the
platform audit log serves to any platform viewer.

Match secret-bearing keys by normalized substring at any depth, which
subsumes the old list and covers the prefixed fields. Top-level arrays
are now scrubbed too. A body that is not JSON (including a large body
truncated mid-object, the previous silent leak path) is summarized as
omitted rather than returned raw, and the middleware no longer
pre-truncates the body into invalid JSON. Tests assert that a sentinel
credential never survives, for every Butler credential field and for
nested, array, truncated, oversized and top-level-array bodies.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01APpgCYzvB1vEntdFm3py2n
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