Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions review-enrichment/src/analyzers/secret-scan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
46 changes: 46 additions & 0 deletions review-enrichment/test/secret-scan.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)],
Expand Down
Loading