Files

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
}