Skip to content

Commit b56bad3

Browse files
authored
fix(logging): redact Telegram tokens across chunk boundaries (#103861)
1 parent eb7613c commit b56bad3

2 files changed

Lines changed: 29 additions & 3 deletions

File tree

src/logging/redact.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,24 @@ describe("redactSensitiveText", () => {
955955
}
956956
});
957957

958+
it("masks Telegram bot tokens that cross bounded-replacement chunk boundaries", () => {
959+
const chunkSize = 16_384;
960+
const credential = `123456:${"A".repeat(28)}WXYZ`;
961+
const cases = [
962+
{ token: `bot${credential}`, redacted: "bot123456…WXYZ" },
963+
{ token: credential, redacted: "123456…WXYZ" },
964+
];
965+
966+
for (const { token, redacted } of cases) {
967+
const tokenStart = chunkSize - 12;
968+
const prefix = `${"x".repeat(tokenStart - 1)} `;
969+
const suffix = ` ${"y".repeat(chunkSize * 2)}`;
970+
expect(redactSensitiveText(`${prefix}${token}${suffix}`, { mode: "tools" })).toBe(
971+
`${prefix}${redacted}${suffix}`,
972+
);
973+
}
974+
});
975+
958976
it("does not corrupt base64 blobs that embed token-prefix shapes", () => {
959977
// Tiny-PNG base64 contains a gAAAA run from zero-filled IHDR bytes; pure-base64-alphabet
960978
// prefixes must not fire mid-blob or media payloads get mangled.

src/logging/redact.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,18 @@ const STANDALONE_ASSIGNMENT_REDACT_PATTERN = String.raw`(^|[\s,;])(?:${STANDALON
127127
// data-URL media is never corrupted while tokens in URL paths or assignments still redact.
128128
const BASE64_SAFE_TOKEN_BOUNDARY = String.raw`(^|[^A-Za-z0-9])(?<!;base64,[A-Za-z0-9+/=]*)`;
129129
const IDENTIFIER_SAFE_TOKEN_BOUNDARY = String.raw`(^|[^A-Za-z0-9_])`;
130+
const TELEGRAM_BOT_TOKEN_REDACT_PATTERN = String.raw`\bbot(\d{6,}:[A-Za-z0-9_-]{20,})\b`;
131+
const TELEGRAM_TOKEN_REDACT_PATTERN = String.raw`\b(\d{6,}:[A-Za-z0-9_-]{20,})\b`;
130132
const SHELL_REFERENCE_PRESERVING_PATTERN_SOURCES = new Set([
131133
ENV_ASSIGNMENT_REDACT_PATTERN,
132134
ESCAPED_ENV_ASSIGNMENT_REDACT_PATTERN,
133135
STANDALONE_ASSIGNMENT_QUOTED_REDACT_PATTERN,
134136
STANDALONE_ASSIGNMENT_REDACT_PATTERN,
135137
]);
138+
const CHUNK_UNSAFE_PATTERN_SOURCES = new Set([
139+
TELEGRAM_BOT_TOKEN_REDACT_PATTERN,
140+
TELEGRAM_TOKEN_REDACT_PATTERN,
141+
]);
136142
const shellReferencePreservingPatterns = new WeakSet<RegExp>();
137143
// Patterns whose left-context assertions or complete token can cross a chunk boundary must run
138144
// against the full string; chunking can invent a `^` boundary or split the secret itself.
@@ -251,8 +257,8 @@ const DEFAULT_REDACT_PATTERNS: string[] = [
251257
String.raw`(api_org_[A-Za-z0-9]{20,})`,
252258
String.raw`(r8_[A-Za-z0-9]{10,})`,
253259
// Telegram Bot API URLs embed the token as `/bot<token>/...` (no word-boundary before digits).
254-
String.raw`\bbot(\d{6,}:[A-Za-z0-9_-]{20,})\b`,
255-
String.raw`\b(\d{6,}:[A-Za-z0-9_-]{20,})\b`,
260+
TELEGRAM_BOT_TOKEN_REDACT_PATTERN,
261+
TELEGRAM_TOKEN_REDACT_PATTERN,
256262
];
257263
let defaultResolvedPatterns: RegExp[] | undefined;
258264

@@ -323,7 +329,9 @@ function parsePattern(raw: RedactPattern): RegExp | null {
323329
if (
324330
pattern &&
325331
typeof raw === "string" &&
326-
(raw.startsWith(BASE64_SAFE_TOKEN_BOUNDARY) || raw.startsWith(IDENTIFIER_SAFE_TOKEN_BOUNDARY))
332+
(raw.startsWith(BASE64_SAFE_TOKEN_BOUNDARY) ||
333+
raw.startsWith(IDENTIFIER_SAFE_TOKEN_BOUNDARY) ||
334+
CHUNK_UNSAFE_PATTERN_SOURCES.has(raw))
327335
) {
328336
chunkUnsafePatterns.add(pattern);
329337
}

0 commit comments

Comments
 (0)