Critical fixes: - Add SanitizeConnectionString() in storage/db_connect.go to remove passwords - Add SecureEnvVar() in api/factory.go to clear env vars after reading (JWT_SECRET) - Clear DB password from config after connection Logging improvements: - Enhance logging/sanitize.go with patterns for: - PostgreSQL connection strings - Generic connection string passwords - HTTP Authorization headers - Private keys CLI security: - Add --security-audit flag to api-server for security checks: - Config file permissions - Exposed environment variables - Running as root - API key file permissions - Add warning when --api-key flag used (process list exposure) Files changed: - internal/storage/db_connect.go - internal/api/factory.go - internal/logging/sanitize.go - internal/auth/flags.go - cmd/api-server/main.go
107 lines
3.2 KiB
Go
107 lines
3.2 KiB
Go
package logging
|
|
|
|
import (
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
// Patterns for sensitive data
|
|
var (
|
|
// API keys: 32+ hex characters
|
|
apiKeyPattern = regexp.MustCompile(`\b[0-9a-fA-F]{32,}\b`)
|
|
|
|
// JWT tokens
|
|
jwtPattern = regexp.MustCompile(`eyJ[a-zA-Z0-9_-]{10,}\.eyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}`)
|
|
|
|
// Password-like fields in logs
|
|
passwordPattern = regexp.MustCompile(
|
|
`(?i)(password|passwd|pwd|secret|token|key)["']?\s*[:=]\s*["']?` +
|
|
`([^"'\s,}]+)`,
|
|
)
|
|
|
|
// Redis URLs with passwords
|
|
redisPasswordPattern = regexp.MustCompile(`redis://:[^@]+@`)
|
|
|
|
// Database connection strings - PostgreSQL
|
|
postgresPasswordPattern = regexp.MustCompile(`(postgresql?://[^:]+:)[^@]+(@)`)
|
|
|
|
// Generic connection strings with passwords
|
|
connStringPasswordPattern = regexp.MustCompile(`(password[=:])[^\s&;]+`)
|
|
|
|
// HTTP Authorization headers
|
|
authHeaderPattern = regexp.MustCompile(`(Authorization:\s*(?:Bearer|Basic)\s+)[^\s]+`)
|
|
|
|
// Private keys
|
|
privateKeyPattern = regexp.MustCompile(`(-----BEGIN (?:RSA|EC|DSA|OPENSSH) PRIVATE KEY-----)[\s\S]*?(-----END (?:RSA|EC|DSA|OPENSSH) PRIVATE KEY-----)`)
|
|
)
|
|
|
|
// SanitizeLogMessage removes sensitive data from log messages
|
|
func SanitizeLogMessage(message string) string {
|
|
// Redact API keys
|
|
message = apiKeyPattern.ReplaceAllString(message, "[REDACTED-API-KEY]")
|
|
|
|
// Redact JWT tokens
|
|
message = jwtPattern.ReplaceAllString(message, "[REDACTED-JWT]")
|
|
|
|
// Redact password-like fields
|
|
message = passwordPattern.ReplaceAllStringFunc(message, func(match string) string {
|
|
parts := passwordPattern.FindStringSubmatch(match)
|
|
if len(parts) >= 2 {
|
|
return parts[1] + "=[REDACTED]"
|
|
}
|
|
return match
|
|
})
|
|
|
|
// Redact Redis passwords from URLs
|
|
message = redisPasswordPattern.ReplaceAllString(message, "redis://:[REDACTED]@")
|
|
|
|
// Redact PostgreSQL connection strings
|
|
message = postgresPasswordPattern.ReplaceAllString(message, "${1}[REDACTED]${2}")
|
|
|
|
// Redact generic connection string passwords
|
|
message = connStringPasswordPattern.ReplaceAllString(message, "${1}[REDACTED]")
|
|
|
|
// Redact HTTP Authorization headers
|
|
message = authHeaderPattern.ReplaceAllString(message, "${1}[REDACTED]")
|
|
|
|
// Redact private keys
|
|
message = privateKeyPattern.ReplaceAllString(message, "[REDACTED-PRIVATE-KEY]")
|
|
|
|
return message
|
|
}
|
|
|
|
// SanitizeArgs removes sensitive data from structured log arguments
|
|
func SanitizeArgs(args []any) []any {
|
|
sanitized := make([]any, len(args))
|
|
copy(sanitized, args)
|
|
|
|
for i := 0; i < len(sanitized)-1; i += 2 {
|
|
// Check if this is a key-value pair
|
|
key, okKey := sanitized[i].(string)
|
|
value, okValue := sanitized[i+1].(string)
|
|
|
|
if okKey && okValue {
|
|
lowerKey := strings.ToLower(key)
|
|
// Redact sensitive fields
|
|
if strings.Contains(lowerKey, "password") ||
|
|
strings.Contains(lowerKey, "secret") ||
|
|
strings.Contains(lowerKey, "token") ||
|
|
strings.Contains(lowerKey, "key") ||
|
|
strings.Contains(lowerKey, "api") {
|
|
sanitized[i+1] = "[REDACTED]"
|
|
} else if strings.HasPrefix(value, "redis://") {
|
|
sanitized[i+1] = SanitizeLogMessage(value)
|
|
}
|
|
}
|
|
}
|
|
|
|
return sanitized
|
|
}
|
|
|
|
// RedactAPIKey masks an API key for logging (shows first/last 4 chars)
|
|
func RedactAPIKey(key string) string {
|
|
if len(key) <= 8 {
|
|
return "[REDACTED]"
|
|
}
|
|
return key[:4] + "..." + key[len(key)-4:]
|
|
}
|