Skip to content
Merged
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
85 changes: 85 additions & 0 deletions review-enrichment/src/analyzers/secret-scan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,91 @@ const RULES: Rule[] = [
re: /\bfo1_[A-Za-z0-9_-]{43}\b/,
confidence: "high",
},
{
// Dropbox short-lived access token: `sl.` + 130-152 base64url chars. A negative-lookahead terminator
// (not `\b`) so a body ending in `-` still matches, like the SendGrid/Anthropic rules above.
kind: "dropbox_token",
re: /\bsl\.[A-Za-z0-9_-]{130,152}(?![A-Za-z0-9_-])/,
confidence: "high",
},
{
// JFrog Artifactory API key: `AKCp8` + >=69 base62 (lookahead terminator — the body has no fixed end).
kind: "jfrog_api_key",
re: /\bAKCp8[A-Za-z0-9]{69,}(?![A-Za-z0-9])/,
confidence: "high",
},
{
// Duffel API token: `duffel_test_`/`duffel_live_` + 43 base64url (lookahead terminator — the body may end in `-`).
kind: "duffel_token",
re: /\bduffel_(?:test|live)_[A-Za-z0-9_-]{43}(?![A-Za-z0-9_-])/,
confidence: "high",
},
{
// EasyPost API key: `EZAK` (production) / `EZTK` (test) + 54 base62.
kind: "easypost_key",
re: /\bEZ[AT]K[A-Za-z0-9]{54}\b/,
confidence: "high",
},
{
// Frame.io developer token: `fio-u-` + 64 base64url (lookahead terminator — the body may end in `-`).
kind: "frameio_token",
re: /\bfio-u-[A-Za-z0-9_-]{64}(?![A-Za-z0-9_-])/,
confidence: "high",
},
{
// Contentful personal access token: `CFPAT-` + 43 base64url (lookahead terminator — the body may end in `-`).
kind: "contentful_token",
re: /\bCFPAT-[A-Za-z0-9_-]{43}(?![A-Za-z0-9_-])/,
confidence: "high",
},
{
// SonarQube token: `sqa_`/`sqp_`/`squ_` (analysis/project/user) + 40 hex.
kind: "sonarqube_token",
re: /\bsq[apu]_[a-f0-9]{40}\b/,
confidence: "high",
},
{
// Pulumi access token: `pul-` + 40 hex.
kind: "pulumi_token",
re: /\bpul-[a-f0-9]{40}\b/,
confidence: "high",
},
{
// Adafruit IO key: `aio_` + 28 base62.
kind: "adafruit_io_key",
re: /\baio_[A-Za-z0-9]{28}\b/,
confidence: "high",
},
{
// ReadMe API key: `rdme_` + >=70 lowercase-hex-ish body (lookahead terminator).
kind: "readme_api_key",
re: /\brdme_[a-z0-9]{70,}(?![a-z0-9])/,
confidence: "high",
},
{
// Typeform personal access token: `tfp_` + >=40 base62/._- (lookahead terminator).
kind: "typeform_token",
re: /\btfp_[A-Za-z0-9._-]{40,}(?![A-Za-z0-9._-])/,
confidence: "high",
},
{
// Sentry DSN: an ingest URL embedding a 32-hex public key against a *.sentry.io host + project id.
kind: "sentry_dsn",
re: /\bhttps:\/\/[a-f0-9]{32}@[a-z0-9.-]*sentry\.io\/[0-9]+\b/,
confidence: "high",
},
{
// Groq API key: `gsk_` + 52 base62.
kind: "groq_api_key",
re: /\bgsk_[A-Za-z0-9]{52}\b/,
confidence: "high",
},
{
// Perplexity API key: `pplx-` + >=40 base62 (lookahead terminator).
kind: "perplexity_api_key",
re: /\bpplx-[A-Za-z0-9]{40,}(?![A-Za-z0-9])/,
confidence: "high",
},
{
kind: "private_key",
re: /-----BEGIN (?:RSA |EC |OPENSSH |DSA |PGP )?PRIVATE KEY-----/,
Expand Down
45 changes: 45 additions & 0 deletions review-enrichment/test/secret-scan.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -442,3 +442,48 @@ test("scanPatch does not flag near-miss variants of the new SaaS/cloud credentia
assert.equal(findings.length, 0, `near-miss should not match: ${nm}`);
}
});

test("scanPatch flags additional high-confidence AI-provider and SaaS/CI credential formats", () => {
const cases = [
// The base64url-body formats deliberately END IN `-` to prove the rule terminates on a
// negative-lookahead, not `\b` (a `\b` terminator silently misses a real token ending in `-`).
["dropbox_token", "sl." + b62(139) + "-"],
["jfrog_api_key", "AKCp8" + b62(70)],
["duffel_token", "duffel_test_" + b62(42) + "-"],
["easypost_key", "EZAK" + b62(54)],
["frameio_token", "fio-u-" + b62(63) + "-"],
["contentful_token", "CFPAT-" + b62(42) + "-"],
["sonarqube_token", "sqp_" + hex(40)],
["pulumi_token", "pul-" + hex(40)],
["adafruit_io_key", "aio_" + b62(28)],
["readme_api_key", "rdme_" + hex(70)],
["typeform_token", "tfp_" + b62(40)],
["sentry_dsn", "https://" + hex(32) + "@o0.ingest.sentry.io/12345"],
["groq_api_key", "gsk_" + b62(52)],
["perplexity_api_key", "pplx-" + b62(40)],
];
for (const [kind, secret] of cases) {
const findings = scanPatch("src/config.ts", hunk([`const c = "${secret}";`]));
assert.equal(findings.length, 1, `${kind}: expected exactly one finding, got ${JSON.stringify(findings)}`);
assert.equal(findings[0].kind, kind, `${kind}: wrong kind`);
assert.equal(findings[0].confidence, "high", `${kind}: wrong confidence`);
}
});

test("scanPatch does not flag near-miss variants of the new AI/SaaS credential formats", () => {
// One char short of the fixed/minimum length must produce no finding.
const nearMisses = [
"AKCp8" + b62(68),
"duffel_test_" + b62(42),
"EZAK" + b62(53),
"CFPAT-" + b62(42),
"pul-" + hex(39),
"aio_" + b62(27),
"gsk_" + b62(51),
"sqp_" + hex(39),
];
for (const nm of nearMisses) {
const findings = scanPatch("src/config.ts", hunk([`const c = "${nm}";`]));
assert.equal(findings.length, 0, `near-miss should not match: ${nm}`);
}
});
Loading