Skip to content

Commit 9ac7525

Browse files
fix: change mask-secret function (#251)
* fix: change mask-secret function * fix: change mask-secret function * refactor: unify redactHeaderValue and MaskSecret funcs * refactor: minor fix * refactor: minor fix
1 parent f72a795 commit 9ac7525

7 files changed

Lines changed: 16 additions & 79 deletions

File tree

intercept/apidump/apidump.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414

1515
"cdr.dev/slog/v3"
1616

17+
"github.com/coder/aibridge/utils"
1718
"github.com/coder/quartz"
1819
"github.com/google/uuid"
1920
"github.com/tidwall/pretty"
@@ -186,7 +187,7 @@ func (d *dumper) writeRedactedHeaders(w io.Writer, headers http.Header, sensitiv
186187
}
187188

188189
if isSensitive {
189-
value = redactHeaderValue(value)
190+
value = utils.MaskSecret(value)
190191
}
191192
_, err := fmt.Fprintf(w, "%s: %s\r\n", key, value)
192193
if err != nil {

intercept/apidump/apidump_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func TestBridgedMiddleware_RedactsSensitiveRequestHeaders(t *testing.T) {
7373
// Verify sensitive headers ARE present but redacted
7474
require.Contains(t, content, "Authorization: Bear...2345")
7575
require.Contains(t, content, "X-Api-Key: secr...alue")
76-
require.Contains(t, content, "Cookie: sess...c123") // "session=abc123" is 14 chars, so first 4 + last 4
76+
require.Contains(t, content, "Cookie: se...23") // "session=abc123" is 14 chars, so first 2 + last 2
7777

7878
// Verify the full secret values are NOT present
7979
require.NotContains(t, content, "sk-secret-key-12345")
@@ -133,8 +133,8 @@ func TestBridgedMiddleware_RedactsSensitiveResponseHeaders(t *testing.T) {
133133
// Verify sensitive headers are present but redacted
134134
require.Contains(t, content, "Set-Cookie: sess...cure")
135135
// Note: Go canonicalizes WWW-Authenticate to Www-Authenticate
136-
// "Bearer realm=\"api\"" = 18 chars, first 4 = "Bear", last 4 = "api\""
137-
require.Contains(t, content, "Www-Authenticate: Bear...api\"")
136+
// "Bearer realm=\"api\"" = 18 chars, first 2 = "Be", last 2 = "i\""
137+
require.Contains(t, content, "Www-Authenticate: Be...i\"")
138138

139139
// Verify full secret values are NOT present
140140
require.NotContains(t, content, "secret123")

intercept/apidump/headers.go

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,3 @@ var sensitiveResponseHeaders = map[string]struct{}{
1818
"Www-Authenticate": {},
1919
"Proxy-Authenticate": {},
2020
}
21-
22-
// redactHeaderValue redacts a sensitive header value, showing only partial content.
23-
// For values >= 8 bytes: shows first 4 and last 4 bytes with "..." in between.
24-
// For values < 8 bytes: shows first and last byte with "..." in between.
25-
func redactHeaderValue(value string) string {
26-
if len(value) >= 8 {
27-
return value[:4] + "..." + value[len(value)-4:]
28-
}
29-
if len(value) >= 2 {
30-
return value[:1] + "..." + value[len(value)-1:]
31-
}
32-
// Single character or empty - just return as-is
33-
return value
34-
}

intercept/apidump/headers_test.go

Lines changed: 1 addition & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -12,60 +12,6 @@ import (
1212
"github.com/stretchr/testify/require"
1313
)
1414

15-
func TestRedactHeaderValue(t *testing.T) {
16-
t.Parallel()
17-
18-
tests := []struct {
19-
name string
20-
input string
21-
expected string
22-
}{
23-
{
24-
name: "empty string",
25-
input: "",
26-
expected: "",
27-
},
28-
{
29-
name: "single char",
30-
input: "a",
31-
expected: "a",
32-
},
33-
{
34-
name: "two chars",
35-
input: "ab",
36-
expected: "a...b",
37-
},
38-
{
39-
name: "seven chars",
40-
input: "abcdefg",
41-
expected: "a...g",
42-
},
43-
{
44-
name: "eight chars - threshold",
45-
input: "abcdefgh",
46-
expected: "abcd...efgh",
47-
},
48-
{
49-
name: "long value",
50-
input: "Bearer sk-secret-key-12345",
51-
expected: "Bear...2345",
52-
},
53-
{
54-
name: "realistic api key",
55-
input: "sk-proj-abc123xyz789",
56-
expected: "sk-p...z789",
57-
},
58-
}
59-
60-
for _, tc := range tests {
61-
t.Run(tc.name, func(t *testing.T) {
62-
t.Parallel()
63-
result := redactHeaderValue(tc.input)
64-
require.Equal(t, tc.expected, result)
65-
})
66-
}
67-
}
68-
6915
func TestSensitiveHeaderLists(t *testing.T) {
7016
t.Parallel()
7117

@@ -140,7 +86,7 @@ func TestWriteRedactedHeaders(t *testing.T) {
14086
name: "sensitive header redacted",
14187
headers: http.Header{"Set-Cookie": {"session=abcdefghij"}},
14288
sensitive: sensitiveResponseHeaders,
143-
expected: "Set-Cookie: sess...ghij\r\n",
89+
expected: "Set-Cookie: se...ij\r\n",
14490
},
14591
{
14692
name: "multi-value header",

provider/anthropic_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ func TestAnthropic_CreateInterceptor_BYOK(t *testing.T) {
190190
setHeaders: map[string]string{},
191191
wantXApiKey: "test-key",
192192
wantCredentialKind: intercept.CredentialKindCentralized,
193-
wantCredentialHint: "***",
193+
wantCredentialHint: "t...y",
194194
},
195195
{
196196
name: "Messages_BYOK_BearerToken_And_APIKey",

utils/mask.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ func MaskSecret(s string) string {
1111
runes := []rune(s)
1212
reveal := revealLength(len(runes))
1313

14-
// If there's nothing safe to reveal, mask it all.
15-
if reveal == 0 || reveal*2 >= len(runes) {
16-
return "***"
14+
if len(runes) <= reveal*2 {
15+
return "..."
1716
}
1817

1918
prefix := string(runes[:reveal])
@@ -28,6 +27,8 @@ func revealLength(n int) int {
2827
return 4
2928
case n >= 10:
3029
return 2
30+
case n >= 5:
31+
return 1
3132
default:
3233
return 0
3334
}

utils/mask_test.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@ func TestMaskSecret(t *testing.T) {
1616
expected string
1717
}{
1818
{"empty", "", ""},
19-
{"short", "short", "***"},
20-
{"short_9_chars", "veryshort", "***"},
19+
{"single_char", "x", "..."},
20+
{"two_chars", "ab", "..."},
21+
{"four_chars", "abcd", "..."},
22+
{"short", "short", "s...t"},
23+
{"short_9_chars", "veryshort", "v...t"},
2124
{"medium_15_chars", "thisisquitelong", "th...ng"},
2225
{"long_api_key", "sk-ant-api03-abcdefgh", "sk-a...efgh"},
2326
{"unicode", "hélloworld🌍!", "hé...🌍!"},

0 commit comments

Comments
 (0)