diff --git a/lib/redact-patterns.ts b/lib/redact-patterns.ts index a10f78e17d..de4b60f044 100644 --- a/lib/redact-patterns.ts +++ b/lib/redact-patterns.ts @@ -233,8 +233,14 @@ export const PATTERNS: RedactPattern[] = [ id: "openai.key", tier: "HIGH", category: "secret", - description: "OpenAI API key (incl. sk-proj-)", - regex: /\b(sk-(?:proj-)?[A-Za-z0-9]{32,})\b/, + description: "OpenAI API key (legacy sk- + project/service/admin keys)", + // Two forms. Legacy keys are bare `sk-` + a contiguous alphanumeric run. + // Modern keys carry a `sk-proj-` / `sk-svcacct-` / `sk-admin-` prefix and a + // base64url-style body that includes `-` and `_` (the same charset the + // sibling `anthropic.key` already allows). The body charset is widened only + // on the prefixed form so the bare `sk-` path keeps its narrow alnum match + // and does not start matching kebab/snake identifiers that begin with `sk-`. + regex: /\b(sk-(?:proj|svcacct|admin)-[A-Za-z0-9_\-]{32,}|sk-[A-Za-z0-9]{32,})\b/, }, { id: "sendgrid.key", diff --git a/test/redact-engine.test.ts b/test/redact-engine.test.ts index 52c119a197..e2ec70afcc 100644 --- a/test/redact-engine.test.ts +++ b/test/redact-engine.test.ts @@ -57,6 +57,20 @@ describe("HIGH credential patterns", () => { expect(ids(`random ${tok} here`)).not.toContain("twilio.auth_token"); }); + test("openai.key flags modern prefixed keys whose body carries -/_ separators", () => { + // Regression: the legacy regex required a contiguous [A-Za-z0-9] run right + // after the prefix, so real `sk-proj-`/`sk-svcacct-`/`sk-admin-` keys (whose + // base64url body contains `-` and `_`) slipped through with NO finding — a + // HIGH credential failing OPEN past the redaction gate. + expect(ids("OPENAI_API_KEY=sk-proj-Ab12_Cd34-Ef56Gh78Ij90Kl12Mn34Op56Qr78St90Uv")).toContain("openai.key"); + expect(ids("sk-svcacct-abc_def-ghijklmnopqrstuvwxyz0123456789ABCDEF")).toContain("openai.key"); + expect(ids("sk-admin-AAA_bbb-CCCdddEEEfffGGGhhhIIIjjjKKKlllMMMnnn")).toContain("openai.key"); + // Legacy bare `sk-` + contiguous alnum still flagged. + expect(ids("sk-" + "B".repeat(48))).toContain("openai.key"); + // FP guard: a short `sk-` kebab identifier is not a key. + expect(ids("ran the sk-mydata step")).not.toContain("openai.key"); + }); + test("db.url_with_password flags real password, skips placeholder/env-var", () => { expect(ids("postgres://user:s3cretP@ss@db.example.com/app")).toContain("db.url_with_password"); expect(ids("postgres://user:${DB_PASSWORD}@host/app")).not.toContain("db.url_with_password");