diff --git a/review-enrichment/src/analyzers/secret-scan.ts b/review-enrichment/src/analyzers/secret-scan.ts index fbe56800d7..2f5522265f 100644 --- a/review-enrichment/src/analyzers/secret-scan.ts +++ b/review-enrichment/src/analyzers/secret-scan.ts @@ -187,6 +187,18 @@ const RULES: Rule[] = [ re: /\bSK[0-9a-fA-F]{32}(?![A-Za-z0-9_])/, confidence: "high", }, + { + // Resend API key: `re_` + >=24 base62 chars. + kind: "resend_api_key", + re: /\bre_[A-Za-z0-9]{24,}(?![A-Za-z0-9])/, + confidence: "high", + }, + { + // Mapbox secret access token: `sk.eyJ` + base64url body (distinct from Stripe `sk_live_` / `sk_test_`). + kind: "mapbox_secret_token", + re: /\bsk\.eyJ[A-Za-z0-9_-]{24,}(?![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 13fcff694c..972850f755 100644 --- a/review-enrichment/test/secret-scan.test.ts +++ b/review-enrichment/test/secret-scan.test.ts @@ -471,6 +471,40 @@ test("scanPatch does not flag truncated Twilio SIDs or identifier continuation p ); }); +test("scanPatch flags Resend and Mapbox secret tokens with high confidence", () => { + const fakeResendKey = "re_" + "a".repeat(32); + const resendFindings = scanPatch("src/config.ts", hunk([`const resend = "${fakeResendKey}";`])); + assert.equal(resendFindings.length, 1); + assert.equal(resendFindings[0].kind, "resend_api_key"); + assert.equal(resendFindings[0].confidence, "high"); + + const fakeMapboxSecret = ["sk.", "eyJ", "a".repeat(24)].join(""); + const mapboxFindings = scanPatch("src/config.ts", hunk([`const mapbox = "${fakeMapboxSecret}";`])); + assert.equal(mapboxFindings.length, 1); + assert.equal(mapboxFindings[0].kind, "mapbox_secret_token"); + assert.equal(mapboxFindings[0].confidence, "high"); +}); + +test("scanPatch does not flag truncated Resend keys or classify Mapbox secrets as Stripe keys", () => { + const truncatedResend = "re_" + "a".repeat(23); + assert.equal(scanPatch("src/config.ts", hunk([`const resend = "${truncatedResend}";`])).length, 0); + + const fakeMapboxSecret = ["sk.", "eyJ", "c".repeat(24)].join(""); + const findings = scanPatch("src/config.ts", hunk([`const mapbox = "${fakeMapboxSecret}";`])); + assert.equal(findings.some((f) => f.kind === "stripe_secret_key"), false); + assert.equal(findings.some((f) => f.kind === "mapbox_secret_token"), true); +}); + +test("scanPatch does not flag truncated Mapbox secrets or public pk tokens", () => { + const truncated = ["sk.", "eyJ", "a".repeat(23)].join(""); + assert.equal(scanPatch("src/config.ts", hunk([`const mapbox = "${truncated}";`])).length, 0); + const publicToken = ["pk.", "eyJ", "b".repeat(24)].join(""); + assert.equal( + scanPatch("src/config.ts", hunk([`const mapbox = "${publicToken}";`])).some((f) => f.kind === "mapbox_secret_token"), + false, + ); +}); + test("scanPatch flags additional high-confidence SaaS/cloud/CI credential formats", () => { const cases = [ ["google_oauth_client_secret", "GOCSPX-" + b62(28)],