fix(log): sanitise structured logging keys and values
Some checks failed
CI / test (push) Failing after 2s
CI / auto-fix (push) Failing after 0s
CI / auto-merge (push) Failing after 1s

This commit is contained in:
Virgil 2026-03-29 23:29:43 +00:00
parent 355af66a5c
commit 4f7e730802
2 changed files with 29 additions and 9 deletions

18
log.go
View file

@ -258,11 +258,11 @@ func (l *Logger) log(level Level, prefix, msg string, keyvals ...any) {
if i > 0 {
kvStr += " "
}
key := keyvals[i]
var val any
if i+1 < len(keyvals) {
val = keyvals[i+1]
}
key := normaliseLogText(fmt.Sprintf("%v", keyvals[i]))
var val any
if i+1 < len(keyvals) {
val = keyvals[i+1]
}
// Redaction logic
if shouldRedact(key, redactKeys) {
@ -271,12 +271,12 @@ func (l *Logger) log(level Level, prefix, msg string, keyvals ...any) {
// Secure formatting to prevent log injection
if s, ok := val.(string); ok {
kvStr += fmt.Sprintf("%v=%q", key, s)
} else {
kvStr += fmt.Sprintf("%v=%v", key, val)
}
kvStr += fmt.Sprintf("%s=%q", key, s)
} else {
kvStr += fmt.Sprintf("%s=%v", key, normaliseLogText(fmt.Sprintf("%v", val)))
}
}
}
_, _ = fmt.Fprintf(output, "%s %s %s%s\n", timestamp, prefix, normaliseLogText(msg), kvStr)
}

View file

@ -137,6 +137,26 @@ func TestLogger_InjectionPrevention(t *testing.T) {
}
}
func TestLogger_KeySanitization_Good(t *testing.T) {
var buf bytes.Buffer
l := New(Options{Level: LevelInfo, Output: &buf})
l.Info("message", "key\nwith newline", "value\nwith newline")
output := buf.String()
if !strings.Contains(output, "key\\nwith newline") {
t.Errorf("expected sanitized key, got %q", output)
}
if !strings.Contains(output, "value\\nwith newline") {
t.Errorf("expected sanitized value, got %q", output)
}
lines := strings.Split(strings.TrimSpace(output), "\n")
if len(lines) != 1 {
t.Errorf("expected 1 line, got %d", len(lines))
}
}
func TestLogger_MessageSanitization_Good(t *testing.T) {
var buf bytes.Buffer
l := New(Options{Level: LevelInfo, Output: &buf})