Skip to content

SMOODEV-2716: Redact client_secret from url-encoded request-body logging#94

Open
brentrager wants to merge 1 commit into
mainfrom
SMOODEV-2716-redact-form-credentials
Open

SMOODEV-2716: Redact client_secret from url-encoded request-body logging#94
brentrager wants to merge 1 commit into
mainfrom
SMOODEV-2716-redact-form-credentials

Conversation

@brentrager

Copy link
Copy Markdown
Contributor

Problem

The request/response logger records the outbound request body via getRequestBody, which returned a url-encoded form body verbatim as an opaque string. The logger redacts by object key (headers, JSON fields) but can't see into a string, so an OAuth client_credentials token exchange logged client_secret=sk_... in plaintext to CloudWatch.

Surfaced during the AuthIssuer P0 (SMOODEV-2716): @smooai/config's TokenProvider.refresh does POST /token with grant_type=client_credentials&client_id=...&client_secret=sk_..., and that whole body landed in logs.

Fix

getRequestBody now runs string bodies through the new exported redactFormCredentials, which scrubs sensitive x-www-form-urlencoded params (client_secret, client_id, access_token, refresh_token, token, password, secret, code, assertion, client_assertion, api_key) to [REDACTED]. It:

  • splits each pair on the first = so base64 values with = padding survive,
  • leaves JSON string bodies untouched (they start with {/[; the logger key-redacts those),
  • routes through the single choke point, so all 6 body log sites are covered.

Tests

src/fetch.redact.test.ts — 3 tests (redacts secret/id while preserving base64 padding; leaves benign form bodies unchanged; doesn't corrupt JSON). tsc --noEmit clean.

Scope / follow-up

TypeScript only. This is the language @smooai/config uses, so it stops the reported leak. The Python / Rust / Go / .NET implementations of @smooai/fetch have the same body-logging shape and need the same redaction — parity follow-up, intentionally not bundled into this incident fix.

Note: the leaked secret is already in CloudWatch history until log retention expires — rotation is flagged for Brent separately.

Jira: SMOODEV-2716

🤖 Generated with Claude Code

https://claude.ai/code/session_018tRq2Rge3gGHCjJdNx6xDD

The request/response logger stored the raw form body as an opaque string, so
an OAuth client_credentials token exchange leaked its client_secret to
CloudWatch in plaintext (surfaced during the AuthIssuer P0). The logger redacts
by object key, but a url-encoded body isn't an object it can see into.

getRequestBody now scrubs sensitive x-www-form-urlencoded params via the new
exported redactFormCredentials (splits on the first `=` so base64 padding
survives; leaves JSON bodies untouched). Covers all body log sites since they
route through getRequestBody. TypeScript only — Python/Rust/Go/.NET parity is a
follow-up.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018tRq2Rge3gGHCjJdNx6xDD
@changeset-bot

changeset-bot Bot commented Jul 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f0e4980

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@smooai/fetch Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

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