diff --git a/internal/logging/request_logger.go b/internal/logging/request_logger.go index 47f701e8..70a48b75 100644 --- a/internal/logging/request_logger.go +++ b/internal/logging/request_logger.go @@ -16,6 +16,7 @@ import ( "time" "github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces" + "github.com/router-for-me/CLIProxyAPI/v6/internal/util" ) // RequestLogger defines the interface for logging HTTP requests and responses. @@ -485,7 +486,8 @@ func (l *FileRequestLogger) formatRequestInfo(url, method string, headers map[st content.WriteString("=== HEADERS ===\n") for key, values := range headers { for _, value := range values { - content.WriteString(fmt.Sprintf("%s: %s\n", key, value)) + masked := util.MaskSensitiveHeaderValue(key, value) + content.WriteString(fmt.Sprintf("%s: %s\n", key, masked)) } } content.WriteString("\n") diff --git a/internal/runtime/executor/logging_helpers.go b/internal/runtime/executor/logging_helpers.go index da0dcd40..4ee10fb1 100644 --- a/internal/runtime/executor/logging_helpers.go +++ b/internal/runtime/executor/logging_helpers.go @@ -275,7 +275,8 @@ func writeHeaders(builder *strings.Builder, headers http.Header) { continue } for _, value := range values { - builder.WriteString(fmt.Sprintf("%s: %s\n", key, sanitizeHeaderValue(key, value))) + masked := util.MaskSensitiveHeaderValue(key, value) + builder.WriteString(fmt.Sprintf("%s: %s\n", key, masked)) } } } @@ -319,18 +320,3 @@ func formatAuthInfo(info upstreamRequestLog) string { return strings.Join(parts, ", ") } - -func sanitizeHeaderValue(key, value string) string { - trimmedValue := strings.TrimSpace(value) - lowerKey := strings.ToLower(strings.TrimSpace(key)) - switch { - case strings.Contains(lowerKey, "authorization"), - strings.Contains(lowerKey, "api-key"), - strings.Contains(lowerKey, "apikey"), - strings.Contains(lowerKey, "token"), - strings.Contains(lowerKey, "secret"): - return util.HideAPIKey(trimmedValue) - default: - return trimmedValue - } -} diff --git a/internal/util/provider.go b/internal/util/provider.go index a6b327fd..5f4dcd19 100644 --- a/internal/util/provider.go +++ b/internal/util/provider.go @@ -4,6 +4,8 @@ package util import ( + "strings" + "github.com/router-for-me/CLIProxyAPI/v6/internal/config" "github.com/router-for-me/CLIProxyAPI/v6/internal/registry" ) @@ -141,3 +143,48 @@ func HideAPIKey(apiKey string) string { } return apiKey } + +// maskAuthorizationHeader masks the Authorization header value while preserving the auth type prefix. +// Common formats: "Bearer ", "Basic ", "ApiKey ", etc. +// It preserves the prefix (e.g., "Bearer ") and only masks the token/credential part. +// +// Parameters: +// - value: The Authorization header value +// +// Returns: +// - string: The masked Authorization value with prefix preserved +func MaskAuthorizationHeader(value string) string { + parts := strings.SplitN(strings.TrimSpace(value), " ", 2) + if len(parts) < 2 { + return HideAPIKey(value) + } + return parts[0] + " " + HideAPIKey(parts[1]) +} + +// MaskSensitiveHeaderValue masks sensitive header values while preserving expected formats. +// +// Behavior by header key (case-insensitive): +// - "Authorization": Preserve the auth type prefix (e.g., "Bearer ") and mask only the credential part. +// - Headers containing "api-key": Mask the entire value using HideAPIKey. +// - Others: Return the original value unchanged. +// +// Parameters: +// - key: The HTTP header name to inspect (case-insensitive matching). +// - value: The header value to mask when sensitive. +// +// Returns: +// - string: The masked value according to the header type; unchanged if not sensitive. +func MaskSensitiveHeaderValue(key, value string) string { + lowerKey := strings.ToLower(strings.TrimSpace(key)) + switch { + case lowerKey == "authorization": + return MaskAuthorizationHeader(value) + case strings.Contains(lowerKey, "api-key"), + strings.Contains(lowerKey, "apikey"), + strings.Contains(lowerKey, "token"), + strings.Contains(lowerKey, "secret"): + return HideAPIKey(value) + default: + return value + } +}