From b21b0faaffbd3fc0d7520e250ac51d9d9bd0ee91 Mon Sep 17 00:00:00 2001 From: bohdansolovie Date: Sun, 5 Jul 2026 02:45:11 +0200 Subject: [PATCH] feat(enrichment): detect Voyage AI and Firecrawl API keys in secret-scan Add high-confidence rules for pa-/al- (Voyage) and fc- (Firecrawl) prefixes with truncation and hyphen/underscore continuation regression tests. Co-authored-by: Cursor --- .../src/analyzers/secret-scan.ts | 12 ++++++ review-enrichment/test/secret-scan.test.ts | 38 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/review-enrichment/src/analyzers/secret-scan.ts b/review-enrichment/src/analyzers/secret-scan.ts index 8aa5463198..0b6993d0b2 100644 --- a/review-enrichment/src/analyzers/secret-scan.ts +++ b/review-enrichment/src/analyzers/secret-scan.ts @@ -235,6 +235,18 @@ const RULES: Rule[] = [ re: /\btvly-[A-Za-z0-9]{16,}(?![A-Za-z0-9_-])/, confidence: "high", }, + { + // Voyage AI API key: `pa-` (platform) or `al-` (MongoDB Atlas) + base62 body. + kind: "voyage_api_key", + re: /\b(?:pa|al)-[A-Za-z0-9]{20,}(?![A-Za-z0-9_-])/, + confidence: "high", + }, + { + // Firecrawl API key: `fc-` + base62 body (alnum only; reject hyphen-continued identifiers). + kind: "firecrawl_api_key", + re: /\bfc-[A-Za-z0-9]{16,}(?![A-Za-z0-9_-])/, + 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 83d4a5ce47..d586a78575 100644 --- a/review-enrichment/test/secret-scan.test.ts +++ b/review-enrichment/test/secret-scan.test.ts @@ -599,6 +599,44 @@ test("scanPatch does not flag malformed Pinecone/Tavily keys or identifier conti ); }); +test("scanPatch flags Voyage AI and Firecrawl API keys with high confidence", () => { + const fakeVoyagePlatform = "pa-" + "a".repeat(20); + const voyagePlatformFindings = scanPatch("src/config.ts", hunk([`const voyage = "${fakeVoyagePlatform}";`])); + assert.equal(voyagePlatformFindings.length, 1); + assert.equal(voyagePlatformFindings[0].kind, "voyage_api_key"); + assert.equal(voyagePlatformFindings[0].confidence, "high"); + + const fakeVoyageAtlas = "al-" + "b".repeat(20); + const voyageAtlasFindings = scanPatch("src/config.ts", hunk([`const atlas = "${fakeVoyageAtlas}";`])); + assert.equal(voyageAtlasFindings.length, 1); + assert.equal(voyageAtlasFindings[0].kind, "voyage_api_key"); + assert.equal(voyageAtlasFindings[0].confidence, "high"); + + const fakeFirecrawlKey = "fc-" + "c".repeat(16); + const firecrawlFindings = scanPatch("src/config.ts", hunk([`const firecrawl = "${fakeFirecrawlKey}";`])); + assert.equal(firecrawlFindings.length, 1); + assert.equal(firecrawlFindings[0].kind, "firecrawl_api_key"); + assert.equal(firecrawlFindings[0].confidence, "high"); +}); + +test("scanPatch does not flag truncated Voyage/Firecrawl keys or identifier continuation", () => { + assert.equal(scanPatch("src/config.ts", hunk([`const voyage = "pa-${"a".repeat(19)}";`])).length, 0); + assert.equal( + scanPatch("src/config.ts", hunk([`const voyage = "pa-${"a".repeat(20)}-suffix";`])).some((f) => f.kind === "voyage_api_key"), + false, + ); + assert.equal( + scanPatch("src/config.ts", hunk([`const voyage = "al-${"b".repeat(20)}_suffix";`])).some((f) => f.kind === "voyage_api_key"), + false, + ); + + assert.equal(scanPatch("src/config.ts", hunk([`const firecrawl = "fc-${"c".repeat(15)}";`])).length, 0); + assert.equal( + scanPatch("src/config.ts", hunk([`const firecrawl = "fc-${"c".repeat(16)}-suffix";`])).some((f) => f.kind === "firecrawl_api_key"), + false, + ); +}); + test("scanPatch flags additional high-confidence SaaS/cloud/CI credential formats", () => { const cases = [ ["google_oauth_client_secret", "GOCSPX-" + b62(28)],