Skip to content

Commit 1e11118

Browse files
authored
fix(logs): mask sk-proj/sk-svcacct/sk-admin OpenAI keys (#1762)
## Bug The log sanitizer's `openai_key` pattern is `\b(sk-[A-Za-z0-9]{20,})\b`, which only matches the legacy OpenAI key format. Current keys are `sk-proj-…`, `sk-svcacct-…` and `sk-admin-…`, with `_` and `-` in the random suffix. The hyphen four characters in breaks the 20-alphanumeric run, so these keys are never matched. In free text, with no `=`/`:` before them for `high_entropy` to catch, they are logged in full. The audit redactor (`internal/audit/redact.go`) and the security pattern set (`patterns/tokens.go`) were already updated for these formats; the log sanitizer was not. ## Trigger ```go logger.Info("using key sk-proj-AbCdEf1234567890_GhIjKl-MnOpQrStUvWxYz0123456789 for upstream") ``` On main the key is written to the log unchanged. ## Fix Extend the pattern with a `(proj|svcacct|admin)-` branch that allows `_` and `-` in the suffix. The legacy branch stays alphanumeric, so `sk-ant-…` keys still fall through to the more specific `anthropic_key` pattern and keep its mask. ## Test `TestSanitizer_OpenAIProjectAndServiceAccountKeys` checks that the project, service-account, admin and legacy keys are all masked, and that an `sk-ant-api03-…` key is still masked by `anthropic_key` (`sk-ant-api***`). ## Commands run - `CGO_ENABLED=0 go test -count=1 ./internal/logs`: ok - With the fix reverted (`git stash` of `sanitizer.go`), the new test fails for the `sk-proj-`, `sk-svcacct-` and `sk-admin-` keys with `OpenAI key leaked`. - `gofmt -l` on the edited files: no output
1 parent 970eefb commit 1e11118

2 files changed

Lines changed: 31 additions & 2 deletions

File tree

‎internal/logs/sanitizer.go‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,12 @@ func (s *SecretSanitizer) registerDefaultPatterns() {
5252
},
5353
})
5454

55-
// OpenAI API keys (sk-...)
55+
// OpenAI API keys: legacy sk-..., and the current sk-proj-/sk-svcacct-/
56+
// sk-admin- forms whose suffix also carries '_' and '-'. The legacy branch
57+
// stays alphanumeric so sk-ant- keys still reach anthropic_key below.
5658
s.patterns = append(s.patterns, &secretPattern{
5759
name: "openai_key",
58-
regex: regexp.MustCompile(`\b(sk-[A-Za-z0-9]{20,})\b`),
60+
regex: regexp.MustCompile(`\b(sk-(?:(?:proj|svcacct|admin)-[A-Za-z0-9_\-]{20,}|[A-Za-z0-9]{20,}))\b`),
5961
maskFunc: func(key string) string {
6062
if len(key) <= 5 {
6163
return "****"
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package logs
2+
3+
import (
4+
"strings"
5+
"testing"
6+
)
7+
8+
func TestSanitizer_OpenAIProjectAndServiceAccountKeys(t *testing.T) {
9+
s := newTestSanitizer()
10+
for _, key := range []string{
11+
"sk-proj-AbCdEf1234567890_GhIjKl-MnOpQrStUvWxYz0123456789",
12+
"sk-svcacct-AbCdEf1234567890_GhIjKl-MnOpQrStUvWxYz0123456789",
13+
"sk-admin-AbCdEf1234567890GhIjKlMnOpQrStUvWxYz0123456789",
14+
"sk-AbCdEf1234567890GhIjKlMnOpQrStUvWxYz0123456789", // legacy
15+
} {
16+
out := s.sanitizeString("using key " + key + " for upstream")
17+
if strings.Contains(out, key) || strings.Contains(out, key[12:]) {
18+
t.Errorf("OpenAI key leaked: %q", out)
19+
}
20+
}
21+
22+
// sk-ant- keys keep their own, longer-prefix mask.
23+
ant := "sk-ant-api03-" + strings.Repeat("aB3dE", 8)
24+
if out := s.sanitizeString("key " + ant); !strings.Contains(out, "sk-ant-api***") {
25+
t.Errorf("anthropic key no longer masked by anthropic_key: %q", out)
26+
}
27+
}

0 commit comments

Comments
 (0)