From d0d1e8c9c5d52a512661ae7c329b3e0dbeeadf09 Mon Sep 17 00:00:00 2001 From: Dickson Date: Tue, 24 Mar 2026 14:17:17 -0500 Subject: [PATCH] fix: redact database credentials in startup logs Signed-off-by: Dickson --- go/core/pkg/app/app.go | 19 ++++++++++++ go/core/pkg/app/app_test.go | 58 +++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/go/core/pkg/app/app.go b/go/core/pkg/app/app.go index fd141336b..36d7bd13b 100644 --- a/go/core/pkg/app/app.go +++ b/go/core/pkg/app/app.go @@ -23,6 +23,7 @@ import ( "fmt" "net/http" "net/http/pprof" + "net/url" "os" "path/filepath" "slices" @@ -131,6 +132,24 @@ type Config struct { } } +// redactedConfig is a type alias for Config without MarshalLog to avoid recursion. +type redactedConfig Config + +// MarshalLog implements logr.Marshaler to redact sensitive fields when logging. +func (c Config) MarshalLog() interface{} { + rc := redactedConfig(c) + rc.Database.Url = redactURL(rc.Database.Url) + return rc +} + +func redactURL(rawURL string) string { + u, err := url.Parse(rawURL) + if err != nil { + return "***" + } + return u.Redacted() +} + func (cfg *Config) SetFlags(commandLine *flag.FlagSet) { commandLine.StringVar(&cfg.Metrics.Addr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+ "Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.") diff --git a/go/core/pkg/app/app_test.go b/go/core/pkg/app/app_test.go index a4638836d..15a1c08d0 100644 --- a/go/core/pkg/app/app_test.go +++ b/go/core/pkg/app/app_test.go @@ -371,6 +371,64 @@ func TestMapValueWithLoadFromEnvEqualsInValue(t *testing.T) { assert.Equal(t, map[string]string{"token": "abc=def", "team": "platform"}, target) } +func TestRedactURL(t *testing.T) { + tests := []struct { + name string + url string + want string + }{ + { + name: "postgres URL with credentials", + url: "postgres://postgres:kagent@kagent-postgresql.kagent.svc.cluster.local:5432/postgres", + want: "postgres://postgres:xxxxx@kagent-postgresql.kagent.svc.cluster.local:5432/postgres", + }, + { + name: "URL without credentials", + url: "postgres://kagent-postgresql.kagent.svc.cluster.local:5432/postgres", + want: "postgres://kagent-postgresql.kagent.svc.cluster.local:5432/postgres", + }, + { + name: "URL with username only", + url: "postgres://postgres@localhost:5432/db", + want: "postgres://postgres@localhost:5432/db", + }, + { + name: "empty string", + url: "", + want: "", + }, + { + name: "invalid URL", + url: "://invalid", + want: "***", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := redactURL(tt.url) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestConfigMarshalLog(t *testing.T) { + cfg := Config{} + cfg.Database.Url = "postgres://postgres:supersecret@localhost:5432/mydb" + cfg.Database.VectorEnabled = true + cfg.HttpServerAddr = ":8083" + + result := cfg.MarshalLog() + rc, ok := result.(redactedConfig) + assert.True(t, ok, "MarshalLog should return redactedConfig") + assert.Equal(t, "postgres://postgres:xxxxx@localhost:5432/mydb", rc.Database.Url) + assert.Equal(t, true, rc.Database.VectorEnabled) + assert.Equal(t, ":8083", rc.HttpServerAddr) + + // Original config should not be modified + assert.Equal(t, "postgres://postgres:supersecret@localhost:5432/mydb", cfg.Database.Url) +} + func TestLoadFromEnvIntegration(t *testing.T) { envVars := map[string]string{ "METRICS_BIND_ADDRESS": ":9090",