From 5737ca0e369b9aaf153611fb21301b1ee89d3859 Mon Sep 17 00:00:00 2001 From: sjmiller609 <7516283+sjmiller609@users.noreply.github.com> Date: Mon, 10 Aug 2026 14:47:37 +0000 Subject: [PATCH] Hide ingress authorization values by default --- pkg/cmd/ingresscmd.go | 64 ++++++++++++++++++++++++++++++++++++-- pkg/cmd/ingresscmd_test.go | 24 ++++++++++++++ 2 files changed, 85 insertions(+), 3 deletions(-) diff --git a/pkg/cmd/ingresscmd.go b/pkg/cmd/ingresscmd.go index 2a76098..05943d6 100644 --- a/pkg/cmd/ingresscmd.go +++ b/pkg/cmd/ingresscmd.go @@ -10,6 +10,7 @@ import ( "github.com/kernel/hypeman-go" "github.com/kernel/hypeman-go/option" "github.com/tidwall/gjson" + "github.com/tidwall/sjson" "github.com/urfave/cli/v3" ) @@ -86,15 +87,25 @@ var ingressListCmd = cli.Command{ Name: "tag", Usage: "Filter by tag key-value pair (KEY=VALUE, can be repeated)", }, + &cli.BoolFlag{ + Name: "show-secrets", + Usage: "Show request header authorization values in structured output (default: hidden)", + }, }, Action: handleIngressList, HideHelpCommand: true, } var ingressGetCmd = cli.Command{ - Name: "get", - Usage: "Get ingress details", - ArgsUsage: "", + Name: "get", + Usage: "Get ingress details", + ArgsUsage: "", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "show-secrets", + Usage: "Show request header authorization values (default: hidden)", + }, + }, Action: handleIngressGet, HideHelpCommand: true, } @@ -220,6 +231,9 @@ func handleIngressList(ctx context.Context, cmd *cli.Command) error { if err != nil { return err } + if !cmd.Bool("show-secrets") { + res = []byte(redactIngressAuthValues(string(res))) + } obj := gjson.ParseBytes(res) return ShowJSON(os.Stdout, "ingress list", obj, format, transform) } @@ -296,6 +310,9 @@ func handleIngressGet(ctx context.Context, cmd *cli.Command) error { if err != nil { return err } + if !cmd.Bool("show-secrets") { + res = []byte(redactIngressAuthValues(string(res))) + } format := cmd.Root().String("format") transform := cmd.Root().String("transform") @@ -304,6 +321,47 @@ func handleIngressGet(ctx context.Context, cmd *cli.Command) error { return ShowJSON(os.Stdout, "ingress get", obj, format, transform) } +func redactIngressAuthValues(raw string) string { + root := gjson.Parse(raw) + out := raw + redactRules := func(prefix string, rules gjson.Result) bool { + ruleIndex := 0 + ok := true + rules.ForEach(func(_, rule gjson.Result) bool { + if rule.Get("request_header_auth.value").Exists() { + path := fmt.Sprintf("%s.%d.request_header_auth.value", prefix, ruleIndex) + updated, err := sjson.Set(out, path, "[hidden]") + if err != nil { + ok = false + return false + } + out = updated + } + ruleIndex++ + return true + }) + return ok + } + + if root.IsArray() { + ingressIndex := 0 + ok := true + root.ForEach(func(_, ingress gjson.Result) bool { + ok = redactRules(fmt.Sprintf("%d.rules", ingressIndex), ingress.Get("rules")) + ingressIndex++ + return ok + }) + if !ok { + return raw + } + return out + } + if !redactRules("rules", root.Get("rules")) { + return raw + } + return out +} + func handleIngressDelete(ctx context.Context, cmd *cli.Command) error { args := cmd.Args().Slice() if len(args) < 1 { diff --git a/pkg/cmd/ingresscmd_test.go b/pkg/cmd/ingresscmd_test.go index e125475..cf78630 100644 --- a/pkg/cmd/ingresscmd_test.go +++ b/pkg/cmd/ingresscmd_test.go @@ -5,8 +5,32 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/tidwall/gjson" ) +func TestRedactIngressAuthValues(t *testing.T) { + t.Run("redacts get response", func(t *testing.T) { + raw := `{"id":"ing-1","rules":[{"request_header_auth":{"header":"X-Origin-Verification","value":"secret-value"}},{"target":{"port":8080}}]}` + redacted := redactIngressAuthValues(raw) + + assert.Equal(t, "[hidden]", gjson.Get(redacted, "rules.0.request_header_auth.value").String()) + assert.Equal(t, "X-Origin-Verification", gjson.Get(redacted, "rules.0.request_header_auth.header").String()) + }) + + t.Run("redacts list response", func(t *testing.T) { + raw := `[{"id":"ing-1","rules":[{"request_header_auth":{"value":"first-secret"}}]},{"id":"ing-2","rules":[{"request_header_auth":{"value":"second-secret"}}]}]` + redacted := redactIngressAuthValues(raw) + + assert.Equal(t, "[hidden]", gjson.Get(redacted, "0.rules.0.request_header_auth.value").String()) + assert.Equal(t, "[hidden]", gjson.Get(redacted, "1.rules.0.request_header_auth.value").String()) + }) + + t.Run("preserves response without authorization values", func(t *testing.T) { + raw := `{"id":"ing-1","rules":[{"target":{"port":8080}}]}` + assert.Equal(t, raw, redactIngressAuthValues(raw)) + }) +} + func TestParseIngressRuleSpec(t *testing.T) { t.Run("full spec with host port, tls, and redirect", func(t *testing.T) { rule, err := parseIngressRuleSpec("api.example.com:443=web:8080,tls,redirect-http", "fallback")