From 10f128413dd71f19984ef9c089bed6b67db01481 Mon Sep 17 00:00:00 2001 From: Gustavo Madeira Santana Date: Wed, 22 Apr 2026 17:29:39 -0400 Subject: [PATCH] refactor: reuse support redaction classifiers --- src/logging/diagnostic-support-export.test.ts | 8 ++++++++ src/logging/diagnostic-support-redaction.ts | 13 ++++++------- src/shared/net/redact-sensitive-url.test.ts | 3 +++ src/shared/net/redact-sensitive-url.ts | 6 +++++- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/logging/diagnostic-support-export.test.ts b/src/logging/diagnostic-support-export.test.ts index af7300e0c70..ae5efe11596 100644 --- a/src/logging/diagnostic-support-export.test.ts +++ b/src/logging/diagnostic-support-export.test.ts @@ -384,6 +384,14 @@ describe("diagnostic support export", () => { "connect wss://support-user:support-password@gateway.example/ws?token=short-token&ok=1", "connect wss://:@gateway.example/ws?token=", ], + [ + "connect https://gateway.example/ws?access-token=short-token", + "connect https://gateway.example/ws?access-token=", + ], + [ + "connect https://gateway.example/ws?hook-token=hook-secret", + "connect https://gateway.example/ws?hook-token=", + ], ["connect https://token@gateway.example/ws", "connect https://@gateway.example/ws"], ["auth Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==", "auth Basic "], ["Cookie: sid=secret; theme=light", "Cookie: "], diff --git a/src/logging/diagnostic-support-redaction.ts b/src/logging/diagnostic-support-redaction.ts index f4699dea4bb..13814ba7161 100644 --- a/src/logging/diagnostic-support-redaction.ts +++ b/src/logging/diagnostic-support-redaction.ts @@ -1,4 +1,6 @@ import path from "node:path"; +import { isSecretRefShape } from "../config/redact-snapshot.secret-ref.js"; +import { isSensitiveUrlQueryParamName } from "../shared/net/redact-sensitive-url.js"; import { redactSensitiveText } from "./redact.js"; const SECRET_SUPPORT_FIELD_RE = @@ -17,8 +19,7 @@ const COOKIE_HEADER_RE = /\b(Cookie|Set-Cookie)\s*:\s*[^\r\n]+/giu; const AWS_ACCESS_KEY_ID_RE = /\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/gu; const JWT_RE = /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/gu; const URL_USERINFO_RE = /\b([a-z][a-z0-9+.-]*:\/\/)([^/@\s:?#]+)(?::([^/@\s?#]+))?@/giu; -const SENSITIVE_URL_PARAM_RE = - /([?&](?:api[-_]?key|access[-_]?token|auth[-_]?token|hook[-_]?token|password|passwd|refresh[-_]?token|secret|token)=)[^&#\s]+/giu; +const URL_PARAM_RE = /([?&])([^=&\s]+)=([^&#\s]+)/giu; const EMAIL_RE = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/giu; const MATRIX_USER_ID_RE = /@[A-Za-z0-9._=-]+:[A-Za-z0-9.-]+/gu; const MATRIX_ROOM_ID_RE = /![A-Za-z0-9._=-]+:[A-Za-z0-9.-]+/gu; @@ -58,10 +59,6 @@ function isPrivateConfigField(key: string): boolean { return isPrivateSupportField(key) || CONFIG_PRIVATE_FIELD_RE.test(key); } -function isSecretRefShape(value: Record): boolean { - return typeof value.source === "string" && typeof value.id === "string"; -} - function sanitizeSecretRefForSupport(value: Record): Record { const sanitized: Record = {}; if (typeof value.source === "string") { @@ -143,7 +140,9 @@ function redactUrlSecretsForSupport(value: string): string { .replace(URL_USERINFO_RE, (_match, scheme: string, _username: string, password?: string) => password ? `${scheme}:@` : `${scheme}@`, ) - .replace(SENSITIVE_URL_PARAM_RE, "$1"); + .replace(URL_PARAM_RE, (match, prefix: string, key: string) => + isSensitiveUrlQueryParamName(key) ? `${prefix}${key}=` : match, + ); } function redactContactIdentifiersForSupport(value: string): string { diff --git a/src/shared/net/redact-sensitive-url.test.ts b/src/shared/net/redact-sensitive-url.test.ts index 3a092fab090..2e376349a64 100644 --- a/src/shared/net/redact-sensitive-url.test.ts +++ b/src/shared/net/redact-sensitive-url.test.ts @@ -48,6 +48,9 @@ describe("isSensitiveUrlQueryParamName", () => { it("matches the auth-oriented query params used by MCP SSE config redaction", () => { expect(isSensitiveUrlQueryParamName("token")).toBe(true); expect(isSensitiveUrlQueryParamName("refresh_token")).toBe(true); + expect(isSensitiveUrlQueryParamName("access-token")).toBe(true); + expect(isSensitiveUrlQueryParamName("hook-token")).toBe(true); + expect(isSensitiveUrlQueryParamName("passwd")).toBe(true); expect(isSensitiveUrlQueryParamName("signature")).toBe(true); expect(isSensitiveUrlQueryParamName("safe")).toBe(false); }); diff --git a/src/shared/net/redact-sensitive-url.ts b/src/shared/net/redact-sensitive-url.ts index 090a4ee3200..9596517fd80 100644 --- a/src/shared/net/redact-sensitive-url.ts +++ b/src/shared/net/redact-sensitive-url.ts @@ -10,16 +10,20 @@ const SENSITIVE_URL_QUERY_PARAM_NAMES = new Set([ "apikey", "secret", "access_token", + "auth_token", "password", "pass", + "passwd", "auth", "client_secret", + "hook_token", "refresh_token", "signature", ]); export function isSensitiveUrlQueryParamName(name: string): boolean { - return SENSITIVE_URL_QUERY_PARAM_NAMES.has(normalizeLowercaseStringOrEmpty(name)); + const normalized = normalizeLowercaseStringOrEmpty(name).replaceAll("-", "_"); + return SENSITIVE_URL_QUERY_PARAM_NAMES.has(normalized); } export function isSensitiveUrlConfigPath(path: string): boolean {