Skip to content

fix: redact secrets in DLQ read API (PILOT-314)#29

Open
matthew-pilot wants to merge 1 commit into
mainfrom
openclaw/pilot-314-20260530-234309
Open

fix: redact secrets in DLQ read API (PILOT-314)#29
matthew-pilot wants to merge 1 commit into
mainfrom
openclaw/pilot-314-20260530-234309

Conversation

@matthew-pilot
Copy link
Copy Markdown
Collaborator

Summary

HandleGetWebhookDLQ previously returned event Details verbatim. If the audit-level redaction (redactKey in audit/audit.go) missed a secret field, the DLQ became a credential-disclosure surface for any caller holding the admin token.

Fix

  • Export RedactMap in the audit package — applies the same redactKey rules to a map[string]interface{}
  • Apply audit.RedactMap to each DLQ entry's Details on retrieval in HandleGetWebhookDLQ

Scope

Tier Files LoC
small 2 (+2 test files) +29 production, +99 test

Verification

  • go build ./...
  • go vet ./...
  • go test ./audit/ ./webhook/ -count=1 ✅ (new tests: TestRedactMap, TestHandleGetWebhookDLQRedactSecrets)

Ticket

🔗 PILOT-314

HandleGetWebhookDLQ previously returned event Details verbatim.
If the audit-level redaction (redactKey) missed a secret field,
the DLQ became a credential-disclosure surface for any caller
holding the admin token.

- Export RedactMap in audit package (applies same redactKey rules)
- Apply audit.RedactMap to DLQ entry details on retrieval
- Add tests for RedactMap and DLQ redaction path
@matthew-pilot matthew-pilot added the matthew-fix Autonomous fix by matthew-pilot (small: ≤3 files / ≤50 LoC) label May 30, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented May 30, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@matthew-pilot
Copy link
Copy Markdown
Collaborator Author

📊 PR Status — PILOT-314

  • PR state: Open ✅ · Mergeable (no conflicts) · 1 label (matthew-fix, small tier: 2 files / +29 LoC production)
  • Canary: 🟡 Queued — run #26698070234 waiting for runner (triggered at 23:43 UTC, build+deploy+test)
  • Jira: PILOT-314 — evaluated at 23:48 UTC; fix applied, canary pending
  • Reviews: None yet · Last activity: codecov bot report at 23:46 UTC · No operator review requests open
  • CI: Codecov reports full coverage on modified lines ✅

@matthew-pilot
Copy link
Copy Markdown
Collaborator Author

🔍 Diff Walkthrough — PILOT-314: Redact secrets in DLQ read API


audit/audit.go (+23 lines, after L326)
Exports a new RedactMap function that takes a map[string]interface{} and returns a shallow copy with any value whose key matches redactKey() replaced by "<redacted>". This is the same key-matching logic already used by BuildEntry (private to the audit package). Nil-safe — returns nil for nil input.

  • L327–349: RedactMap — iterates keys, checks redactKey(k), substitutes "<redacted>" for sensitive keys (token, password, api_key, secret suffixes, etc.), passes non-sensitive keys through.

webhook/webhook.go (+4 lines)
The DLQ handler HandleGetWebhookDLQ previously returned ev.Details verbatim on L399. Now applies audit.RedactMap before including it in the response.

  • L27: New import github.com/pilot-protocol/rendezvous/audit
  • L383: Doc-comment updated to note PILOT-314 redaction
  • L400: ev.Detailsaudit.RedactMap(ev.Details) — the single semantic change

audit/zz_redaction_test.go (+45 lines)
TestRedactMap with 3 subtests:

  • ✅ Redacts token, api_key, db_secret; leaves hostname, reason intact
  • ✅ Nil-safe
  • ✅ Empty-map safe

webhook/zz_webhook_test.go (+54 lines)
TestHandleGetWebhookDLQRedactSecrets — end-to-end: emits an event with admin_token + password in details, waits for DLQ delivery, asserts both are <redacted> in the API response while hostname and reason pass through.


Why this matters: redactKey covers ~dozen patterns, but if a new secret key slips through audit's build-time redaction, the DLQ becomes a credential-disclosure surface for any caller with the admin token. This defense-in-depth closes that surface at read time.

@matthew-pilot
Copy link
Copy Markdown
Collaborator Author

📊 PR Status — #29 PILOT-314

Field Value
State OPEN
Mergeable ✅ MERGEABLE (clean)
Draft No
Branch openclaw/pilot-314-20260530-234309main
Files 4 files, +127/−1
Labels matthew-fix

CI Checks (2/2 passing)

Check Result
test ✅ pass
codecov/patch ✅ pass

Links

@matthew-pilot
Copy link
Copy Markdown
Collaborator Author

🔍 PR Explanation — #29 PILOT-314

What this does

Applies audit-level secret redaction to the DLQ read API (HandleGetWebhookDLQ). Previously, DLQ event Details were returned verbatim — if the audit-build redaction (redactKey) missed a secret field, the DLQ became a credential-disclosure surface for any caller holding the admin token.

The problem

HandleGetWebhookDLQ returned ev.Details raw. The existing redactKey function (inside audit/audit.go) correctly redacts secrets at audit-build time, but that's a package-private function. There was no way for the webhook layer to re-apply the same rules on retrieval, so a single missed field at emission time meant permanent exposure.

The fix

1. Exported RedactMap (audit/audit.go +23/−0)

  • New public function RedactMap(m map[string]interface{}) map[string]interface{}
  • Returns a shallow copy with any key matching redactKey replaced by "<redacted>"
  • Nil-safe (returns nil for nil input)
  • Uses the same 11 secret-key prefixes as redactKey (token, password, secret, api_key, auth, credential, private_key, session, jwt, bearer, signature)

2. Applied in HandleGetWebhookDLQ (webhook/webhook.go +5/−1)

  • Added "github.com/pilot-protocol/rendezvous/audit" import
  • Changed entry["details"] = ev.Detailsentry["details"] = audit.RedactMap(ev.Details)
  • One-line call site — applies defense-in-depth redaction on every DLQ read

3. Tests (+99 lines across two test files)

  • TestRedactMap: 3 sub-cases (redacts sensitive keys, nil-safe, empty map)
  • TestHandleGetWebhookDLQRedactSecrets: integration test — emits an event with admin_token and password in Details, waits for DLQ delivery, asserts both are <redacted> and non-secret keys pass through

Scope

Tier Files LoC
small 2 (+2 test) +29 prod, +99 test

@matthew-pilot
Copy link
Copy Markdown
Collaborator Author

📊 PR Status Update — #29 PILOT-314

Field Value
State OPEN
Mergeable ✅ MERGEABLE
CI 2/2 pass (test ✅, codecov/patch ✅)
Labels matthew-fix
Canary ❓ no canary:pending found (earlier run #26698070234 may have completed/expired)
Jira QA/IN-REVIEW (state-correct applied 2026-05-31T03:19Z)
Last updated 2026-05-31T00:13Z

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

matthew-fix Autonomous fix by matthew-pilot (small: ≤3 files / ≤50 LoC)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant