From f791e3a2bd2419339636ddb47b061025df77b434 Mon Sep 17 00:00:00 2001 From: bohdansolovie Date: Sun, 5 Jul 2026 01:39:55 +0200 Subject: [PATCH 1/2] feat(enrichment): detect Discord bot tokens and Twilio SIDs in secret-scan Add high-confidence patterns for Discord bot tokens and Twilio Account/API Key SIDs with fragment-based fixtures, truncation negatives, and webhook parity. Co-authored-by: Cursor --- .../src/analyzers/secret-scan.ts | 18 ++++++++ review-enrichment/test/secret-scan.test.ts | 46 +++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/review-enrichment/src/analyzers/secret-scan.ts b/review-enrichment/src/analyzers/secret-scan.ts index d7cc50c61b..af0047e320 100644 --- a/review-enrichment/src/analyzers/secret-scan.ts +++ b/review-enrichment/src/analyzers/secret-scan.ts @@ -169,6 +169,24 @@ const RULES: Rule[] = [ re: /\bkey-[0-9A-Za-z]{32}\b/, confidence: "high", }, + { + // Discord bot token: `[MNO]` + 23 base64url chars, `.`, 6-char segment, `.`, 27-char segment. + kind: "discord_bot_token", + re: /\b[MNO][A-Za-z0-9_-]{23}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27}(?![A-Za-z0-9_-])/, + confidence: "high", + }, + { + // Twilio Account SID: `AC` + 32 hex chars (distinct from Auth Token, which has no prefix). + kind: "twilio_account_sid", + re: /\bAC[0-9a-fA-F]{32}(?![0-9a-fA-F])/, + confidence: "high", + }, + { + // Twilio API Key SID: `SK` + 32 hex chars. + kind: "twilio_api_key_sid", + re: /\bSK[0-9a-fA-F]{32}(?![0-9a-fA-F])/, + confidence: "high", + }, { // Google OAuth 2.0 client secret: `GOCSPX-` + 28 base64url chars. kind: "google_oauth_client_secret", diff --git a/review-enrichment/test/secret-scan.test.ts b/review-enrichment/test/secret-scan.test.ts index 7f0be87c5c..d884144f39 100644 --- a/review-enrichment/test/secret-scan.test.ts +++ b/review-enrichment/test/secret-scan.test.ts @@ -415,6 +415,52 @@ test("scanPatch does not flag a Mailgun-shaped key with an invalid body characte assert.equal(findings.length, 0); }); +test("scanPatch flags a Discord bot token with high confidence", () => { + const fakeDiscordBotToken = ["M", "A".repeat(23), ".", "b".repeat(6), ".", "c".repeat(27)].join(""); + const findings = scanPatch("src/config.ts", hunk([`const discord = "${fakeDiscordBotToken}";`])); + assert.equal(findings.length, 1); + assert.equal(findings[0].kind, "discord_bot_token"); + assert.equal(findings[0].confidence, "high"); +}); + +test("scanPatch does not flag a truncated Discord bot token", () => { + const truncated = ["M", "A".repeat(23), ".", "b".repeat(6), ".", "c".repeat(26)].join(""); + const findings = scanPatch("src/config.ts", hunk([`const discord = "${truncated}";`])); + assert.equal(findings.length, 0); +}); + +test("scanPatch does not classify a Discord bot token as a webhook URL", () => { + const fakeDiscordBotToken = ["N", "B".repeat(23), ".", "d".repeat(6), ".", "e".repeat(27)].join(""); + const findings = scanPatch("src/config.ts", hunk([`const discord = "${fakeDiscordBotToken}";`])); + assert.equal(findings.length, 1); + assert.equal(findings[0].kind, "discord_bot_token"); + assert.equal(findings.some((f) => f.kind === "discord_webhook_url"), false); +}); + +test("scanPatch flags Twilio Account and API Key SIDs with high confidence", () => { + const fakeTwilioAccountSid = "AC" + "a".repeat(32); + const fakeTwilioApiKeySid = "SK" + "b".repeat(32); + const accountFindings = scanPatch("src/config.ts", hunk([`const sid = "${fakeTwilioAccountSid}";`])); + assert.equal(accountFindings.length, 1); + assert.equal(accountFindings[0].kind, "twilio_account_sid"); + assert.equal(accountFindings[0].confidence, "high"); + + const keyFindings = scanPatch("src/config.ts", hunk([`const apiKey = "${fakeTwilioApiKeySid}";`])); + assert.equal(keyFindings.length, 1); + assert.equal(keyFindings[0].kind, "twilio_api_key_sid"); + assert.equal(keyFindings[0].confidence, "high"); +}); + +test("scanPatch does not flag truncated Twilio SIDs or hex overrun", () => { + const truncated = "AC" + "a".repeat(31); + assert.equal(scanPatch("src/config.ts", hunk([`const sid = "${truncated}";`])).length, 0); + const overrun = "AC" + "a".repeat(32) + "f"; + assert.equal( + scanPatch("src/config.ts", hunk([`const sid = "${overrun}";`])).some((f) => f.kind === "twilio_account_sid"), + false, + ); +}); + test("scanPatch flags additional high-confidence SaaS/cloud/CI credential formats", () => { const cases = [ ["google_oauth_client_secret", "GOCSPX-" + b62(28)], From cc9f92ac6b913b6f07771d4b2c3751fe0bb0a55e Mon Sep 17 00:00:00 2001 From: bohdansolovie Date: Sun, 5 Jul 2026 01:45:55 +0200 Subject: [PATCH 2/2] fix(enrichment): tighten Twilio SID tail boundary in secret-scan Use identifier-continuation lookahead so AC/SK + 32 hex is not matched when immediately followed by a non-hex identifier char (Orb #3262). Co-authored-by: Cursor --- review-enrichment/src/analyzers/secret-scan.ts | 4 ++-- review-enrichment/test/secret-scan.test.ts | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/review-enrichment/src/analyzers/secret-scan.ts b/review-enrichment/src/analyzers/secret-scan.ts index af0047e320..fbe56800d7 100644 --- a/review-enrichment/src/analyzers/secret-scan.ts +++ b/review-enrichment/src/analyzers/secret-scan.ts @@ -178,13 +178,13 @@ const RULES: Rule[] = [ { // Twilio Account SID: `AC` + 32 hex chars (distinct from Auth Token, which has no prefix). kind: "twilio_account_sid", - re: /\bAC[0-9a-fA-F]{32}(?![0-9a-fA-F])/, + re: /\bAC[0-9a-fA-F]{32}(?![A-Za-z0-9_])/, confidence: "high", }, { // Twilio API Key SID: `SK` + 32 hex chars. kind: "twilio_api_key_sid", - re: /\bSK[0-9a-fA-F]{32}(?![0-9a-fA-F])/, + re: /\bSK[0-9a-fA-F]{32}(?![A-Za-z0-9_])/, confidence: "high", }, { diff --git a/review-enrichment/test/secret-scan.test.ts b/review-enrichment/test/secret-scan.test.ts index d884144f39..13fcff694c 100644 --- a/review-enrichment/test/secret-scan.test.ts +++ b/review-enrichment/test/secret-scan.test.ts @@ -451,12 +451,22 @@ test("scanPatch flags Twilio Account and API Key SIDs with high confidence", () assert.equal(keyFindings[0].confidence, "high"); }); -test("scanPatch does not flag truncated Twilio SIDs or hex overrun", () => { +test("scanPatch does not flag truncated Twilio SIDs or identifier continuation past 32 hex chars", () => { const truncated = "AC" + "a".repeat(31); assert.equal(scanPatch("src/config.ts", hunk([`const sid = "${truncated}";`])).length, 0); - const overrun = "AC" + "a".repeat(32) + "f"; + const hexOverrun = "AC" + "a".repeat(32) + "f"; assert.equal( - scanPatch("src/config.ts", hunk([`const sid = "${overrun}";`])).some((f) => f.kind === "twilio_account_sid"), + scanPatch("src/config.ts", hunk([`const sid = "${hexOverrun}";`])).some((f) => f.kind === "twilio_account_sid"), + false, + ); + const nonHexTail = "AC" + "a".repeat(32) + "z"; + assert.equal( + scanPatch("src/config.ts", hunk([`const sid = "${nonHexTail}";`])).some((f) => f.kind === "twilio_account_sid"), + false, + ); + const skNonHexTail = "SK" + "b".repeat(32) + "z"; + assert.equal( + scanPatch("src/config.ts", hunk([`const key = "${skNonHexTail}";`])).some((f) => f.kind === "twilio_api_key_sid"), false, ); });