import { describe, it, expect } from "vitest"; import path from "node:path"; import { scanText, isAllowlistedPath, lineHasPlaceholder, PATTERNS, } from "./verify-secrets.mjs"; describe("verify-secrets pattern detection", () => { it("detects a GitHub PAT in plain text", () => { const sample = `const token = "ghp_AbCdEfGhIjKlMnOpQrStUv0123456789";`; const findings = scanText(sample, { filename: "apps/web/secret.ts" }); expect(findings.length).toBeGreaterThanOrEqual(1); expect(findings.some((f) => f.patternId === "github-pat")).toBe(true); }); it("detects a GitHub fine-grained PAT", () => { const sample = `const t = "github_pat_11ABCDEFG0123456789abcdef";`; const findings = scanText(sample, { filename: "apps/api/foo.ts" }); expect(findings.some((f) => f.patternId === "github-fine-grained-pat")).toBe( true, ); }); it("detects a GitLab PAT", () => { const sample = `const t = "glpat-abcDEF1234567890_xyzQR";`; const findings = scanText(sample, { filename: "apps/web/x.ts" }); expect(findings.some((f) => f.patternId === "gitlab-pat")).toBe(true); }); it("detects an AWS Access Key ID", () => { const sample = `AKIAIOSFODNN7EXAMPLE2`; const findings = scanText(sample, { filename: "apps/web/aws.ts" }); // Note: this sample contains "example" placeholder so it should be skipped. expect(findings.length).toBe(0); }); it("detects a real AWS Access Key ID on a non-placeholder line", () => { const sample = `const k = "AKIAZZZZZZZZZZZZZZZZ";`; const findings = scanText(sample, { filename: "apps/web/aws.ts" }); expect(findings.some((f) => f.patternId === "aws-access-key-id")).toBe(true); }); it("detects Stripe live secret keys but not test keys", () => { const live = `const k = "sk_live_aaaaaaaaaaaaaaaaaaaaaaaaaaaa";`; const test = `const k = "sk_test_aaaaaaaaaaaaaaaaaaaaaaaaaaaa";`; const liveFindings = scanText(live, { filename: "apps/web/s.ts" }); const testFindings = scanText(test, { filename: "apps/web/s.ts" }); expect(liveFindings.some((f) => f.patternId === "stripe-secret-live")).toBe( true, ); expect( testFindings.some((f) => f.patternId === "stripe-secret-live"), ).toBe(false); }); it("detects Stripe webhook secrets only when the line contains 'live'", () => { const withLive = `const k = "whsec_aaaaaaaaaaaaaaaaaaaaaaaaaaaa"; // live prod webhook`; const withoutLive = `const k = "whsec_aaaaaaaaaaaaaaaaaaaaaaaaaaaa";`; const a = scanText(withLive, { filename: "apps/web/w.ts" }); const b = scanText(withoutLive, { filename: "apps/web/w.ts" }); expect(a.some((f) => f.patternId === "stripe-webhook-live")).toBe(true); expect(b.some((f) => f.patternId === "stripe-webhook-live")).toBe(false); }); it("detects Slack bot tokens", () => { const sample = `const t = "xoxb-1234567890-1234567890-1234567890-abcdef1234567890";`; const findings = scanText(sample, { filename: "apps/web/slack.ts" }); expect(findings.some((f) => f.patternId === "slack-bot-token")).toBe(true); }); it("detects a PEM private key header", () => { const sample = `-----BEGIN RSA PRIVATE KEY-----`; const findings = scanText(sample, { filename: "apps/web/key.pem" }); expect(findings.some((f) => f.patternId === "private-key-header")).toBe( true, ); }); it("detects a JWT-shaped string", () => { const sample = `const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";`; const findings = scanText(sample, { filename: "apps/web/jwt.ts" }); expect(findings.some((f) => f.patternId === "jwt-like")).toBe(true); }); it("detects generic high-entropy assignments to secret-named identifiers", () => { const sample = `const password = "abcdef0123456789abcdef0123456789ZZ";`; const findings = scanText(sample, { filename: "apps/web/pw.ts" }); expect( findings.some((f) => f.patternId === "generic-high-entropy-assignment"), ).toBe(true); }); }); describe("verify-secrets placeholder false-positive suppression", () => { it("ignores lines containing 'placeholder'", () => { const sample = `const token = "ghp_ZZZZZZZZZZZZZZZZZZZZ"; // placeholder only`; expect(scanText(sample, { filename: "x.ts" })).toHaveLength(0); }); it("ignores lines containing 'example'", () => { const sample = `# example: AKIAIOSFODNN7EXAMPL1`; expect(scanText(sample, { filename: "x.ts" })).toHaveLength(0); }); it("ignores 'your-' style placeholders", () => { const sample = `STRIPE_KEY=sk_live_your-real-stripe-secret-here-1234`; expect(scanText(sample, { filename: "x.ts" })).toHaveLength(0); }); it("ignores angle-bracket placeholders", () => { const sample = `TOKEN=ghp_`; expect(scanText(sample, { filename: "x.ts" })).toHaveLength(0); }); it("ignores shell/JS interpolation", () => { const sample = "const k = `ghp_${process.env.GITHUB_TOKEN}aaaaaaaaaaaaaaaaaaaaa`;"; expect(scanText(sample, { filename: "x.ts" })).toHaveLength(0); }); it("ignores 'REDACTED' / 'xxxxx' / 'dummy' tokens", () => { expect(lineHasPlaceholder("ghp_REDACTED_VALUE")).toBe(true); expect(lineHasPlaceholder("AKIAxxxxxYYYYYY")).toBe(true); expect(lineHasPlaceholder("token=dummy_value_here")).toBe(true); }); }); describe("verify-secrets allowlist semantics", () => { it("treats .env.example as allowlisted", () => { const p = path.join(process.cwd(), "apps", "web", ".env.example"); expect(isAllowlistedPath(p)).toBe(true); }); it("treats .env.production.example as allowlisted", () => { expect( isAllowlistedPath("/repo/.env.production.example"), ).toBe(true); }); it("treats .env.fly.example and .env.test as allowlisted", () => { expect(isAllowlistedPath("/repo/.env.fly.example")).toBe(true); expect(isAllowlistedPath("/repo/.env.test")).toBe(true); }); it("treats test/spec/fixture/mock/stub paths as allowlisted", () => { expect(isAllowlistedPath("/repo/apps/web/x.test.ts")).toBe(true); expect(isAllowlistedPath("/repo/apps/web/x.spec.ts")).toBe(true); expect(isAllowlistedPath("/repo/scripts/fixtures/foo.json")).toBe(true); expect(isAllowlistedPath("/repo/apps/web/mocks/server.ts")).toBe(true); expect(isAllowlistedPath("/repo/apps/web/stubs/foo.ts")).toBe(true); }); it("does not allowlist arbitrary source files", () => { expect(isAllowlistedPath("/repo/apps/web/routes/login.tsx")).toBe(false); expect(isAllowlistedPath("/repo/packages/db/src/client.ts")).toBe(false); }); }); describe("verify-secrets registry shape", () => { it("exposes all expected pattern ids", () => { const ids = PATTERNS.map((p) => p.id); expect(ids).toEqual( expect.arrayContaining([ "github-pat", "github-fine-grained-pat", "gitlab-pat", "aws-access-key-id", "stripe-secret-live", "stripe-webhook-live", "slack-bot-token", "private-key-header", "jwt-like", "generic-high-entropy-assignment", ]), ); }); });