diff --git a/internal/api/notifications.go b/internal/api/notifications.go index cbd888d..cbd24f9 100644 --- a/internal/api/notifications.go +++ b/internal/api/notifications.go @@ -482,17 +482,39 @@ func redactSecretsFromURL(urlStr string) string { } // Redact query parameters with sensitive names - if idx := strings.Index(urlStr, "?"); idx != -1 { + if qIdx := strings.Index(urlStr, "?"); qIdx != -1 { sensitiveParams := []string{"token", "apikey", "api_key", "key", "secret", "password"} for _, param := range sensitiveParams { pattern := param + "=" - if paramIdx := strings.Index(urlStr, pattern); paramIdx != -1 { + // Search for the pattern after the query string starts + searchStart := qIdx + for { + paramIdx := strings.Index(urlStr[searchStart:], pattern) + if paramIdx == -1 { + break + } + paramIdx += searchStart // Convert to absolute index + + // Check that we're at a parameter boundary (after ? or &) + if paramIdx > 0 { + prevChar := urlStr[paramIdx-1] + if prevChar != '?' && prevChar != '&' { + // Not at a boundary - this is part of another param name + // Move past this match and continue searching + searchStart = paramIdx + len(pattern) + continue + } + } + + // Valid match - redact the value start := paramIdx + len(pattern) end := start for end < len(urlStr) && urlStr[end] != '&' && urlStr[end] != '#' { end++ } urlStr = urlStr[:start] + "REDACTED" + urlStr[end:] + // After modification, continue from after the inserted REDACTED + searchStart = start + len("REDACTED") } } } diff --git a/internal/api/notifications_test.go b/internal/api/notifications_test.go index 576a455..78fe976 100644 --- a/internal/api/notifications_test.go +++ b/internal/api/notifications_test.go @@ -109,8 +109,24 @@ func TestRedactSecretsFromURL(t *testing.T) { }, { name: "combined telegram and query param secrets", - input: "https://api.telegram.org/bot123:token/send?extra_token=abc", - expected: "https://api.telegram.org/botREDACTED/send?extra_token=REDACTED", + input: "https://api.telegram.org/bot123:token/send?token=abc", + expected: "https://api.telegram.org/botREDACTED/send?token=REDACTED", + }, + // Boundary checking - prefixed params should NOT be redacted + { + name: "prefixed param name should not match", + input: "https://example.com/api?extra_token=abc&myapikey=xyz", + expected: "https://example.com/api?extra_token=abc&myapikey=xyz", + }, + { + name: "prefixed param with real sensitive param", + input: "https://example.com/api?extra_token=abc&token=secret", + expected: "https://example.com/api?extra_token=abc&token=REDACTED", + }, + { + name: "multiple prefixed params unchanged", + input: "https://example.com/api?mytoken=a&yourkey=b&thesecret=c", + expected: "https://example.com/api?mytoken=a&yourkey=b&thesecret=c", }, }