Repository navigation
Description
Background
Follow-up from #1561 / #1585 (redact sensitive headers in the fetch log). That slice masks Authorization, Cookie, Set-Cookie, Proxy-Authorization, X-Api-Key, and x-mcp-remote-auth on the recorded log entry. But core/mcp/fetchTracking.ts records four things per request — url, headers, requestBody, and responseBody — and only headers is redacted. The secrets that ride in the body and URL of those same requests are still logged verbatim.
Claude's review of #1585 flagged this as legitimately out of scope for the headers slice but recommended a tracked follow-up so it doesn't fall through the cracks. There was no existing issue for it.
The gap
For an OAuth token-endpoint exchange, the Authorization header is now masked, yet:
requestBody— the token request POSTsclient_secret,code,refresh_token,code_verifierin the form body → logged in plaintext.responseBody— the token endpoint returnsaccess_token/refresh_token/id_token→ logged in plaintext.url— the full URL including query string is stored; some flows carrycode/tokens as query params → logged verbatim.
Net: the network log still leaks the exact credentials the header redaction was meant to protect.
Scope
In core/mcp/fetchTracking.ts, before an entry reaches any sink, redact sensitive material in:
- Request/response bodies — for form-encoded and JSON bodies, redact known-sensitive fields (
client_secret,code,refresh_token,access_token,id_token,code_verifier,client_assertion,assertion,password,token). Preserve the surrounding shape so the log stays useful; replace only the values. Leave non-sensitive / non-parseable bodies untouched (best-effort parse, never throw). - URL query strings — redact the same sensitive param names in the stored
urlwhile keeping the path + non-sensitive params readable.
Reuse the header slice's REDACTED_HEADER_VALUE-style sentinel and case-insensitive matching. Live outbound requests must remain untouched — only the logged copy is redacted.
Acceptance
- OAuth token request/response bodies and any token-bearing URL query params show the redaction sentinel in the fetch log; live requests are byte-identical to before.
- Non-sensitive bodies/URLs are unchanged and still logged.
- Malformed/unparseable bodies don't throw — they log as-is (or as an opaque redaction if they can't be safely inspected).
- Per-file coverage ≥90 on all four dimensions for the touched code, expected-error output suppressed in tests.
Activity
cliffhall commentedon Jul 2, 2026
Smoke test & AGENTS.md audit — PR #1598
Independent verification of
1593-redact-bodies(base1579-wave-1, HEAD775b98a). Detached-checkout oforigin/1593-redact-bodies, freshnpm install, Node v25.9.0.Smoke method
A standalone
tsxscript imported the real exports fromcore/mcp/fetchTracking.tsand drove both the helpers directly (redactBody,redactUrlQuery,redactSensitiveHeaders) and the full tracker entry path (createFetchTracker+trackRequest/updateResponseBodywith a fakefetchthat captures exactly what was forwarded to the network). 40/40 checks passed. No Playwright — this is a pure core module with no visual surface.Real before/after output
Form-encoded OAuth token body —
code/client_secret/refresh_tokenmasked,grant_type/redirect_urikept:Nested JSON body — recursive masking of
access_token/id_token/refresh_token/password(incl. inside arrays), non-sensitive fields kept:URL query —
code/client_secretmasked, path +state/fooreadable, fragment preserved:Malformed / non-parseable inputs pass through unchanged (no throw): malformed form
%%%not-valid=&&&, malformed JSON{not json at all, empty"", and unparseable URL::::not a url::::all returned verbatim.Live request provably untouched — the fake
fetchcaptured what the tracker forwarded; only the logged entry is redacted:The live URL and body strings are
===-identical to the originals and the liveAuthorizationheader is the real bearer — redaction is confined entirely to the recorded copy.AGENTS.md compliance
Mantine: N/A — this is core (
core/mcp/), no UI surface.TypeScript: No
any, noas any, noeslint-disable/@ts-ignore/@ts-expect-errorin the diff. New helpers useunknown+Record<string, unknown>and narrow properly.Coverage (
clients/webtest:coverage, unit + integration, gate ≥90 on all four dims):core/mcp/fetchTracking.tsAll four dimensions clear the 90 gate; the full run passed (234 files / 3372 tests) with no threshold error.
Integration:
test:integration— 39 files / 809 tests passed.Header redaction from feat(core): redact sensitive headers in the fetch log #1585 intact:
SENSITIVE_HEADERSset andredactSensitiveHeaders()are unchanged and still called for both request (line 261) and response (line 328) headers; the new body/URL logic is additive (a separateSENSITIVE_BODY_FIELDSset +REDACTED_VALUEsentinel, deliberately kept distinct fromREDACTED_HEADER_VALUE). Not removed, not duplicated. The live smoke run confirmed the auth header is still masked in the logged entry.Verdict: PASS
Body + URL redaction works recursively and case-insensitively, malformed input is safe, and the live outbound request (URL, body, headers) is provably byte-identical to the original — only the logged copy is redacted. Header redaction from #1585 remains intact. Coverage and integration are green.
Smoke test script (tsx, not committed)
Automated smoke test & audit run against
775b98a.merge: #1598 redact bodies+URL query (#1593)
cliffhall commentedon Jul 2, 2026
🔗 Implemented by PR #1598, merged into the Wave 1 rollup branch
1579-wave-1. GitHub's Development panel doesn't show this link because #1598 targeted a non-default branch. Rollup: #1600 / #1601.feat(core): redact sensitive body + URL-query fields in fetch log (cl…