Skip to content

fix(security): redact credentials in JSON and form bodies under --verbose - #504

Open
gyanranjanpanda wants to merge 3 commits into
microcks:masterfrom
gyanranjanpanda:fix/redact-oauth-tokens-from-logs
Open

fix(security): redact credentials in JSON and form bodies under --verbose#504
gyanranjanpanda wants to merge 3 commits into
microcks:masterfrom
gyanranjanpanda:fix/redact-oauth-tokens-from-logs

Conversation

@gyanranjanpanda

Copy link
Copy Markdown
Contributor

Problem

Commit 0f8b000 (closes #449) added redactSensitiveContent to verbose HTTP dumps. It works for Authorization headers but the sensitiveParamPattern regex only matches name=value (form encoding). Both dump sites that carry secrets produce JSON bodies, so the pattern never fires.

Leak 1keycloak_client.go:96: Keycloak token response body is JSON. access_token, refresh_token, and id_token print verbatim.

Leak 2microcks_client.go:371: OAuth2ClientContext is marshalled into the POST /api/tests body. clientSecret, password, and refreshToken are not in the pattern at all.

The Authorization: [REDACTED] header line makes the output look safe, masking the residual exposure.

Tracked in #503.

Fix

Replace the single catch-all regex with a structured approach:

  • JSON bodies — parse with encoding/json, redact sensitive keys by name
  • Form bodies — parse with url.ParseQuery, redact by key name
  • Unknown/chunked — text-pattern fallback

Key matching normalises snake_case, camelCase, and kebab-case so every spelling variant is covered (access_token, accessToken, Access-Token all match).

Non-sensitive metadata (token_type, expires_in, serviceId) is preserved so --verbose stays useful.

Tests

pkg/config/config_test.go — 13 tests (all new), covering:

  • JSON token response (regression for Leak 1)
  • JSON test request with oAuth2Context (regression for Leak 2)
  • Form-encoded token exchange
  • Nested/array JSON
  • Chunked transfer fallback
  • Header-only dumps
  • Non-sensitive body preservation
  • CRLF round-trip
  • Key normalisation variants
  • End-to-end through DumpRequestIfRequired / DumpResponseIfRequired
ok  github.com/microcks/microcks-cli/pkg/config  0.657s

…bose

The regex introduced in 0f8b000 only matched name=value (URL form
encoding). The two dump sites that carry secrets both produce JSON
bodies, so the pattern never fired. Additionally, the OAuth2 body
field names (clientSecret, password, refreshToken) were absent from
the alternation entirely.

Replace the single catch-all regex with a structured approach:
- Parse JSON bodies with encoding/json and mask sensitive keys by name.
- Parse form bodies with url.ParseQuery and mask by key name.
- Fall back to text-pattern matching for chunked or unparseable bodies.
- Match key names after normalising snake_case / camelCase / kebab-case
  so every spelling variant of the same credential is covered.
- Widen the header pattern to cover Proxy-Authorization, X-Auth-Token,
  Cookie and Set-Cookie in addition to Authorization.

Sensitive key set: accessToken, refreshToken, idToken, token,
clientSecret, password, secret, code, codeVerifier, authorization,
apiKey.

Non-sensitive metadata (token_type, expires_in, serviceId, …) is
preserved so --verbose output remains useful for debugging.

Fixes: microcks#503 (follow-up to 0f8b000, closes microcks#449)

Signed-off-by: gyanranjanpanda <sanupanda141@gmail.com>
Resolve the add/add conflict on pkg/config/config_test.go against the
suite added by microcks#498, which also defined TestRedactSensitiveContent and
captureStdout.

Both suites are kept. microcks#498's file is taken as the base; the redaction
cases from this branch are appended as TestRedactSensitiveContentInBodies
and rewritten in testify style to match the surrounding file. The
duplicate captureStdout helper from this branch is dropped in favour of
microcks#498's testing.TB variant.

Two assertions in microcks#498's TestRedactSensitiveContent caught real defects
in this branch, both now fixed in config.go:

- Query parameters were no longer redacted. Splitting the dump into head
  and body moved all key matching into the body, so an OAuth code in a
  request line or redirect target passed through untouched -- the exact
  leak 0f8b000 set out to close. redactText now runs over the head as
  well. The value character class additionally excludes '?' so that the
  "http://host" portion of a URL cannot swallow the query string before
  its parameters are examined.

- The Authorization header emitted "Bearer [REDACTED]". Preserving the
  auth scheme was a gratuitous change that broke the output contract
  microcks#498 pinned; reverted to "Authorization: [REDACTED]".

Signed-off-by: gyanranjanpanda <sanupanda141@gmail.com>
@gyanranjanpanda
gyanranjanpanda force-pushed the fix/redact-oauth-tokens-from-logs branch from a15df70 to 1261ec2 Compare August 17, 2026 08:48

@Caesarsage Caesarsage left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is good.

I left a comment. Also, could you reduce the comments , it is making the code unnecessarily large and noisy. comments is only useful where it is really needed like on the complex regex and should be simple

Comment thread pkg/config/config_test.go
}

func TestRedactSensitiveContentPreservesCRLF(t *testing.T) {
dump := "GET / HTTP/1.1\r\nAuthorization: Bearer eyJLEAKEDACCESS\r\nAccept: */*\r\n\r\n"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One gap here: authToken / auth-token values leak through — authtoken isn't in sensitiveValueKeys, and auth-token is the exact key the CLI's own config uses:

dump := "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n" +
{"authToken":"eyJPROBELEAK"}
redactSensitiveContent(dump) // value survives
Adding "authtoken": {} to the set covers both spellings via the normalizer.

Minor, non-blocking: json.Marshal re-encoding reorders keys and HTML-escapes, so dumped bodies aren't byte-faithful (and won't match Content-Length) — worth a one-line code comment.

- Add authtoken to sensitiveValueKeys to cover authToken and auth-token in JSON and form bodies.

- Add one-line note regarding json.Marshal re-encoding.

- Reduce comment noise across redaction logic and tests.

- Add test coverage for authToken and auth-token redaction.

Signed-off-by: gyanranjanpanda <sanupanda141@gmail.com>
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.

Security: SSO login leaks OAuth access and refresh tokens to CLI logs

2 participants