From 2b67f3de810e068c942cce58298de9172e85aa95 Mon Sep 17 00:00:00 2001 From: Connor Moss Date: Tue, 29 Sep 2026 17:04:41 -0400 Subject: [PATCH 1/3] fix: redact --svc-param values in config show --- internal/cli/cli_test.go | 8 ++++++++ internal/clicfg/binder.go | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/internal/cli/cli_test.go b/internal/cli/cli_test.go index 20133cc..39c07cf 100644 --- a/internal/cli/cli_test.go +++ b/internal/cli/cli_test.go @@ -1344,6 +1344,14 @@ another_flag: 123 "agent-card": "https://yaml-extra-agent.example.com", }, }, + { + name: "svc-param credentials are redacted", + filename: "creds.yaml", + content: "svc-param:\n - \"Authorization=Bearer SECRET123\"\n", + wantVals: map[string]string{ + "svc-param": "", + }, + }, { name: "json config with additional properties ignored", filename: "extra.json", diff --git a/internal/clicfg/binder.go b/internal/clicfg/binder.go index c3bab65..d2fa849 100644 --- a/internal/clicfg/binder.go +++ b/internal/clicfg/binder.go @@ -50,7 +50,7 @@ func Bind(flags *pflag.FlagSet, store *Store) ([]FlagBinding, error) { sensitive := false switch f.Name { - case "auth", "bearer", "api-key": + case "auth", "bearer", "api-key", "svc-param": sensitive = true } From 0764b44a164d65829554e4f9af7258a6a5ce039f Mon Sep 17 00:00:00 2001 From: Connor Moss Date: Wed, 30 Sep 2026 11:17:35 -0400 Subject: [PATCH 2/3] fix: redact only Authorization service params --- internal/cli/cli_test.go | 6 +++--- internal/clicfg/binder.go | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/internal/cli/cli_test.go b/internal/cli/cli_test.go index 39c07cf..061b733 100644 --- a/internal/cli/cli_test.go +++ b/internal/cli/cli_test.go @@ -1345,11 +1345,11 @@ another_flag: 123 }, }, { - name: "svc-param credentials are redacted", + name: "svc-param Authorization values are redacted", filename: "creds.yaml", - content: "svc-param:\n - \"Authorization=Bearer SECRET123\"\n", + content: "svc-param:\n - \"Authorization=Bearer SECRET123\"\n - \"X-Trace=abc\"\n", wantVals: map[string]string{ - "svc-param": "", + "svc-param": "Authorization=,X-Trace=abc", }, }, { diff --git a/internal/clicfg/binder.go b/internal/clicfg/binder.go index d2fa849..ab373d8 100644 --- a/internal/clicfg/binder.go +++ b/internal/clicfg/binder.go @@ -50,7 +50,7 @@ func Bind(flags *pflag.FlagSet, store *Store) ([]FlagBinding, error) { sensitive := false switch f.Name { - case "auth", "bearer", "api-key", "svc-param": + case "auth": sensitive = true } @@ -92,6 +92,9 @@ func Bind(flags *pflag.FlagSet, store *Store) ([]FlagBinding, error) { binding.Source = source.String() binding.Path = source.Path binding.Value = formatValue(value) + if f.Name == "svc-param" { + binding.Value = redactServiceParams(binding.Value) + } bindings = append(bindings, binding) }) @@ -104,6 +107,20 @@ func Bind(flags *pflag.FlagSet, store *Store) ([]FlagBinding, error) { return bindings, nil } +// redactServiceParams hides the value of each Authorization service parameter. +func redactServiceParams(value string) string { + entries := strings.Split(value, ",") + for i, entry := range entries { + if !strings.HasPrefix(strings.ToLower(entry), "authorization") { + continue + } + if sep := strings.IndexAny(entry, ":="); sep >= 0 { + entries[i] = entry[:sep+1] + "" + } + } + return strings.Join(entries, ",") +} + func flagValueString(f *pflag.Flag) string { if sv, ok := f.Value.(pflag.SliceValue); ok { return strings.Join(sv.GetSlice(), ",") From 9ad793aee11e022a8f39d47e9e53683876b57910 Mon Sep 17 00:00:00 2001 From: Yaroslav Shevchuk Date: Thu, 15 Oct 2026 09:31:15 +0000 Subject: [PATCH 3/3] fix: redact svc-param entries before joining Splitting the joined value on commas let Authorization values containing a comma leak their tail, and the prefix match redacted unrelated keys such as Authorization-Hint. Redact each entry individually and match the key exactly (case-insensitive). --- internal/cli/cli_test.go | 32 ++++++++++++++++++++++++++++++++ internal/clicfg/binder.go | 35 ++++++++++++++++++++++++----------- 2 files changed, 56 insertions(+), 11 deletions(-) diff --git a/internal/cli/cli_test.go b/internal/cli/cli_test.go index 061b733..bcccfbd 100644 --- a/internal/cli/cli_test.go +++ b/internal/cli/cli_test.go @@ -1352,6 +1352,38 @@ another_flag: 123 "svc-param": "Authorization=,X-Trace=abc", }, }, + { + name: "svc-param Authorization value containing a comma is fully redacted", + filename: "creds-comma.yaml", + content: "svc-param:\n - \"Authorization=Basic abc,def\"\n", + wantVals: map[string]string{ + "svc-param": "Authorization=", + }, + }, + { + name: "svc-param Authorization key is matched case-insensitively with colon separator", + filename: "creds-colon.yaml", + content: "svc-param:\n - \"authorization:Bearer SECRET123\"\n", + wantVals: map[string]string{ + "svc-param": "authorization:", + }, + }, + { + name: "svc-param key with Authorization prefix is not redacted", + filename: "creds-prefix.yaml", + content: "svc-param:\n - \"Authorization-Hint=visible\"\n", + wantVals: map[string]string{ + "svc-param": "Authorization-Hint=visible", + }, + }, + { + name: "svc-param scalar Authorization value is redacted", + filename: "creds-scalar.yaml", + content: "svc-param: \"Authorization=Bearer a,b\"\n", + wantVals: map[string]string{ + "svc-param": "Authorization=", + }, + }, { name: "json config with additional properties ignored", filename: "extra.json", diff --git a/internal/clicfg/binder.go b/internal/clicfg/binder.go index ab373d8..608e4da 100644 --- a/internal/clicfg/binder.go +++ b/internal/clicfg/binder.go @@ -91,10 +91,10 @@ func Bind(flags *pflag.FlagSet, store *Store) ([]FlagBinding, error) { f.Changed = true binding.Source = source.String() binding.Path = source.Path - binding.Value = formatValue(value) if f.Name == "svc-param" { - binding.Value = redactServiceParams(binding.Value) + value = redactServiceParams(value) } + binding.Value = formatValue(value) bindings = append(bindings, binding) }) @@ -108,17 +108,30 @@ func Bind(flags *pflag.FlagSet, store *Store) ([]FlagBinding, error) { } // redactServiceParams hides the value of each Authorization service parameter. -func redactServiceParams(value string) string { - entries := strings.Split(value, ",") - for i, entry := range entries { - if !strings.HasPrefix(strings.ToLower(entry), "authorization") { - continue - } - if sep := strings.IndexAny(entry, ":="); sep >= 0 { - entries[i] = entry[:sep+1] + "" +// Entries are redacted before joining so commas inside values cannot split a +// credential across entries. +func redactServiceParams(value any) any { + entries, ok := toStringSlice(value) + if !ok { + s, ok := value.(string) + if !ok { + return value } + entries = []string{s} + } + redacted := make([]string, len(entries)) + for i, entry := range entries { + redacted[i] = redactServiceParam(entry) + } + return redacted +} + +func redactServiceParam(entry string) string { + sep := strings.IndexAny(entry, ":=") + if sep < 0 || !strings.EqualFold(strings.TrimSpace(entry[:sep]), "Authorization") { + return entry } - return strings.Join(entries, ",") + return entry[:sep+1] + "" } func flagValueString(f *pflag.Flag) string {