diff --git a/src/logging/redact.test.ts b/src/logging/redact.test.ts index ce6654cc666e..9de8cec565bc 100644 --- a/src/logging/redact.test.ts +++ b/src/logging/redact.test.ts @@ -955,6 +955,24 @@ describe("redactSensitiveText", () => { } }); + it("masks Telegram bot tokens that cross bounded-replacement chunk boundaries", () => { + const chunkSize = 16_384; + const credential = `123456:${"A".repeat(28)}WXYZ`; + const cases = [ + { token: `bot${credential}`, redacted: "bot123456…WXYZ" }, + { token: credential, redacted: "123456…WXYZ" }, + ]; + + for (const { token, redacted } of cases) { + const tokenStart = chunkSize - 12; + const prefix = `${"x".repeat(tokenStart - 1)} `; + const suffix = ` ${"y".repeat(chunkSize * 2)}`; + expect(redactSensitiveText(`${prefix}${token}${suffix}`, { mode: "tools" })).toBe( + `${prefix}${redacted}${suffix}`, + ); + } + }); + it("does not corrupt base64 blobs that embed token-prefix shapes", () => { // Tiny-PNG base64 contains a gAAAA run from zero-filled IHDR bytes; pure-base64-alphabet // prefixes must not fire mid-blob or media payloads get mangled. diff --git a/src/logging/redact.ts b/src/logging/redact.ts index 5a53bda1b743..93c7f5fbad42 100644 --- a/src/logging/redact.ts +++ b/src/logging/redact.ts @@ -127,12 +127,18 @@ const STANDALONE_ASSIGNMENT_REDACT_PATTERN = String.raw`(^|[\s,;])(?:${STANDALON // data-URL media is never corrupted while tokens in URL paths or assignments still redact. const BASE64_SAFE_TOKEN_BOUNDARY = String.raw`(^|[^A-Za-z0-9])(?(); // Patterns whose left-context assertions or complete token can cross a chunk boundary must run // against the full string; chunking can invent a `^` boundary or split the secret itself. @@ -251,8 +257,8 @@ const DEFAULT_REDACT_PATTERNS: string[] = [ String.raw`(api_org_[A-Za-z0-9]{20,})`, String.raw`(r8_[A-Za-z0-9]{10,})`, // Telegram Bot API URLs embed the token as `/bot/...` (no word-boundary before digits). - String.raw`\bbot(\d{6,}:[A-Za-z0-9_-]{20,})\b`, - String.raw`\b(\d{6,}:[A-Za-z0-9_-]{20,})\b`, + TELEGRAM_BOT_TOKEN_REDACT_PATTERN, + TELEGRAM_TOKEN_REDACT_PATTERN, ]; let defaultResolvedPatterns: RegExp[] | undefined; @@ -323,7 +329,9 @@ function parsePattern(raw: RedactPattern): RegExp | null { if ( pattern && typeof raw === "string" && - (raw.startsWith(BASE64_SAFE_TOKEN_BOUNDARY) || raw.startsWith(IDENTIFIER_SAFE_TOKEN_BOUNDARY)) + (raw.startsWith(BASE64_SAFE_TOKEN_BOUNDARY) || + raw.startsWith(IDENTIFIER_SAFE_TOKEN_BOUNDARY) || + CHUNK_UNSAFE_PATTERN_SOURCES.has(raw)) ) { chunkUnsafePatterns.add(pattern); }