From e903d9a2c2fe3c3ec7e7e12001c5c5890a4393e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sat, 9 May 2026 02:00:01 +0200 Subject: [PATCH] fix(logging): scrub OpenAI / Anthropic API keys in redactSecrets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P11. Adds a `sk-(ant-)?[A-Za-z0-9_-]+` rule so any sk- / sk-ant- token that finds its way into an error message or stack trace gets redacted before it reaches Loki or Glitchtip. We never log these on purpose, but a mis-typed config or a dumped request body could carry one — defense in depth on the egress path. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/lib/logging/__tests__/redact.test.ts | 14 ++++++++++++++ src/lib/logging/redact.ts | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/src/lib/logging/__tests__/redact.test.ts b/src/lib/logging/__tests__/redact.test.ts index eeea3f594..55aec6b70 100644 --- a/src/lib/logging/__tests__/redact.test.ts +++ b/src/lib/logging/__tests__/redact.test.ts @@ -29,6 +29,20 @@ describe("redactSecrets", () => { expect(redactSecrets("api?api_key=secret")).toBe("api?api_key=[REDACTED]"); }); + it("redacts OpenAI and Anthropic API keys", () => { + expect( + redactSecrets("error: invalid key sk-1234567890abcdefABCDEF"), + ).toBe("error: invalid key [REDACTED]"); + expect( + redactSecrets("Authorization: Bearer sk-ant-api03-xyzABC_42-token"), + ).toBe("Authorization: Bearer [REDACTED]"); + // Standalone Anthropic key (no Bearer prefix) — make sure the sk-ant- + // path matches independently of the Bearer rule. + expect(redactSecrets("body={apiKey: sk-ant-abcDEF123_-}")).toBe( + "body={apiKey: [REDACTED]}", + ); + }); + it("leaves non-secret-shaped strings alone", () => { expect(redactSecrets("HTTP 503 from upstream")).toBe( "HTTP 503 from upstream", diff --git a/src/lib/logging/redact.ts b/src/lib/logging/redact.ts index 69ad0de5c..6018dfa57 100644 --- a/src/lib/logging/redact.ts +++ b/src/lib/logging/redact.ts @@ -15,6 +15,9 @@ * Withings webhooks and OAuth callbacks (already scrubbed at the * URL level in `reportToGlitchtip`, but error.message can carry * the URL too). + * - `sk-…` and `sk-ant-…` — OpenAI / Anthropic API keys. We never + * log them on purpose, but a misconfigured client error or a + * dump of the request body could carry one. Scrub before egress. * * The substitution is intentionally generic ([REDACTED]) — we don't * want partial revelation of token entropy. @@ -23,6 +26,7 @@ export function redactSecrets(input: string): string { return input .replace(/Bearer\s+\S+/gi, "Bearer [REDACTED]") .replace(/bot\d+:[A-Za-z0-9_-]+/g, "bot[REDACTED]") + .replace(/sk-(?:ant-)?[A-Za-z0-9_-]+/g, "[REDACTED]") .replace( /([?&])(secret|code|token|api[_-]?key)=[^&\s]+/gi, "$1$2=[REDACTED]",