Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package kubectl-gather for openSUSE:Factory 
checked in at 2026-03-26 21:10:08
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/kubectl-gather (Old)
 and      /work/SRC/openSUSE:Factory/.kubectl-gather.new.8177 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "kubectl-gather"

Thu Mar 26 21:10:08 2026 rev:5 rq:1342840 version:0.12.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/kubectl-gather/kubectl-gather.changes    
2025-10-23 16:38:29.617045800 +0200
+++ /work/SRC/openSUSE:Factory/.kubectl-gather.new.8177/kubectl-gather.changes  
2026-03-27 06:42:34.532808665 +0100
@@ -1,0 +2,15 @@
+Thu Mar 26 11:51:24 UTC 2026 - Johannes Kastl 
<[email protected]>
+
+- Update to version 0.12.0:
+  * doc: Update readme with secret sanitization support
+  * e2e: Add e2e tests for secret sanitization
+  * gather: Add unit tests for secret sanitization
+  * gather: Sanitize secret data with PBKDF2 hashes
+  * Update actions for node 24 (#121)
+  * deps: Update k8s.io dependencies to oldest supported k8s
+    version
+  * Update zap to v1.27.1
+  * Update cobra to latest version
+  * Update go to 1.25.0
+
+-------------------------------------------------------------------

Old:
----
  kubectl-gather-0.11.0.obscpio

New:
----
  kubectl-gather-0.12.0.obscpio

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ kubectl-gather.spec ++++++
--- /var/tmp/diff_new_pack.fjC22s/_old  2026-03-27 06:42:35.660855271 +0100
+++ /var/tmp/diff_new_pack.fjC22s/_new  2026-03-27 06:42:35.668855601 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package kubectl-gather
 #
-# Copyright (c) 2025 SUSE LLC and contributors
+# Copyright (c) 2026 SUSE LLC and contributors
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -17,7 +17,7 @@
 
 
 Name:           kubectl-gather
-Version:        0.11.0
+Version:        0.12.0
 Release:        0
 Summary:        Kubectl plugin to gather data about your cluster
 License:        Apache-2.0

++++++ _service ++++++
--- /var/tmp/diff_new_pack.fjC22s/_old  2026-03-27 06:42:35.708857254 +0100
+++ /var/tmp/diff_new_pack.fjC22s/_new  2026-03-27 06:42:35.708857254 +0100
@@ -3,7 +3,7 @@
     <param name="url">https://github.com/nirs/kubectl-gather</param>
     <param name="scm">git</param>
     <param name="exclude">.git</param>
-    <param name="revision">v0.11.0</param>
+    <param name="revision">v0.12.0</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="versionrewrite-pattern">v(.*)</param>
     <param name="changesgenerate">enable</param>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.fjC22s/_old  2026-03-27 06:42:35.732858246 +0100
+++ /var/tmp/diff_new_pack.fjC22s/_new  2026-03-27 06:42:35.732858246 +0100
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param 
name="url">https://github.com/nirs/kubectl-gather</param>
-              <param 
name="changesrevision">c42a9ee16b240236e0cb67809e810e028176d0ad</param></service></servicedata>
+              <param 
name="changesrevision">a0e7607071c92e7d7c8b1902eafb0dcd39f392dc</param></service></servicedata>
 (No newline at EOF)
 

++++++ kubectl-gather-0.11.0.obscpio -> kubectl-gather-0.12.0.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-gather-0.11.0/README.md 
new/kubectl-gather-0.12.0/README.md
--- old/kubectl-gather-0.11.0/README.md 2025-10-22 14:55:18.000000000 +0200
+++ new/kubectl-gather-0.12.0/README.md 2026-03-26 11:26:48.000000000 +0100
@@ -416,11 +416,15 @@
 > [!IMPORTANT]
 > Gathering remotely require the "oc" command.
 
+Use `--salt` to ensure all remote clusters use the same salt for
+consistent secret hashing. See [Secret
+sanitization](#secret-sanitization) for more info.
+
 In this example we gather data from OpenShift Data Foundation clusters
 configured for disaster recovery. Gathering everything takes more than 6
 minutes:
 
-    $ kubectl gather --contexts kevin-rdr-hub,kevin-rdr-c1,kevin-rdr-c2 
--remote --directory gather.remote
+    $ kubectl gather --contexts kevin-rdr-hub,kevin-rdr-c1,kevin-rdr-c2 
--remote --salt "$(openssl rand -base64 16)" --directory gather.remote
     2024-05-28T20:57:32.684+0300       INFO    gather  Using kubeconfig 
"/home/nsoffer/.kube/config"
     2024-05-28T20:57:32.686+0300       INFO    gather  Gathering from all 
namespaces
     2024-05-28T20:57:32.686+0300       INFO    gather  Gathering on remote 
cluster "kevin-rdr-c2"
@@ -496,7 +500,7 @@
 In this example we gather data related to single DR protected VM:
 
 ```
-$ kubectl gather --contexts kevin-rdr-hub,kevin-rdr-c1,kevin-rdr-c2 
--namespaces openshift-dr-ops,ui-vms3 --remote -d gather.remote.app
+$ kubectl gather --contexts kevin-rdr-hub,kevin-rdr-c1,kevin-rdr-c2 
--namespaces openshift-dr-ops,ui-vms3 --remote --salt "$(openssl rand -base64 
16)" -d gather.remote.app
 2024-05-28T21:14:15.883+0300   INFO    gather  Using kubeconfig 
"/home/nsoffer/.kube/config"
 2024-05-28T21:14:15.884+0300   INFO    gather  Gathering from namespaces 
[openshift-dr-ops ui-vms3]
 2024-05-28T21:14:15.884+0300   INFO    gather  Gathering on remote cluster 
"kevin-rdr-c2"
@@ -573,6 +577,42 @@
 8.8M   gather.resources
 ```
 
+## Secret sanitization
+
+All Secret resources are automatically sanitized before writing. Each
+data value is replaced with a deterministic PBKDF2-HMAC-SHA256 hash,
+and the `kubectl.kubernetes.io/last-applied-configuration` annotation
+is stripped to prevent plaintext leaks.
+
+A `kubectl-gather.nirs.github.com/sanitized` annotation is added to
+each sanitized secret with the base64-encoded salt. The salt can be
+used to verify the original secret hash value.
+
+By default a random salt is generated. To use a specific salt, use the
+`--salt` flag with `--remote` to ensure all remote clusters use the
+same salt.
+
+```
+$ kubectl gather --contexts dr1,dr2,hub --remote --salt "$(openssl rand 
-base64 16)" -d gather.remote
+```
+
+Specifying a salt is also useful when comparing changes between two
+gather runs:
+
+```
+salt=$(openssl rand -base64 16)
+kubectl gather --contexts hub,dr1,dr2 -d gather.a --salt "$salt"
+sleep 60
+kubectl gather --contexts hub,dr1,dr2 -d gather.b --salt "$salt"
+diff -ur gather.a gather.b
+```
+
+Without a specified salt, the diff would include unwanted changes in
+secret hashes and annotations.
+
+Gathering secrets can be slower than other resources. The impact is
+small when gathering only a few namespaces.
+
 ## Integrating with other programs
 
 When running the *kubectl gather* from another program you may want to
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-gather-0.11.0/cmd/local.go 
new/kubectl-gather-0.12.0/cmd/local.go
--- old/kubectl-gather-0.11.0/cmd/local.go      2025-10-22 14:55:18.000000000 
+0200
+++ new/kubectl-gather-0.12.0/cmd/local.go      2026-03-26 11:26:48.000000000 
+0100
@@ -40,6 +40,7 @@
                        Namespaces: namespaces,
                        Addons:     addons,
                        Cluster:    cluster,
+                       Salt:       parsedSalt,
                        Log:        log.Named(clusterConfig.Context),
                }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-gather-0.11.0/cmd/remote.go 
new/kubectl-gather-0.12.0/cmd/remote.go
--- old/kubectl-gather-0.11.0/cmd/remote.go     2025-10-22 14:55:18.000000000 
+0200
+++ new/kubectl-gather-0.12.0/cmd/remote.go     2026-03-26 11:26:48.000000000 
+0100
@@ -113,6 +113,10 @@
                remoteArgs = append(remoteArgs, 
"--addons="+strings.Join(addons, ","))
        }
 
+       // Always pass the salt so all remote clusters use the same salt value,
+       // ensuring consistent hashes for comparing secrets across clusters.
+       remoteArgs = append(remoteArgs, "--salt="+salt)
+
        if len(remoteArgs) > 0 {
                args = append(args, "--", "/usr/bin/gather")
                args = append(args, remoteArgs...)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-gather-0.11.0/cmd/root.go 
new/kubectl-gather-0.12.0/cmd/root.go
--- old/kubectl-gather-0.11.0/cmd/root.go       2025-10-22 14:55:18.000000000 
+0200
+++ new/kubectl-gather-0.12.0/cmd/root.go       2026-03-26 11:26:48.000000000 
+0100
@@ -4,6 +4,7 @@
 package cmd
 
 import (
+       "encoding/base64"
        "fmt"
        stdlog "log"
        "os"
@@ -26,6 +27,8 @@
 var addons []string
 var cluster bool
 var remote bool
+var salt string
+var parsedSalt gather.Salt
 var verbose bool
 var logFormat string
 var log *zap.SugaredLogger
@@ -43,8 +46,9 @@
   kubectl gather --contexts dr1,dr2,hub --namespaces my-ns,other-ns 
--directory gather.ns
 
   # Gather data on the remote clusters "dr1", "dr2" and "hub" and download it 
to
-  # "gather.remote/". Requires the "oc" command.
-  kubectl gather --contexts dr1,dr2,hub --remote --directory gather.remote
+  # "gather.remote/". Requires the "oc" command. Use --salt to ensure all 
remote
+  # clusters use the same salt for consistent secret hashing.
+  kubectl gather --contexts dr1,dr2,hub --remote --salt "$(openssl rand 
-base64 16)" --directory gather.remote
 
   # Enable only the "logs" addon, gathering all resources and pod logs. Use
   # --addons= to disable all addons.
@@ -97,6 +101,8 @@
                        "specified, gather all resources")
        rootCmd.Flags().BoolVarP(&remote, "remote", "r", false,
                "run on the remote clusters (requires the \"oc\" command)")
+       rootCmd.Flags().StringVar(&salt, "salt", "",
+               "base64-encoded 16-byte salt for secret hashing (default: 
randomly generated)")
        rootCmd.Flags().BoolVarP(&verbose, "verbose", "v", false,
                "be more verbose")
        rootCmd.Flags().StringVar(&logFormat, "log-format", "text", "Set the 
logging format [text, json]")
@@ -124,6 +130,12 @@
                log.Fatal(err)
        }
 
+       if cmd.Flags().Changed("salt") {
+               log.Infof("Using user-provided salt %q", salt)
+       } else {
+               log.Infof("Using generated salt %q", salt)
+       }
+
        if namespaces == nil {
                log.Infof("Gathering from all namespaces")
        } else if len(namespaces) > 0 {
@@ -152,6 +164,18 @@
 }
 
 func validateOptions(cmd *cobra.Command) error {
+       if salt != "" {
+               var err error
+               parsedSalt, err = validateSalt(salt)
+               if err != nil {
+                       return err
+               }
+       } else {
+               parsedSalt = gather.RandomSalt()
+               // Keep the base64 salt string for logging and passing to 
remote clusters.
+               salt = base64.StdEncoding.EncodeToString(parsedSalt[:])
+       }
+
        // --namespaces=""
        // --namespaces="" --cluster=false
        if namespaces != nil && len(namespaces) == 0 && !cluster {
@@ -223,6 +247,21 @@
        return zap.New(core).Named("gather").Sugar()
 }
 
+func validateSalt(saltFlag string) (gather.Salt, error) {
+       var salt gather.Salt
+
+       decodedSalt, err := base64.StdEncoding.DecodeString(saltFlag)
+       if err != nil {
+               return salt, fmt.Errorf("invalid --salt value: must be 
base64-encoded: %w", err)
+       }
+       if len(decodedSalt) != 16 {
+               return salt, fmt.Errorf("invalid --salt value: must be 16 
bytes, got %d", len(decodedSalt))
+       }
+
+       copy(salt[:], decodedSalt)
+       return salt, nil
+}
+
 func defaultGatherDirectory() string {
        return time.Now().Format("gather.20060102150405")
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-gather-0.11.0/e2e/gather_test.go 
new/kubectl-gather-0.12.0/e2e/gather_test.go
--- old/kubectl-gather-0.11.0/e2e/gather_test.go        2025-10-22 
14:55:18.000000000 +0200
+++ new/kubectl-gather-0.12.0/e2e/gather_test.go        2026-03-26 
11:26:48.000000000 +0100
@@ -23,6 +23,7 @@
                "namespaces/test-common/apps/replicasets/common-busybox-*.yaml",
                "namespaces/test-common/configmaps/kube-root-ca.crt.yaml",
                "namespaces/test-common/serviceaccounts/default.yaml",
+               "namespaces/test-common/secrets/common-secret1.yaml",
        }
 
        commonLogResources = []string{
@@ -48,6 +49,7 @@
                "namespaces/test-c1/apps/replicasets/c1-busybox-*.yaml",
                "namespaces/test-c1/configmaps/kube-root-ca.crt.yaml",
                "namespaces/test-c1/serviceaccounts/default.yaml",
+               "namespaces/test-c1/secrets/c1-secret1.yaml",
        }
 
        c1LogResources = []string{
@@ -73,6 +75,7 @@
                "namespaces/test-c2/apps/replicasets/c2-busybox-*.yaml",
                "namespaces/test-c2/configmaps/kube-root-ca.crt.yaml",
                "namespaces/test-c2/serviceaccounts/default.yaml",
+               "namespaces/test-c2/secrets/c2-secret1.yaml",
        }
 
        c2LogResources = []string{
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-gather-0.11.0/e2e/go.mod 
new/kubectl-gather-0.12.0/e2e/go.mod
--- old/kubectl-gather-0.11.0/e2e/go.mod        2025-10-22 14:55:18.000000000 
+0200
+++ new/kubectl-gather-0.12.0/e2e/go.mod        2026-03-26 11:26:48.000000000 
+0100
@@ -1,15 +1,22 @@
 module github.com/nirs/kubectl-gather/e2e
 
-go 1.24.0
+go 1.25.0
 
-toolchain go1.24.5
+toolchain go1.26.1
 
 require (
        github.com/nirs/kubectl-gather v0.8.0
-       github.com/spf13/cobra v1.9.1
+       github.com/spf13/cobra v1.10.2
 )
 
 require (
+       github.com/pkg/errors v0.9.1 // indirect
+       gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
+)
+
+require golang.org/x/crypto v0.49.0 // indirect
+
+require (
        github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // 
indirect
        github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // 
indirect
        github.com/emicklei/go-restful/v3 v3.12.1 // indirect
@@ -19,10 +26,8 @@
        github.com/go-openapi/jsonreference v0.21.0 // indirect
        github.com/go-openapi/swag v0.23.0 // indirect
        github.com/gogo/protobuf v1.3.2 // indirect
-       github.com/golang/protobuf v1.5.4 // indirect
-       github.com/google/gnostic-models v0.6.8 // indirect
-       github.com/google/go-cmp v0.6.0 // indirect
-       github.com/google/gofuzz v1.2.0 // indirect
+       github.com/google/gnostic-models v0.6.9 // indirect
+       github.com/google/go-cmp v0.7.0 // indirect
        github.com/google/uuid v1.6.0 // indirect
        github.com/inconshreveable/mousetrap v1.1.0 // indirect
        github.com/josharian/intern v1.0.0 // indirect
@@ -33,26 +38,25 @@
        github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // 
indirect
        github.com/modern-go/reflect2 v1.0.2 // indirect
        github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // 
indirect
-       github.com/spf13/pflag v1.0.6 // indirect
+       github.com/spf13/pflag v1.0.9 // indirect
        github.com/x448/float16 v0.8.4 // indirect
        go.uber.org/multierr v1.11.0 // indirect
-       go.uber.org/zap v1.27.0 // indirect
-       golang.org/x/net v0.38.0 // indirect
+       go.uber.org/zap v1.27.1 // indirect
+       golang.org/x/net v0.51.0 // indirect
        golang.org/x/oauth2 v0.27.0 // indirect
-       golang.org/x/sys v0.31.0 // indirect
-       golang.org/x/term v0.30.0 // indirect
-       golang.org/x/text v0.23.0 // indirect
-       golang.org/x/time v0.6.0 // indirect
-       google.golang.org/protobuf v1.34.2 // indirect
+       golang.org/x/sys v0.42.0 // indirect
+       golang.org/x/term v0.41.0 // indirect
+       golang.org/x/text v0.35.0 // indirect
+       golang.org/x/time v0.9.0 // indirect
+       google.golang.org/protobuf v1.36.5 // indirect
        gopkg.in/inf.v0 v0.9.1 // indirect
-       gopkg.in/yaml.v2 v2.4.0 // indirect
        gopkg.in/yaml.v3 v3.0.1 // indirect
-       k8s.io/api v0.31.1
-       k8s.io/apimachinery v0.31.1 // indirect
-       k8s.io/cli-runtime v0.31.1 // indirect
-       k8s.io/client-go v0.31.1 // indirect
+       k8s.io/api v0.33.10
+       k8s.io/apimachinery v0.33.10 // indirect
+       k8s.io/cli-runtime v0.33.10 // indirect
+       k8s.io/client-go v0.33.10 // indirect
        k8s.io/klog/v2 v2.130.1 // indirect
-       k8s.io/kube-openapi v0.0.0-20240816214639-573285566f34 // indirect
+       k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
        k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
        sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
        sigs.k8s.io/randfill v1.0.0 // indirect
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-gather-0.11.0/e2e/go.sum 
new/kubectl-gather-0.12.0/e2e/go.sum
--- old/kubectl-gather-0.11.0/e2e/go.sum        2025-10-22 14:55:18.000000000 
+0200
+++ new/kubectl-gather-0.12.0/e2e/go.sum        2026-03-26 11:26:48.000000000 
+0100
@@ -23,18 +23,14 @@
 github.com/go-task/slim-sprig/v3 v3.0.0/go.mod 
h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod 
h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/golang/protobuf v1.5.4 
h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
-github.com/golang/protobuf v1.5.4/go.mod 
h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
-github.com/google/gnostic-models v0.6.8 
h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
-github.com/google/gnostic-models v0.6.8/go.mod 
h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
+github.com/google/gnostic-models v0.6.9 
h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
+github.com/google/gnostic-models v0.6.9/go.mod 
h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
 github.com/google/go-cmp v0.5.9/go.mod 
h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
-github.com/google/go-cmp v0.6.0/go.mod 
h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod 
h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
 github.com/google/gofuzz v1.0.0/go.mod 
h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
-github.com/google/gofuzz v1.2.0/go.mod 
h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 
h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k=
-github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod 
h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
+github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db 
h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
+github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod 
h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod 
h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/inconshreveable/mousetrap v1.1.0 
h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
@@ -62,26 +58,27 @@
 github.com/modern-go/reflect2 v1.0.2/go.mod 
h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 
h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod 
h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/onsi/ginkgo/v2 v2.20.0 
h1:PE84V2mHqoT1sglvHc8ZdQtPcwmvvt29WLEEO3xmdZw=
-github.com/onsi/ginkgo/v2 v2.20.0/go.mod 
h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI=
-github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
-github.com/onsi/gomega v1.34.1/go.mod 
h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
+github.com/onsi/ginkgo/v2 v2.21.0 
h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
+github.com/onsi/ginkgo/v2 v2.21.0/go.mod 
h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
+github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
+github.com/onsi/gomega v1.35.1/go.mod 
h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod 
h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 
h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod 
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 
h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
-github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod 
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/rogpeppe/go-internal v1.12.0 
h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
-github.com/rogpeppe/go-internal v1.12.0/go.mod 
h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
+github.com/rogpeppe/go-internal v1.13.1 
h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
+github.com/rogpeppe/go-internal v1.13.1/go.mod 
h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod 
h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
-github.com/spf13/cobra v1.9.1/go.mod 
h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
-github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
-github.com/spf13/pflag v1.0.6/go.mod 
h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
+github.com/spf13/cobra v1.10.2/go.mod 
h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
+github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
+github.com/spf13/pflag v1.0.9/go.mod 
h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/stretchr/objx v0.1.0/go.mod 
h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod 
h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
 github.com/stretchr/testify v1.3.0/go.mod 
h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.9.0 
h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
-github.com/stretchr/testify v1.9.0/go.mod 
h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.10.0 
h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod 
h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
 github.com/x448/float16 v0.8.4/go.mod 
h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
 github.com/yuin/goldmark v1.1.27/go.mod 
h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -90,21 +87,22 @@
 go.uber.org/goleak v1.3.0/go.mod 
h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
 go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
 go.uber.org/multierr v1.11.0/go.mod 
h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
-go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
-go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
+go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.yaml.in/yaml/v3 v3.0.4/go.mod 
h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod 
h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod 
h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod 
h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 
h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
-golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod 
h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
+golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
+golang.org/x/crypto v0.49.0/go.mod 
h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod 
h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod 
h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod 
h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod 
h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
-golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
+golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
+golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
 golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
 golang.org/x/oauth2 v0.27.0/go.mod 
h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -114,28 +112,28 @@
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
-golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
-golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
-golang.org/x/term v0.30.0/go.mod 
h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
+golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
+golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
+golang.org/x/term v0.41.0/go.mod 
h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
-golang.org/x/text v0.23.0/go.mod 
h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
-golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
-golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
+golang.org/x/text v0.35.0/go.mod 
h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
+golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
+golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod 
h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod 
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod 
h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod 
h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
-golang.org/x/tools v0.24.0/go.mod 
h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
+golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
+golang.org/x/tools v0.42.0/go.mod 
h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/protobuf v1.34.2 
h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
-google.golang.org/protobuf v1.34.2/go.mod 
h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
+google.golang.org/protobuf v1.36.5 
h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
+google.golang.org/protobuf v1.36.5/go.mod 
h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c 
h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod 
h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -143,22 +141,20 @@
 gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod 
h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
 gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
-gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
-gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU=
-k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI=
-k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U=
-k8s.io/apimachinery v0.31.1/go.mod 
h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
-k8s.io/cli-runtime v0.31.1 h1:/ZmKhmZ6hNqDM+yf9s3Y4KEYakNXUn5sod2LWGGwCuk=
-k8s.io/cli-runtime v0.31.1/go.mod 
h1:pKv1cDIaq7ehWGuXQ+A//1OIF+7DI+xudXtExMCbe9U=
-k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0=
-k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg=
+k8s.io/api v0.33.10 h1:8OHPP+ybXl9UKq1gpqvQPvfnwDtilBqfCY3+LQl4TK4=
+k8s.io/api v0.33.10/go.mod h1:pVsaVIqAFpx/8NDmICPI0VOgnIc6oZJoGCpOgXrPGxY=
+k8s.io/apimachinery v0.33.10 h1:XHdu/6MeJHzh7H2y31XVOL5GIYjP/tH5jcHofNYWaDc=
+k8s.io/apimachinery v0.33.10/go.mod 
h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
+k8s.io/cli-runtime v0.33.10 h1:4V3wwsEhp+9edaXyO71PuQXpaIP5Xcrl3aLfk1nA9RE=
+k8s.io/cli-runtime v0.33.10/go.mod 
h1:t3UsILXibAluoLrWc8PLr7trFoGsFI+Dq/3ZVAvgaPQ=
+k8s.io/client-go v0.33.10 h1:Q3KkTB0zPnGlrs6mS2p6LaJ2zM5ehBVz0JO9ZKQied0=
+k8s.io/client-go v0.33.10/go.mod 
h1:pEg4JnHgHR+m6tZ23SUWj+md2iPdszmVG8KD9/pHtec=
 k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
 k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
-k8s.io/kube-openapi v0.0.0-20240816214639-573285566f34 
h1:/amS69DLm09mtbFtN3+LyygSFohnYGMseF8iv+2zulg=
-k8s.io/kube-openapi v0.0.0-20240816214639-573285566f34/go.mod 
h1:G0W3eI9gG219NHRq3h5uQaRBl4pj4ZpwzRP5ti8y770=
+k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff 
h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
+k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod 
h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
 k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 
h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
 k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod 
h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
 sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 
h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-gather-0.11.0/e2e/output_test.go 
new/kubectl-gather-0.12.0/e2e/output_test.go
--- old/kubectl-gather-0.11.0/e2e/output_test.go        2025-10-22 
14:55:18.000000000 +0200
+++ new/kubectl-gather-0.12.0/e2e/output_test.go        2026-03-26 
11:26:48.000000000 +0100
@@ -1,8 +1,11 @@
 package e2e
 
 import (
+       "encoding/base64"
+       "os"
        "os/exec"
        "path/filepath"
+       "strings"
        "testing"
 
        apps "k8s.io/api/apps/v1"
@@ -118,3 +121,89 @@
                }
        })
 }
+
+func TestSecretSanitization(t *testing.T) {
+       outputDir := "out/test-secret-sanitization"
+
+       salt := gather.RandomSalt()
+       saltB64 := base64.StdEncoding.EncodeToString(salt[:])
+
+       cmd := exec.Command(
+               kubectlGather,
+               "--contexts", strings.Join(clusters.Names, ","),
+               "--kubeconfig", clusters.Kubeconfig(),
+               "--salt", saltB64,
+               "--directory", outputDir,
+       )
+       if err := commands.Run(cmd); err != nil {
+               t.Fatalf("kubectl-gather failed: %s", err)
+       }
+
+       for _, cluster := range clusters.Names {
+               for _, secret := range gatheredSecrets(t, outputDir, cluster) {
+                       salt, ok := 
secret.Annotations["kubectl-gather.nirs.github.com/sanitized"]
+                       if !ok {
+                               t.Fatalf("secret %s: sanitized annotation not 
found", secret.Name)
+                       }
+                       if salt != saltB64 {
+                               t.Errorf("secret %s: expected salt %q, got %q", 
secret.Name, saltB64, salt)
+                       }
+               }
+       }
+}
+
+func TestSecretSanitizationRandomSalt(t *testing.T) {
+       outputDir := "out/test-secret-sanitization-random"
+
+       cmd := exec.Command(
+               kubectlGather,
+               "--contexts", strings.Join(clusters.Names, ","),
+               "--kubeconfig", clusters.Kubeconfig(),
+               "--directory", outputDir,
+       )
+       if err := commands.Run(cmd); err != nil {
+               t.Fatalf("kubectl-gather failed: %s", err)
+       }
+
+       salts := map[string]struct{}{}
+       for _, cluster := range clusters.Names {
+               for _, secret := range gatheredSecrets(t, outputDir, cluster) {
+                       salt, ok := 
secret.Annotations["kubectl-gather.nirs.github.com/sanitized"]
+                       if !ok {
+                               t.Fatalf("secret %s: sanitized annotation not 
found", secret.Name)
+                       }
+                       salts[salt] = struct{}{}
+               }
+       }
+       if len(salts) != 1 {
+               t.Fatalf("secrets hashed with multiple salts: %v", salts)
+       }
+}
+
+// Test helpers
+
+// gatheredSecrets returns all secrets gathered for a cluster.
+func gatheredSecrets(t *testing.T, outputDir, cluster string) []core.Secret {
+       t.Helper()
+       pattern := filepath.Join(outputDir, cluster, "namespaces", "*", 
"secrets", "*.yaml")
+       files, err := filepath.Glob(pattern)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(files) == 0 {
+               t.Fatalf("no secrets found for cluster %s", cluster)
+       }
+       var secrets []core.Secret
+       for _, file := range files {
+               data, err := os.ReadFile(file)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               var secret core.Secret
+               if err := yaml.Unmarshal(data, &secret); err != nil {
+                       t.Fatal(err)
+               }
+               secrets = append(secrets, secret)
+       }
+       return secrets
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kubectl-gather-0.11.0/e2e/testdata/base/kustomization.yaml 
new/kubectl-gather-0.12.0/e2e/testdata/base/kustomization.yaml
--- old/kubectl-gather-0.11.0/e2e/testdata/base/kustomization.yaml      
2025-10-22 14:55:18.000000000 +0200
+++ new/kubectl-gather-0.12.0/e2e/testdata/base/kustomization.yaml      
2026-03-26 11:26:48.000000000 +0100
@@ -4,3 +4,4 @@
   - ns.yaml
   - pvc.yaml
   - deploy.yaml
+  - secret.yaml
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-gather-0.11.0/e2e/testdata/base/secret.yaml 
new/kubectl-gather-0.12.0/e2e/testdata/base/secret.yaml
--- old/kubectl-gather-0.11.0/e2e/testdata/base/secret.yaml     1970-01-01 
01:00:00.000000000 +0100
+++ new/kubectl-gather-0.12.0/e2e/testdata/base/secret.yaml     2026-03-26 
11:26:48.000000000 +0100
@@ -0,0 +1,9 @@
+---
+apiVersion: v1
+kind: Secret
+metadata:
+  name: secret1
+type: Opaque
+data:
+  secret1: c2VjcmV0MQ==
+  secret2: c2VjcmV0Mg==
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-gather-0.11.0/go.mod 
new/kubectl-gather-0.12.0/go.mod
--- old/kubectl-gather-0.11.0/go.mod    2025-10-22 14:55:18.000000000 +0200
+++ new/kubectl-gather-0.12.0/go.mod    2026-03-26 11:26:48.000000000 +0100
@@ -1,16 +1,19 @@
 module github.com/nirs/kubectl-gather
 
-go 1.24.0
+go 1.25.0
 
-toolchain go1.25.3
+toolchain go1.26.1
 
 require (
-       github.com/spf13/cobra v1.9.1
-       go.uber.org/zap v1.27.0
-       k8s.io/api v0.31.1
-       k8s.io/apimachinery v0.31.1
-       k8s.io/cli-runtime v0.31.1
-       k8s.io/client-go v0.31.1
+       github.com/aymanbagabas/go-udiff v0.4.1
+       github.com/spf13/cobra v1.10.2
+       go.uber.org/zap v1.27.1
+       golang.org/x/crypto v0.49.0
+       k8s.io/api v0.33.10
+       k8s.io/apimachinery v0.33.10
+       k8s.io/cli-runtime v0.33.10
+       k8s.io/client-go v0.33.10
+       sigs.k8s.io/yaml v1.4.0
 )
 
 require (
@@ -23,12 +26,9 @@
        github.com/go-openapi/jsonreference v0.21.0 // indirect
        github.com/go-openapi/swag v0.23.0 // indirect
        github.com/gogo/protobuf v1.3.2 // indirect
-       github.com/golang/protobuf v1.5.4 // indirect
-       github.com/google/gnostic-models v0.6.8 // indirect
-       github.com/google/go-cmp v0.6.0 // indirect
-       github.com/google/gofuzz v1.2.0 // indirect
+       github.com/google/gnostic-models v0.6.9 // indirect
+       github.com/google/go-cmp v0.7.0 // indirect
        github.com/google/uuid v1.6.0 // indirect
-       github.com/imdario/mergo v0.3.16 // indirect
        github.com/inconshreveable/mousetrap v1.1.0 // indirect
        github.com/josharian/intern v1.0.0 // indirect
        github.com/json-iterator/go v1.1.12 // indirect
@@ -38,23 +38,24 @@
        github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // 
indirect
        github.com/modern-go/reflect2 v1.0.2 // indirect
        github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // 
indirect
-       github.com/spf13/pflag v1.0.6 // indirect
+       github.com/pkg/errors v0.9.1 // indirect
+       github.com/spf13/pflag v1.0.9 // indirect
        github.com/x448/float16 v0.8.4 // indirect
        go.uber.org/multierr v1.11.0 // indirect
-       golang.org/x/net v0.38.0 // indirect
+       golang.org/x/net v0.51.0 // indirect
        golang.org/x/oauth2 v0.27.0 // indirect
-       golang.org/x/sys v0.31.0 // indirect
-       golang.org/x/term v0.30.0 // indirect
-       golang.org/x/text v0.23.0 // indirect
-       golang.org/x/time v0.6.0 // indirect
-       google.golang.org/protobuf v1.34.2 // indirect
+       golang.org/x/sys v0.42.0 // indirect
+       golang.org/x/term v0.41.0 // indirect
+       golang.org/x/text v0.35.0 // indirect
+       golang.org/x/time v0.9.0 // indirect
+       google.golang.org/protobuf v1.36.5 // indirect
+       gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
        gopkg.in/inf.v0 v0.9.1 // indirect
-       gopkg.in/yaml.v2 v2.4.0 // indirect
        gopkg.in/yaml.v3 v3.0.1 // indirect
        k8s.io/klog/v2 v2.130.1 // indirect
-       k8s.io/kube-openapi v0.0.0-20240816214639-573285566f34 // indirect
-       k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
-       sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
-       sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
-       sigs.k8s.io/yaml v1.4.0 // indirect
+       k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
+       k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
+       sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
+       sigs.k8s.io/randfill v1.0.0 // indirect
+       sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
 )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-gather-0.11.0/go.sum 
new/kubectl-gather-0.12.0/go.sum
--- old/kubectl-gather-0.11.0/go.sum    2025-10-22 14:55:18.000000000 +0200
+++ new/kubectl-gather-0.12.0/go.sum    2026-03-26 11:26:48.000000000 +0100
@@ -1,5 +1,7 @@
 github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 
h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
 github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod 
h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
+github.com/aymanbagabas/go-udiff v0.4.1 
h1:OEIrQ8maEeDBXQDoGCbbTTXYJMYRCRO1fnodZ12Gv5o=
+github.com/aymanbagabas/go-udiff v0.4.1/go.mod 
h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w=
 github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod 
h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
 github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
 github.com/creack/pty v1.1.18/go.mod 
h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
@@ -23,22 +25,16 @@
 github.com/go-task/slim-sprig/v3 v3.0.0/go.mod 
h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod 
h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/golang/protobuf v1.5.4 
h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
-github.com/golang/protobuf v1.5.4/go.mod 
h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
-github.com/google/gnostic-models v0.6.8 
h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
-github.com/google/gnostic-models v0.6.8/go.mod 
h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
+github.com/google/gnostic-models v0.6.9 
h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
+github.com/google/gnostic-models v0.6.9/go.mod 
h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
 github.com/google/go-cmp v0.5.9/go.mod 
h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
-github.com/google/go-cmp v0.6.0/go.mod 
h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod 
h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
 github.com/google/gofuzz v1.0.0/go.mod 
h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
-github.com/google/gofuzz v1.2.0/go.mod 
h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 
h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k=
-github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod 
h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
+github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db 
h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
+github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod 
h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod 
h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/imdario/mergo v0.3.16 
h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
-github.com/imdario/mergo v0.3.16/go.mod 
h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
 github.com/inconshreveable/mousetrap v1.1.0 
h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod 
h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/josharian/intern v1.0.0 
h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@@ -64,26 +60,27 @@
 github.com/modern-go/reflect2 v1.0.2/go.mod 
h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 
h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod 
h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/onsi/ginkgo/v2 v2.20.0 
h1:PE84V2mHqoT1sglvHc8ZdQtPcwmvvt29WLEEO3xmdZw=
-github.com/onsi/ginkgo/v2 v2.20.0/go.mod 
h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI=
-github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
-github.com/onsi/gomega v1.34.1/go.mod 
h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
+github.com/onsi/ginkgo/v2 v2.21.0 
h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
+github.com/onsi/ginkgo/v2 v2.21.0/go.mod 
h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
+github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
+github.com/onsi/gomega v1.35.1/go.mod 
h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod 
h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 
h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod 
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 
h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
-github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod 
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/rogpeppe/go-internal v1.12.0 
h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
-github.com/rogpeppe/go-internal v1.12.0/go.mod 
h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
+github.com/rogpeppe/go-internal v1.13.1 
h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
+github.com/rogpeppe/go-internal v1.13.1/go.mod 
h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod 
h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
-github.com/spf13/cobra v1.9.1/go.mod 
h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
-github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
-github.com/spf13/pflag v1.0.6/go.mod 
h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
+github.com/spf13/cobra v1.10.2/go.mod 
h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
+github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
+github.com/spf13/pflag v1.0.9/go.mod 
h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/stretchr/objx v0.1.0/go.mod 
h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod 
h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
 github.com/stretchr/testify v1.3.0/go.mod 
h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.9.0 
h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
-github.com/stretchr/testify v1.9.0/go.mod 
h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.10.0 
h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod 
h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
 github.com/x448/float16 v0.8.4/go.mod 
h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
 github.com/yuin/goldmark v1.1.27/go.mod 
h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -92,21 +89,22 @@
 go.uber.org/goleak v1.3.0/go.mod 
h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
 go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
 go.uber.org/multierr v1.11.0/go.mod 
h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
-go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
-go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
+go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.yaml.in/yaml/v3 v3.0.4/go.mod 
h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod 
h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod 
h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod 
h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 
h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
-golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod 
h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
+golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
+golang.org/x/crypto v0.49.0/go.mod 
h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod 
h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod 
h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod 
h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod 
h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
-golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
+golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
+golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
 golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
 golang.org/x/oauth2 v0.27.0/go.mod 
h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -116,28 +114,28 @@
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
-golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
-golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
-golang.org/x/term v0.30.0/go.mod 
h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
+golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
+golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
+golang.org/x/term v0.41.0/go.mod 
h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
-golang.org/x/text v0.23.0/go.mod 
h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
-golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
-golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
+golang.org/x/text v0.35.0/go.mod 
h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
+golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
+golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod 
h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod 
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod 
h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod 
h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
-golang.org/x/tools v0.24.0/go.mod 
h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
+golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
+golang.org/x/tools v0.42.0/go.mod 
h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/protobuf v1.34.2 
h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
-google.golang.org/protobuf v1.34.2/go.mod 
h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
+google.golang.org/protobuf v1.36.5 
h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
+google.golang.org/protobuf v1.36.5/go.mod 
h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c 
h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod 
h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -145,28 +143,28 @@
 gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod 
h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
 gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
-gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU=
-k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI=
-k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U=
-k8s.io/apimachinery v0.31.1/go.mod 
h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
-k8s.io/cli-runtime v0.31.1 h1:/ZmKhmZ6hNqDM+yf9s3Y4KEYakNXUn5sod2LWGGwCuk=
-k8s.io/cli-runtime v0.31.1/go.mod 
h1:pKv1cDIaq7ehWGuXQ+A//1OIF+7DI+xudXtExMCbe9U=
-k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0=
-k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg=
+k8s.io/api v0.33.10 h1:8OHPP+ybXl9UKq1gpqvQPvfnwDtilBqfCY3+LQl4TK4=
+k8s.io/api v0.33.10/go.mod h1:pVsaVIqAFpx/8NDmICPI0VOgnIc6oZJoGCpOgXrPGxY=
+k8s.io/apimachinery v0.33.10 h1:XHdu/6MeJHzh7H2y31XVOL5GIYjP/tH5jcHofNYWaDc=
+k8s.io/apimachinery v0.33.10/go.mod 
h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
+k8s.io/cli-runtime v0.33.10 h1:4V3wwsEhp+9edaXyO71PuQXpaIP5Xcrl3aLfk1nA9RE=
+k8s.io/cli-runtime v0.33.10/go.mod 
h1:t3UsILXibAluoLrWc8PLr7trFoGsFI+Dq/3ZVAvgaPQ=
+k8s.io/client-go v0.33.10 h1:Q3KkTB0zPnGlrs6mS2p6LaJ2zM5ehBVz0JO9ZKQied0=
+k8s.io/client-go v0.33.10/go.mod 
h1:pEg4JnHgHR+m6tZ23SUWj+md2iPdszmVG8KD9/pHtec=
 k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
 k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
-k8s.io/kube-openapi v0.0.0-20240816214639-573285566f34 
h1:/amS69DLm09mtbFtN3+LyygSFohnYGMseF8iv+2zulg=
-k8s.io/kube-openapi v0.0.0-20240816214639-573285566f34/go.mod 
h1:G0W3eI9gG219NHRq3h5uQaRBl4pj4ZpwzRP5ti8y770=
-k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 
h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
-k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod 
h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
-sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd 
h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
-sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod 
h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
-sigs.k8s.io/structured-merge-diff/v4 v4.4.1 
h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
-sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod 
h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
+k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff 
h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
+k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod 
h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
+k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 
h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
+k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod 
h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 
h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
+sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod 
h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
+sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod 
h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
+sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
+sigs.k8s.io/randfill v1.0.0/go.mod 
h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
+sigs.k8s.io/structured-merge-diff/v4 v4.6.0 
h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc=
+sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod 
h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
 sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
 sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-gather-0.11.0/pkg/gather/agent.go 
new/kubectl-gather-0.12.0/pkg/gather/agent.go
--- old/kubectl-gather-0.11.0/pkg/gather/agent.go       2025-10-22 
14:55:18.000000000 +0200
+++ new/kubectl-gather-0.12.0/pkg/gather/agent.go       2026-03-26 
11:26:48.000000000 +0100
@@ -81,21 +81,20 @@
 
 type agentWatcher struct {
        agent *AgentPod
-       ctx   context.Context
 }
 
-func (w *agentWatcher) Watch(opts metav1.ListOptions) (watch.Interface, error) 
{
+func (w *agentWatcher) WatchWithContext(ctx context.Context, opts 
metav1.ListOptions) (watch.Interface, error) {
        w.agent.Log.Debugf("Watching agent pod %q", w.agent)
        opts.FieldSelector = 
fields.OneTermEqualSelector(metav1.ObjectNameField, w.agent.Pod.Name).String()
-       return w.agent.Client.CoreV1().Pods(w.agent.Pod.Namespace).Watch(w.ctx, 
opts)
+       return w.agent.Client.CoreV1().Pods(w.agent.Pod.Namespace).Watch(ctx, 
opts)
 }
 
 func (a *AgentPod) WaitUntilRunning() error {
        ctx, cancel := context.WithTimeout(context.Background(), 
agentPodTimeoutSeconds*time.Second)
        defer cancel()
 
-       w := agentWatcher{agent: a, ctx: ctx}
-       watcher, err := toolswatch.NewRetryWatcher(a.Pod.ResourceVersion, &w)
+       w := agentWatcher{agent: a}
+       watcher, err := toolswatch.NewRetryWatcherWithContext(ctx, 
a.Pod.ResourceVersion, &w)
        if err != nil {
                return err
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-gather-0.11.0/pkg/gather/gather.go 
new/kubectl-gather-0.12.0/pkg/gather/gather.go
--- old/kubectl-gather-0.11.0/pkg/gather/gather.go      2025-10-22 
14:55:18.000000000 +0200
+++ new/kubectl-gather-0.12.0/pkg/gather/gather.go      2026-03-26 
11:26:48.000000000 +0100
@@ -44,6 +44,7 @@
        Namespaces []string
        Addons     []string
        Cluster    bool
+       Salt       Salt
        Log        *zap.SugaredLogger
 }
 
@@ -103,6 +104,11 @@
                return nil, err
        }
 
+       // Generate a random salt if the Salt option is not set.
+       if opts.Salt == (Salt{}) {
+               opts.Salt = RandomSalt()
+       }
+
        g := &Gatherer{
                config:       config,
                httpClient:   httpClient,
@@ -450,6 +456,8 @@
 }
 
 func (g *Gatherer) dumpResource(r *resourceInfo, item 
*unstructured.Unstructured) error {
+       g.sanitizeResource(item)
+
        dst, err := g.createResource(r, item)
        if err != nil {
                return err
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-gather-0.11.0/pkg/gather/sanitize.go 
new/kubectl-gather-0.12.0/pkg/gather/sanitize.go
--- old/kubectl-gather-0.11.0/pkg/gather/sanitize.go    1970-01-01 
01:00:00.000000000 +0100
+++ new/kubectl-gather-0.12.0/pkg/gather/sanitize.go    2026-03-26 
11:26:48.000000000 +0100
@@ -0,0 +1,104 @@
+// SPDX-FileCopyrightText: The kubectl-gather authors
+// SPDX-License-Identifier: Apache-2.0
+
+package gather
+
+import (
+       "crypto/rand"
+       "crypto/sha256"
+       "encoding/base64"
+
+       "golang.org/x/crypto/pbkdf2"
+       "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+)
+
+// Salt is a 16-byte (128-bit) salt for PBKDF2 hashing.
+type Salt [16]byte
+
+const (
+       // OWASP recommended iterations for PBKDF2-HMAC-SHA256, needed to 
protect
+       // weak passwords in user applications. Could use fewer iterations if we
+       // know secrets are random keys (e.g. ceph keys).
+       // 
https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2
+       pbkdf2Iterations = 600_000
+
+       // sanitizedAnnotation is added to sanitized secrets with the 
base64-encoded
+       // salt. Prevents double-hashing on repeated runs and makes the salt 
visible
+       // in the gathered output for verification.
+       sanitizedAnnotation = "kubectl-gather.nirs.github.com/sanitized"
+
+       lastAppliedConfigAnnotation = 
"kubectl.kubernetes.io/last-applied-configuration"
+)
+
+// RandomSalt generates a cryptographically secure random salt.
+// It may panic if the system cannot provide random data.
+func RandomSalt() Salt {
+       var salt Salt
+       // rand.Read panics if the underlying OS returns an error.
+       _, _ = rand.Read(salt[:])
+       return salt
+}
+
+// HashValue returns the PBKDF2-HMAC-SHA256 hash of data with the given salt.
+func HashValue(data []byte, salt Salt) []byte {
+       return pbkdf2.Key(data, salt[:], pbkdf2Iterations, sha256.Size, 
sha256.New)
+}
+
+// sanitizeResource modifies the resource in place, replacing sensitive data
+// with deterministic hashes. Currently handles Secret resources, other 
resource
+// types pass through unchanged.
+func (g *Gatherer) sanitizeResource(item *unstructured.Unstructured) {
+       switch item.GetKind() {
+       case "Secret":
+               g.sanitizeSecret(item)
+       }
+}
+
+// sanitizeSecret hashes secret data values and strips the
+// last-applied-configuration annotation.
+func (g *Gatherer) sanitizeSecret(item *unstructured.Unstructured) {
+       log := g.opts.Log
+       obj := item.Object
+       name := item.GetName()
+       salt := g.opts.Salt
+       saltB64 := base64.StdEncoding.EncodeToString(salt[:])
+
+       // If already sanitized, skip to avoid double hashing.
+       existing, found, _ := unstructured.NestedString(obj, "metadata", 
"annotations", sanitizedAnnotation)
+       if found {
+               if existing != saltB64 {
+                       log.Warnf("Secret %q: already sanitized with different 
salt %q", name, existing)
+               }
+               return
+       }
+
+       // Replace each secret value with a deterministic PBKDF2 hash.
+       data, found, err := unstructured.NestedStringMap(obj, "data")
+       if err != nil {
+               log.Warnf("Secret %q: failed to read data, removing data: %s", 
name, err)
+               unstructured.RemoveNestedField(obj, "data")
+       }
+       if found {
+               hashed := make(map[string]string, len(data))
+               for key, value := range data {
+                       raw, err := base64.StdEncoding.DecodeString(value)
+                       if err != nil {
+                               log.Warnf("Secret %q: dropping key %q: invalid 
base64: %s", name, key, err)
+                               continue
+                       }
+                       h := HashValue(raw, salt)
+                       hashed[key] = base64.StdEncoding.EncodeToString(h)
+               }
+               if err := unstructured.SetNestedStringMap(obj, hashed, "data"); 
err != nil {
+                       log.Warnf("Secret %q: failed to set sanitized data: 
%s", name, err)
+               }
+       }
+
+       // Remove last-applied-configuration which may contain plaintext secret 
data.
+       unstructured.RemoveNestedField(obj, "metadata", "annotations", 
lastAppliedConfigAnnotation)
+
+       // Mark the secret as sanitized with the salt used.
+       if err := unstructured.SetNestedField(obj, saltB64, "metadata", 
"annotations", sanitizedAnnotation); err != nil {
+               log.Warnf("Secret %q: failed to set sanitized annotation: %s", 
name, err)
+       }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-gather-0.11.0/pkg/gather/sanitize_test.go 
new/kubectl-gather-0.12.0/pkg/gather/sanitize_test.go
--- old/kubectl-gather-0.11.0/pkg/gather/sanitize_test.go       1970-01-01 
01:00:00.000000000 +0100
+++ new/kubectl-gather-0.12.0/pkg/gather/sanitize_test.go       2026-03-26 
11:26:48.000000000 +0100
@@ -0,0 +1,149 @@
+// SPDX-FileCopyrightText: The kubectl-gather authors
+// SPDX-License-Identifier: Apache-2.0
+
+package gather
+
+import (
+       "os"
+       "path/filepath"
+       "testing"
+
+       "github.com/aymanbagabas/go-udiff"
+       "go.uber.org/zap"
+       "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+       "sigs.k8s.io/yaml"
+)
+
+var testSalt = Salt{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
+
+func TestSanitizeResource(t *testing.T) {
+       tests := []string{
+               "secret-with-data",
+               "secret-with-annotations",
+               "secret-empty",
+               "configmap",
+       }
+
+       g := newTestGatherer(testSalt)
+
+       for _, name := range tests {
+               t.Run(name, func(t *testing.T) {
+                       obj := loadTestdata(t, name+".yaml")
+                       g.sanitizeResource(obj)
+                       actual := marshalYAML(t, obj.Object)
+
+                       golden := filepath.Join("testdata", "sanitize", 
name+".golden.yaml")
+                       expected, err := os.ReadFile(golden)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       if actual != string(expected) {
+                               t.Errorf("mismatch:\n%s", unifiedDiff(t, 
string(expected), actual))
+                       }
+               })
+       }
+}
+
+func TestSanitizeSecretIdempotent(t *testing.T) {
+       g := newTestGatherer(testSalt)
+
+       obj := loadTestdata(t, "secret-with-data.yaml")
+       g.sanitizeResource(obj)
+       expected := marshalYAML(t, obj.Object)
+
+       g.sanitizeResource(obj)
+       actual := marshalYAML(t, obj.Object)
+
+       if expected != actual {
+               t.Fatalf("sanitizing twice changed the secret:\n%s", 
unifiedDiff(t, expected, actual))
+       }
+}
+
+func TestSanitizeSecretDifferentSalts(t *testing.T) {
+       g1 := newTestGatherer(RandomSalt())
+       g2 := newTestGatherer(RandomSalt())
+
+       s1 := loadTestdata(t, "secret-with-data.yaml")
+       s2 := loadTestdata(t, "secret-with-data.yaml")
+
+       g1.sanitizeResource(s1)
+       g2.sanitizeResource(s2)
+
+       d1, found, err := unstructured.NestedStringMap(s1.Object, "data")
+       if err != nil || !found {
+               t.Fatalf("failed to get data from secret 1: err=%v, found=%v", 
err, found)
+       }
+       d2, found, err := unstructured.NestedStringMap(s2.Object, "data")
+       if err != nil || !found {
+               t.Fatalf("failed to get data from secret 2: err=%v, found=%v", 
err, found)
+       }
+
+       if d1["secret1"] == d2["secret1"] {
+               t.Fatalf("different salts must produce different hashes, both 
got %q", d1["secret1"])
+       }
+}
+
+func TestRandomSalt(t *testing.T) {
+       values := map[Salt]struct{}{}
+       for range 1000 {
+               values[RandomSalt()] = struct{}{}
+       }
+       if len(values) != 1000 {
+               t.Fatalf("duplicate random salt: got %d unique out of 1000", 
len(values))
+       }
+}
+
+func BenchmarkHashValue(b *testing.B) {
+       data := []byte("random secure key, 32 bytes long")
+       for b.Loop() {
+               HashValue(data, testSalt)
+       }
+}
+
+// Test helpers
+
+func newTestGatherer(salt Salt) *Gatherer {
+       return &Gatherer{
+               opts: &Options{
+                       Salt: salt,
+                       Log:  zap.NewNop().Sugar(),
+               },
+       }
+}
+
+func loadTestdata(t *testing.T, name string) *unstructured.Unstructured {
+       t.Helper()
+       data, err := os.ReadFile(filepath.Join("testdata", "sanitize", name))
+       if err != nil {
+               t.Fatal(err)
+       }
+       obj := &unstructured.Unstructured{}
+       if err := yaml.Unmarshal(data, &obj.Object); err != nil {
+               t.Fatal(err)
+       }
+       return obj
+}
+
+func marshalYAML(t *testing.T, obj any) string {
+       t.Helper()
+       data, err := yaml.Marshal(obj)
+       if err != nil {
+               t.Fatal(err)
+       }
+       return string(data)
+}
+
+func unifiedDiff(t *testing.T, expected, actual any) string {
+       t.Helper()
+       expectedString := marshal(t, expected)
+       actualString := marshal(t, actual)
+       return udiff.Unified("expected", "actual", expectedString, actualString)
+}
+
+func marshal(t *testing.T, obj any) string {
+       t.Helper()
+       if s, ok := obj.(string); ok {
+               return s
+       }
+       return marshalYAML(t, obj)
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/configmap.golden.yaml 
new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/configmap.golden.yaml
--- 
old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/configmap.golden.yaml    
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/configmap.golden.yaml    
    2026-03-26 11:26:48.000000000 +0100
@@ -0,0 +1,7 @@
+apiVersion: v1
+data:
+  config.yaml: 'key: value'
+kind: ConfigMap
+metadata:
+  name: test-configmap
+  namespace: default
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/configmap.yaml 
new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/configmap.yaml
--- old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/configmap.yaml       
1970-01-01 01:00:00.000000000 +0100
+++ new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/configmap.yaml       
2026-03-26 11:26:48.000000000 +0100
@@ -0,0 +1,7 @@
+apiVersion: v1
+data:
+  config.yaml: 'key: value'
+kind: ConfigMap
+metadata:
+  name: test-configmap
+  namespace: default
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/secret-empty.golden.yaml 
new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/secret-empty.golden.yaml
--- 
old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/secret-empty.golden.yaml 
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/secret-empty.golden.yaml 
    2026-03-26 11:26:48.000000000 +0100
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: Secret
+metadata:
+  annotations:
+    kubectl-gather.nirs.github.com/sanitized: AQIDBAUGBwgJCgsMDQ4PEA==
+  name: test-secret
+  namespace: default
+type: Opaque
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/secret-empty.yaml 
new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/secret-empty.yaml
--- old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/secret-empty.yaml    
1970-01-01 01:00:00.000000000 +0100
+++ new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/secret-empty.yaml    
2026-03-26 11:26:48.000000000 +0100
@@ -0,0 +1,6 @@
+apiVersion: v1
+kind: Secret
+metadata:
+  name: test-secret
+  namespace: default
+type: Opaque
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/secret-with-annotations.golden.yaml
 
new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/secret-with-annotations.golden.yaml
--- 
old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/secret-with-annotations.golden.yaml
  1970-01-01 01:00:00.000000000 +0100
+++ 
new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/secret-with-annotations.golden.yaml
  2026-03-26 11:26:48.000000000 +0100
@@ -0,0 +1,11 @@
+apiVersion: v1
+data:
+  secret1: vfit80gH2RKCit7Rsa/lv6wGJa5cgA+4Q5WyyOQJIDI=
+kind: Secret
+metadata:
+  annotations:
+    app.kubernetes.io/name: my-app
+    kubectl-gather.nirs.github.com/sanitized: AQIDBAUGBwgJCgsMDQ4PEA==
+  name: test-secret
+  namespace: default
+type: Opaque
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/secret-with-annotations.yaml
 
new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/secret-with-annotations.yaml
--- 
old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/secret-with-annotations.yaml
 1970-01-01 01:00:00.000000000 +0100
+++ 
new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/secret-with-annotations.yaml
 2026-03-26 11:26:48.000000000 +0100
@@ -0,0 +1,11 @@
+apiVersion: v1
+data:
+  secret1: c2VjcmV0MQ==
+kind: Secret
+metadata:
+  annotations:
+    app.kubernetes.io/name: my-app
+    kubectl.kubernetes.io/last-applied-configuration: 
'{"apiVersion":"v1","data":{"secret1":"c2VjcmV0"},"kind":"Secret"}'
+  name: test-secret
+  namespace: default
+type: Opaque
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/secret-with-data.golden.yaml
 
new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/secret-with-data.golden.yaml
--- 
old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/secret-with-data.golden.yaml
 1970-01-01 01:00:00.000000000 +0100
+++ 
new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/secret-with-data.golden.yaml
 2026-03-26 11:26:48.000000000 +0100
@@ -0,0 +1,12 @@
+apiVersion: v1
+data:
+  duplicate: vfit80gH2RKCit7Rsa/lv6wGJa5cgA+4Q5WyyOQJIDI=
+  secret1: vfit80gH2RKCit7Rsa/lv6wGJa5cgA+4Q5WyyOQJIDI=
+  secret2: eSWghhz+7r7TPKmMN9Gv50RVTXd/HpoEjwQXcBDFEZA=
+kind: Secret
+metadata:
+  annotations:
+    kubectl-gather.nirs.github.com/sanitized: AQIDBAUGBwgJCgsMDQ4PEA==
+  name: test-secret
+  namespace: default
+type: Opaque
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/secret-with-data.yaml 
new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/secret-with-data.yaml
--- 
old/kubectl-gather-0.11.0/pkg/gather/testdata/sanitize/secret-with-data.yaml    
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/kubectl-gather-0.12.0/pkg/gather/testdata/sanitize/secret-with-data.yaml    
    2026-03-26 11:26:48.000000000 +0100
@@ -0,0 +1,10 @@
+apiVersion: v1
+data:
+  duplicate: c2VjcmV0MQ==
+  secret1: c2VjcmV0MQ==
+  secret2: c2VjcmV0Mg==
+kind: Secret
+metadata:
+  name: test-secret
+  namespace: default
+type: Opaque

++++++ kubectl-gather.obsinfo ++++++
--- /var/tmp/diff_new_pack.fjC22s/_old  2026-03-27 06:42:35.876864195 +0100
+++ /var/tmp/diff_new_pack.fjC22s/_new  2026-03-27 06:42:35.876864195 +0100
@@ -1,5 +1,5 @@
 name: kubectl-gather
-version: 0.11.0
-mtime: 1761137718
-commit: c42a9ee16b240236e0cb67809e810e028176d0ad
+version: 0.12.0
+mtime: 1774520808
+commit: a0e7607071c92e7d7c8b1902eafb0dcd39f392dc
 

++++++ vendor.tar.gz ++++++
++++ 266676 lines of diff (skipped)

Reply via email to