From a2bcf50f49af5a35da081c7b3d9e78b0c6275f0f Mon Sep 17 00:00:00 2001 From: luciferlive112116 <291889058+luciferlive112116@users.noreply.github.com> Date: Sun, 5 Jul 2026 17:39:44 +0800 Subject: [PATCH] feat(enrichment): flag managed-database connection strings in secret-scan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add six high-confidence RULES for managed-database connection strings that embed credentials, following the existing cloudinary_url rule (scheme://:@) — a committed connection string with an embedded password is a high-severity credential leak. - mongodb_atlas_uri: mongodb+srv://:@...mongodb.net - neon_postgres_uri: postgres://:@...neon.tech - supabase_postgres_uri: postgres://:@...supabase.co - upstash_redis_uri: rediss://default:@...upstash.io - planetscale_mysql_uri: mysql://:@...psdb.cloud - cockroachdb_uri: postgres://:@...cockroachlabs.cloud Each rule requires a non-empty user:password pair (charset excludes < and >, so an angle-bracket docs placeholder does not match), a distinctive managed-provider host (so a local/self-hosted URI is never flagged), and a host-boundary terminator (so a look-alike suffix host cannot match). The negative test asserts placeholders, localhost/127.0.0.1 hosts, and a suffix-host look-alike all produce zero findings. All kinds are new and inserted before the generic-assignment rule. SecretFinding.kind is a plain string, so there is no types/render/metadata change. --- .../src/analyzers/secret-scan.ts | 41 +++++++++++++++++++ review-enrichment/test/secret-scan.test.ts | 38 +++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/review-enrichment/src/analyzers/secret-scan.ts b/review-enrichment/src/analyzers/secret-scan.ts index 1f011c2a98..2e491ce07e 100644 --- a/review-enrichment/src/analyzers/secret-scan.ts +++ b/review-enrichment/src/analyzers/secret-scan.ts @@ -955,6 +955,47 @@ const RULES: Rule[] = [ re: /https:\/\/chat\.googleapis\.com\/v1\/spaces\/[A-Za-z0-9_-]+\/messages\?[^\s"']*key=/, confidence: "high", }, + { + // Managed-database connection strings that embed credentials, like the `cloudinary_url` rule above: + // `scheme://:@`. Each requires a NON-empty user:password pair (so an angle-bracket + // `:` docs placeholder does not match — `<`/`>` are excluded) AND a distinctive provider + // host, so an ordinary `postgres://user:pass@localhost` (no managed host) is never flagged. The host is + // followed by a negative lookahead so a look-alike suffix host (`…mongodb.net.evil.com`) can't match. + kind: "mongodb_atlas_uri", + re: /\bmongodb(?:\+srv)?:\/\/[^\s:/<>]+:[^\s@/<>]+@[a-z0-9.-]+\.mongodb\.net(?![a-z0-9.-])/i, + confidence: "high", + }, + { + // Neon serverless-Postgres connection string (`…@.neon.tech`). + kind: "neon_postgres_uri", + re: /\bpostgres(?:ql)?:\/\/[^\s:/<>]+:[^\s@/<>]+@[a-z0-9.-]+\.neon\.tech(?![a-z0-9.-])/i, + confidence: "high", + }, + { + // Supabase Postgres connection string (`…@db..supabase.co`). + kind: "supabase_postgres_uri", + re: /\bpostgres(?:ql)?:\/\/[^\s:/<>]+:[^\s@/<>]+@[a-z0-9.-]+\.supabase\.co(?![a-z0-9.-])/i, + confidence: "high", + }, + { + // Upstash Redis connection string (`rediss://default:@.upstash.io`). Upstash uses the + // default user, so the user segment may be empty. + kind: "upstash_redis_uri", + re: /\brediss?:\/\/[^\s:/<>]*:[^\s@/<>]+@[a-z0-9.-]+\.upstash\.io(?![a-z0-9.-])/i, + confidence: "high", + }, + { + // PlanetScale MySQL connection string (`…@.connect.psdb.cloud`). + kind: "planetscale_mysql_uri", + re: /\bmysql:\/\/[^\s:/<>]+:[^\s@/<>]+@[a-z0-9.-]+\.psdb\.cloud(?![a-z0-9.-])/i, + confidence: "high", + }, + { + // CockroachDB Cloud connection string (`…@.cockroachlabs.cloud`). + kind: "cockroachdb_uri", + re: /\bpostgres(?:ql)?:\/\/[^\s:/<>]+:[^\s@/<>]+@[a-z0-9.-]+\.cockroachlabs\.cloud(?![a-z0-9.-])/i, + confidence: "high", + }, { // Netlify build-hook URL — the trailing id triggers a production build, like the webhook rules above. kind: "netlify_build_hook_url", diff --git a/review-enrichment/test/secret-scan.test.ts b/review-enrichment/test/secret-scan.test.ts index 726de47e42..321e2df30d 100644 --- a/review-enrichment/test/secret-scan.test.ts +++ b/review-enrichment/test/secret-scan.test.ts @@ -1615,3 +1615,41 @@ test("scanPatch does not flag near-misses of the deployment-hook URL formats", ( assert.equal(findings.length, 0, `near-miss should not match: ${nm}`); } }); + +test("scanPatch flags managed-database connection strings that embed credentials", () => { + // `:` fragments joined into a URI at test time — never a contiguous real secret in source. + const u = "dbuser"; + const p = "s3cr3tP" + "w0rd1234"; + const cases = [ + ["mongodb_atlas_uri", "mongodb+srv://" + u + ":" + p + "@cluster0.ab12c.mongodb.net/mydb"], + ["neon_postgres_uri", "postgresql://" + u + ":" + p + "@ep-cool-name-123.us-east-2.aws.neon.tech/neondb"], + ["supabase_postgres_uri", "postgres://" + u + ":" + p + "@db.abcdefghij.supabase.co:5432/postgres"], + ["upstash_redis_uri", "rediss://default:" + p + "@apn1-cool-cat-12345.upstash.io:6379"], + ["planetscale_mysql_uri", "mysql://" + u + ":" + p + "@aws.connect.psdb.cloud/mydb"], + ["cockroachdb_uri", "postgresql://" + u + ":" + p + "@cool-cluster-123.abc.cockroachlabs.cloud:26257/defaultdb"], + ]; + 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 connection-string placeholders or non-managed hosts", () => { + const p = "s3cr3tP" + "w0rd1234"; + const nearMisses = [ + // Angle-bracket docs placeholders — ``/`` are excluded by the rule's charset. + "mongodb+srv://:@cluster0.ab12c.mongodb.net/mydb", + "postgresql://:@ep-x.neon.tech/db", + // A plain local/self-hosted host is not a managed provider, so nothing is flagged. + "postgres://dbuser:" + p + "@localhost:5432/postgres", + "redis://default:" + p + "@127.0.0.1:6379", + // A look-alike suffix host must not match via the provider host prefix. + "mongodb+srv://dbuser:" + p + "@cluster0.ab12c.mongodb.net.evil.com/mydb", + ]; + 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}`); + } +});