105 lines
2.4 KiB
Go
105 lines
2.4 KiB
Go
package logger
|
|
|
|
import (
|
|
"log"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
// Logger wraps standard logger with security features
|
|
type Logger struct {
|
|
*log.Logger
|
|
}
|
|
|
|
// New creates a new logger
|
|
func New(output *os.File) *Logger {
|
|
return &Logger{
|
|
Logger: log.New(output, "", log.LstdFlags),
|
|
}
|
|
}
|
|
|
|
// NewStdout creates a logger that writes to stdout
|
|
func NewStdout() *Logger {
|
|
return &Logger{
|
|
Logger: log.New(os.Stdout, "", log.LstdFlags),
|
|
}
|
|
}
|
|
|
|
// MaskSecret masks sensitive data, showing only first 4 and last 4 characters
|
|
func MaskSecret(secret string) string {
|
|
if len(secret) <= 8 {
|
|
return "****"
|
|
}
|
|
return secret[:4] + "****" + secret[len(secret)-4:]
|
|
}
|
|
|
|
// SafePrintf logs a message, masking any secrets found in the format string or args
|
|
func (l *Logger) SafePrintf(format string, v ...interface{}) {
|
|
// Mask secrets in format string
|
|
safeFormat := maskSecretsInString(format)
|
|
|
|
// Mask secrets in arguments
|
|
safeArgs := make([]interface{}, len(v))
|
|
for i, arg := range v {
|
|
if str, ok := arg.(string); ok {
|
|
safeArgs[i] = maskSecretsInString(str)
|
|
} else {
|
|
safeArgs[i] = arg
|
|
}
|
|
}
|
|
|
|
l.Printf(safeFormat, safeArgs...)
|
|
}
|
|
|
|
// maskSecretsInString masks common secret patterns in a string
|
|
func maskSecretsInString(s string) string {
|
|
// This is a simple implementation - in production you might want more sophisticated detection
|
|
// For now, we'll mask common patterns like tokens and passwords
|
|
lower := strings.ToLower(s)
|
|
|
|
// Mask if contains "token=", "pass=", "password=" patterns
|
|
if strings.Contains(lower, "token=") {
|
|
parts := strings.Split(s, "token=")
|
|
if len(parts) > 1 {
|
|
rest := parts[1]
|
|
// Find where the token ends (space, newline, or end of string)
|
|
end := len(rest)
|
|
for i, r := range rest {
|
|
if r == ' ' || r == '\n' || r == '\t' {
|
|
end = i
|
|
break
|
|
}
|
|
}
|
|
if end > 0 {
|
|
token := rest[:end]
|
|
s = strings.Replace(s, "token="+token, "token="+MaskSecret(token), 1)
|
|
}
|
|
}
|
|
}
|
|
|
|
if strings.Contains(lower, "pass=") || strings.Contains(lower, "password=") {
|
|
key := "pass="
|
|
if strings.Contains(lower, "password=") {
|
|
key = "password="
|
|
}
|
|
parts := strings.Split(s, key)
|
|
if len(parts) > 1 {
|
|
rest := parts[1]
|
|
end := len(rest)
|
|
for i, r := range rest {
|
|
if r == ' ' || r == '\n' || r == '\t' {
|
|
end = i
|
|
break
|
|
}
|
|
}
|
|
if end > 0 {
|
|
pass := rest[:end]
|
|
s = strings.Replace(s, key+pass, key+MaskSecret(pass), 1)
|
|
}
|
|
}
|
|
}
|
|
|
|
return s
|
|
}
|
|
|