From f30c4e73359a1eb7eb8c7ad26207d5e3e62050f5 Mon Sep 17 00:00:00 2001 From: Jayesh Betala Date: Thu, 4 Jun 2026 12:40:47 +0530 Subject: [PATCH] fix(redact): detect modern OpenAI keys with -/_ in the body MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The HIGH-tier `openai.key` pattern required a contiguous [A-Za-z0-9] run right after the prefix, so real `sk-proj-`/`sk-svcacct-`/`sk-admin-` keys (base64url body with `-` and `_`) produced no finding at all — a genuinely-secret credential failing OPEN past the redaction gate used by /spec, /ship, /cso, and /document-*. The sibling `anthropic.key` pattern already allows separators. Widen the body charset to [A-Za-z0-9_-] only on the prefixed form; the bare `sk-` legacy path keeps its narrow alnum match so it does not start matching kebab/snake identifiers that begin with `sk-`. Linear-time, passes redact-pattern-lint. +regression test for the separator forms. Fixes #1866 Co-Authored-By: Claude Opus 4.8 (1M context) --- lib/redact-patterns.ts | 10 ++++++++-- test/redact-engine.test.ts | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) 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");