fix: redact URL query credentials in diagnostics

This commit is contained in:
Peter Steinberger
2026-04-27 10:55:16 +01:00
parent d33eebd050
commit e7432ae01d
3 changed files with 23 additions and 2 deletions

View File

@@ -20,7 +20,8 @@ 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 URL_PARAM_RE = /([?&])([^=&\s]+)=([^&#\s]+)/giu;
const URL_SENSITIVE_PARAM_RE =
/([?&])(api[-_]?key|apikey|access[-_]?token|auth[-_]?token|client[-_]?secret|hook[-_]?token|refresh[-_]?token|token|key|secret|password|pass|passwd|auth|signature)=([^&#\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;
@@ -306,7 +307,7 @@ function redactUrlSecretsForSupport(value: string): string {
.replace(URL_USERINFO_RE, (_match, scheme: string, _username: string, password?: string) =>
password ? `${scheme}<redacted>:<redacted>@` : `${scheme}<redacted>@`,
)
.replace(URL_PARAM_RE, (match, prefix: string, key: string) =>
.replace(URL_SENSITIVE_PARAM_RE, (match, prefix: string, key: string) =>
isSensitiveUrlQueryParamName(key) ? `${prefix}${key}=<redacted>` : match,
);
}

View File

@@ -61,6 +61,24 @@ describe("redactSensitiveText", () => {
expect(output).toBe("gog gmail watch serve --hook-token abcdef…ghij");
});
it("masks sensitive URL query parameters", () => {
const input = "connect https://user.example/sync?access_token=abcdef1234567890ghij&safe=value";
const output = redactSensitiveText(input, {
mode: "tools",
patterns: defaults,
});
expect(output).toBe("connect https://user.example/sync?access_token=abcdef…ghij&safe=value");
});
it("masks short URL query tokens fully", () => {
const input = "cdp=https://browserless.example.com/?token=supersecret123";
const output = redactSensitiveText(input, {
mode: "tools",
patterns: defaults,
});
expect(output).toBe("cdp=https://browserless.example.com/?token=***");
});
it("masks JSON fields", () => {
const input = '{"token":"abcdef1234567890ghij"}';
const output = redactSensitiveText(input, {

View File

@@ -18,6 +18,8 @@ const DEFAULT_REDACT_PATTERNS: string[] = [
String.raw`"(?:apiKey|token|secret|password|passwd|accessToken|refreshToken)"\s*:\s*"([^"]+)"`,
// CLI flags.
String.raw`--(?:api[-_]?key|hook[-_]?token|token|secret|password|passwd)\s+(["']?)([^\s"']+)\1`,
// URL query credentials.
String.raw`/([?&](?:api[-_]?key|apikey|access[-_]?token|auth[-_]?token|client[-_]?secret|hook[-_]?token|refresh[-_]?token|token|key|secret|password|pass|passwd|auth|signature)=)([^&#\s)"'<>]+)/giu`,
// Authorization headers.
String.raw`Authorization\s*[:=]\s*Bearer\s+([A-Za-z0-9._\-+=]+)`,
String.raw`\bBearer\s+([A-Za-z0-9._\-+=]{18,})\b`,