From 0e0ca56f0fbdcb3d0bb138aff04d4944196bd142 Mon Sep 17 00:00:00 2001 From: B <6723574+louisgv@users.noreply.github.com> Date: Sat, 2 May 2026 10:21:29 +0000 Subject: [PATCH] fix(export): broaden SECRET_REGEX to cover Slack, Stripe, Discord, Google SA, future OR prefixes Closes known gaps in the secret-scan regex that would allow Slack tokens (xoxb/xoxp/xoxa), Stripe live keys (sk_live_), Discord bot tokens, Google service account JSON blocks, and future OpenRouter key prefixes (sk-or-v2+) to bypass the export redaction pass. Fixes #3381 Agent: ux-engineer Co-Authored-By: Claude Sonnet 4.6 --- packages/cli/src/__tests__/export.test.ts | 5 ++++- packages/cli/src/commands/export.ts | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/__tests__/export.test.ts b/packages/cli/src/__tests__/export.test.ts index 6a4510655..e5497eeea 100644 --- a/packages/cli/src/__tests__/export.test.ts +++ b/packages/cli/src/__tests__/export.test.ts @@ -194,13 +194,16 @@ describe("buildExportScript", () => { const s = buildExportScript(opts); expect(s).toContain("SECRET_REGEX="); // Verify a representative pattern from each provider family is present - expect(s).toContain("sk-or-v1-"); // OpenRouter + expect(s).toContain("sk-or-[a-zA-Z0-9_-]"); // OpenRouter (v1, v2+) expect(s).toContain("sk-ant-api"); // Anthropic expect(s).toContain("sk-proj-"); // OpenAI expect(s).toContain("gh[ops]_"); // GitHub PAT/OAuth/server expect(s).toContain("AKIA"); // AWS access key expect(s).toContain("hcloud_"); // Hetzner expect(s).toContain("dop_v1_"); // DigitalOcean + expect(s).toContain("xox[abp]-"); // Slack bot/user/app tokens + expect(s).toContain("sk_live_"); // Stripe live keys + expect(s).toContain("service_account"); // Google service account JSON expect(s).toContain("BEGIN ([A-Z]+ )?PRIVATE KEY"); // PEM }); diff --git a/packages/cli/src/commands/export.ts b/packages/cli/src/commands/export.ts index 76ef6f55c..3baa51137 100644 --- a/packages/cli/src/commands/export.ts +++ b/packages/cli/src/commands/export.ts @@ -323,7 +323,20 @@ export function buildExportScript(opts: { "# 9. SECRETS SCAN — first pass just detects and stops if hits exist so the", "# host can confirm before pushing. Second pass (ALLOW_REDACT=1) redacts", "# in-place and continues to commit/push.", - "SECRET_REGEX='(sk-or-v1-[a-f0-9]{20,})|(sk-ant-api[0-9-]+_[A-Za-z0-9_-]{20,})|(sk-proj-[A-Za-z0-9_-]{20,})|(gh[ops]_[A-Za-z0-9]{36})|(AKIA[0-9A-Z]{16})|(hcloud_[a-zA-Z0-9_-]{20,})|(dop_v1_[a-f0-9]{32,})|(-----BEGIN ([A-Z]+ )?PRIVATE KEY-----)'", + "# Secret patterns grouped by provider family:", + "# - OpenRouter: sk-or- prefix (v1, v2+, non-hex chars)", + "# - Anthropic: sk-ant-api prefix", + "# - OpenAI: sk-proj- prefix", + "# - GitHub: gho/ghp/ghs tokens", + "# - AWS: AKIA access key IDs", + "# - Hetzner: hcloud_ tokens", + "# - DigitalOcean: dop_v1_ tokens", + "# - Slack: xoxb/xoxp/xoxa bot/user/app tokens", + "# - Stripe: sk_live_ secret keys", + "# - Discord: bot tokens (base64-encoded user ID.timestamp.HMAC)", + "# - Google: service account JSON blocks", + "# - PEM: private key blocks", + 'SECRET_REGEX=\'(sk-or-[a-zA-Z0-9_-]{20,})|(sk-ant-api[0-9-]+_[A-Za-z0-9_-]{20,})|(sk-proj-[A-Za-z0-9_-]{20,})|(gh[ops]_[A-Za-z0-9]{36})|(AKIA[0-9A-Z]{16})|(hcloud_[a-zA-Z0-9_-]{20,})|(dop_v1_[a-f0-9]{32,})|(xox[abp]-[0-9A-Za-z-]{10,})|(sk_live_[A-Za-z0-9]{24,})|([A-Za-z0-9_-]{24}\\.[A-Za-z0-9_-]{6}\\.[A-Za-z0-9_-]{27,})|("type":\\s*"service_account")|(-----BEGIN ([A-Z]+ )?PRIVATE KEY-----)\'', "REDACT_PLACEHOLDER='***REDACTED-BY-SPAWN-EXPORT***'", 'SECRET_HITS="$(git ls-files -z | xargs -0 grep -lEa "$SECRET_REGEX" 2>/dev/null || true)"', 'REDACTED_JSON="[]"',