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:] }