From f791e3a2bd2419339636ddb47b061025df77b434 Mon Sep 17 00:00:00 2001 From: bohdansolovie Date: Sun, 5 Jul 2026 01:39:55 +0200 Subject: [PATCH] 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)],