fix(logging): scrub OpenAI / Anthropic API keys in redactSecrets

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) <noreply@anthropic.com>
This commit is contained in:
Marc-André BombeckandClaude Opus 4.7 committed 2026-05-09 02:00:01 +02:00
1 parent 5c884b3d0e
commit e903d9a2c2
2 files changed
+18

No files matched your search

+14
View File
@@ -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",
+4
View File
@@ -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]",