From 58090b42ec7568fdf1c36feeb4c56d8327d32232 Mon Sep 17 00:00:00 2001 From: Deluan Date: Tue, 29 Sep 2026 10:37:53 -0400 Subject: [PATCH] fix(log): redact marked secrets in every field type redactSecrets only looked at strings, maps and errors, so a marked secret inside a slice, struct or []byte field was logged as is, and a typed-nil error field made it panic. It now renders each field with fmt.Sprint, as the text formatter does (which also survives typed-nil errors), and writes []byte raw. Signed-off-by: Deluan --- log/redactrus.go | 14 ++++---------- log/redactrus_test.go | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/log/redactrus.go b/log/redactrus.go index a9cf5a981..b8ad0ddb4 100755 --- a/log/redactrus.go +++ b/log/redactrus.go @@ -83,18 +83,12 @@ func redactSecrets(e *logrus.Entry) { if v == nil { continue } + // fmt.Sprint renders like the text formatter and survives typed-nil errors; []byte is written raw. var s string - if err, ok := v.(error); ok { - s = err.Error() + if b, ok := v.([]byte); ok { + s = string(b) } else { - switch reflect.TypeOf(v).Kind() { - case reflect.String: - s = reflect.ValueOf(v).String() - case reflect.Map: - s = fmt.Sprintf("%+v", v) - default: - continue - } + s = fmt.Sprint(v) } if hidden := hide(s); hidden != s { e.Data[k] = hidden diff --git a/log/redactrus_test.go b/log/redactrus_test.go index 6b8a0e5fe..6b9d71f89 100755 --- a/log/redactrus_test.go +++ b/log/redactrus_test.go @@ -2,6 +2,7 @@ package log import ( "errors" + "net/url" "testing" "github.com/sirupsen/logrus" @@ -196,6 +197,26 @@ func TestFireRedactsContextSecrets(t *testing.T) { assert.Equal(t, namedString("untouched"), e.Data["clean"]) } +func TestFireRedactsContextSecretsInAnyValueType(t *testing.T) { + ctx := WithSecrets(t.Context(), "s3cr3t-value") + var nilErr *url.Error + e := &logrus.Entry{ + Context: ctx, + Data: logrus.Fields{ + "slice": []any{"s3cr3t-value", 1}, + "struct": struct{ A string }{"s3cr3t-value"}, + "bytes": []byte("has s3cr3t-value"), + "nilErr": nilErr, + }, + } + + assert.NotPanics(t, func() { _ = (&Hook{}).Fire(e) }) + assert.Equal(t, "[[REDACTED] 1]", e.Data["slice"]) + assert.Equal(t, "{[REDACTED]}", e.Data["struct"]) + assert.Equal(t, "has [REDACTED]", e.Data["bytes"]) + assert.Equal(t, nilErr, e.Data["nilErr"]) +} + func TestFireWithoutContextSecretsLeavesEntryUnchanged(t *testing.T) { args := map[string]any{"p0": "value"} e := &logrus.Entry{Context: t.Context(), Message: "value", Data: logrus.Fields{"str": "value", "args": args}}