From 01680b12abb29097be59a7248b48af134d03ed56 Mon Sep 17 00:00:00 2001 From: yaara sirkis Date: Thu, 18 Jun 2026 14:48:26 +0300 Subject: [PATCH 01/10] add CSI information to discovery-agent --- .../disco-agent/templates/configmap.yaml | 14 ++ deploy/charts/disco-agent/templates/rbac.yaml | 28 +++ internal/cyberark/dataupload/dataupload.go | 4 + pkg/client/client_cyberark.go | 6 + ...lient_cyberark_convertdatareadings_test.go | 182 ++++++++++++++++++ pkg/client/client_cyberark_test.go | 2 + pkg/datagatherer/k8sdynamic/dynamic.go | 8 + pkg/datagatherer/k8sdynamic/fieldfilter.go | 45 +++++ .../k8sdynamic/fieldfilter_test.go | 116 +++++++++++ 9 files changed, 405 insertions(+) diff --git a/deploy/charts/disco-agent/templates/configmap.yaml b/deploy/charts/disco-agent/templates/configmap.yaml index e88ae910..3d38b542 100644 --- a/deploy/charts/disco-agent/templates/configmap.yaml +++ b/deploy/charts/disco-agent/templates/configmap.yaml @@ -145,3 +145,17 @@ data: group: external-secrets.io version: v1 resource: clustersecretstores + - kind: k8s-dynamic + name: ark/secretproviderclasses + config: + resource-type: + group: secrets-store.csi.x-k8s.io + version: v1 + resource: secretproviderclasses + - kind: k8s-dynamic + name: ark/secretproviderclasspodstatuses + config: + resource-type: + group: secrets-store.csi.x-k8s.io + version: v1 + resource: secretproviderclasspodstatuses diff --git a/deploy/charts/disco-agent/templates/rbac.yaml b/deploy/charts/disco-agent/templates/rbac.yaml index 92bd1349..844ddf1e 100644 --- a/deploy/charts/disco-agent/templates/rbac.yaml +++ b/deploy/charts/disco-agent/templates/rbac.yaml @@ -140,3 +140,31 @@ subjects: - kind: ServiceAccount name: {{ include "disco-agent.serviceAccountName" . }} namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "disco-agent.fullname" . }}-csi-reader + labels: + {{- include "disco-agent.labels" . | nindent 4 }} +rules: + - apiGroups: ["secrets-store.csi.x-k8s.io"] + resources: + - secretproviderclasses + - secretproviderclasspodstatuses + verbs: ["get", "list", "watch"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "disco-agent.fullname" . }}-csi-reader + labels: + {{- include "disco-agent.labels" . | nindent 4 }} +roleRef: + kind: ClusterRole + name: {{ include "disco-agent.fullname" . }}-csi-reader + apiGroup: rbac.authorization.k8s.io +subjects: + - kind: ServiceAccount + name: {{ include "disco-agent.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} diff --git a/internal/cyberark/dataupload/dataupload.go b/internal/cyberark/dataupload/dataupload.go index 27e8e856..b3043c3b 100644 --- a/internal/cyberark/dataupload/dataupload.go +++ b/internal/cyberark/dataupload/dataupload.go @@ -88,6 +88,10 @@ type Snapshot struct { ClusterExternalSecrets []runtime.Object `json:"clusterexternalsecrets"` // ClusterSecretStores is a list of ClusterSecretStore resources in the cluster. ClusterSecretStores []runtime.Object `json:"clustersecretstores"` + // SecretProviderClasses is a list of SecretProviderClass resources in the cluster. + SecretProviderClasses []runtime.Object `json:"secretproviderclasses"` + // SecretProviderClassPodStatuses is a list of SecretProviderClassPodStatus resources in the cluster. + SecretProviderClassPodStatuses []runtime.Object `json:"secretproviderclasspodstatuses"` // Roles is a list of Role resources in the cluster. Roles []runtime.Object `json:"roles"` // ClusterRoles is a list of ClusterRole resources in the cluster. diff --git a/pkg/client/client_cyberark.go b/pkg/client/client_cyberark.go index 3232e7c6..6de22791 100644 --- a/pkg/client/client_cyberark.go +++ b/pkg/client/client_cyberark.go @@ -244,6 +244,12 @@ var defaultExtractorFunctions = map[string]func(*api.DataReading, *dataupload.Sn "ark/esoclustersecretstores": func(r *api.DataReading, s *dataupload.Snapshot) error { return extractResourceListFromReading(r, &s.ClusterSecretStores) }, + "ark/secretproviderclasses": func(r *api.DataReading, s *dataupload.Snapshot) error { + return extractResourceListFromReading(r, &s.SecretProviderClasses) + }, + "ark/secretproviderclasspodstatuses": func(r *api.DataReading, s *dataupload.Snapshot) error { + return extractResourceListFromReading(r, &s.SecretProviderClassPodStatuses) + }, } // convertDataReadings processes a list of DataReadings using the provided diff --git a/pkg/client/client_cyberark_convertdatareadings_test.go b/pkg/client/client_cyberark_convertdatareadings_test.go index 293629c9..de1a598b 100644 --- a/pkg/client/client_cyberark_convertdatareadings_test.go +++ b/pkg/client/client_cyberark_convertdatareadings_test.go @@ -815,6 +815,188 @@ func TestConvertDataReadings_ClusterSecretStores(t *testing.T) { assert.Equal(t, "aws-cluster-secret-store", css2.GetName()) } +// TestConvertDataReadings_SecretProviderClasses tests that secretproviderclasses are correctly converted. +func TestConvertDataReadings_SecretProviderClasses(t *testing.T) { + extractorFunctions := map[string]func(*api.DataReading, *dataupload.Snapshot) error{ + "ark/discovery": extractClusterIDAndServerVersionFromReading, + "ark/secretproviderclasses": func(reading *api.DataReading, snapshot *dataupload.Snapshot) error { + return extractResourceListFromReading(reading, &snapshot.SecretProviderClasses) + }, + } + + readings := []*api.DataReading{ + { + DataGatherer: "ark/discovery", + Data: &api.DiscoveryData{ + ClusterID: "test-cluster-id", + ServerVersion: &version.Info{ + GitVersion: "v1.21.0", + }, + }, + }, + { + DataGatherer: "ark/secretproviderclasses", + Data: &api.DynamicData{ + Items: []*api.GatheredResource{ + { + Resource: &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": "secrets-store.csi.x-k8s.io/v1", + "kind": "SecretProviderClass", + "metadata": map[string]any{ + "name": "conjur-spc", + "namespace": "default", + }, + "spec": map[string]any{ + "provider": "conjur", + }, + }, + }, + }, + { + Resource: &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": "secrets-store.csi.x-k8s.io/v1", + "kind": "SecretProviderClass", + "metadata": map[string]any{ + "name": "vault-spc", + "namespace": "default", + }, + "spec": map[string]any{ + "provider": "vault", + }, + }, + }, + }, + // Deleted secretproviderclass should be ignored + { + DeletedAt: api.Time{Time: time.Now()}, + Resource: &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": "secrets-store.csi.x-k8s.io/v1", + "kind": "SecretProviderClass", + "metadata": map[string]any{ + "name": "deleted-spc", + "namespace": "default", + }, + }, + }, + }, + }, + }, + }, + } + + var snapshot dataupload.Snapshot + err := convertDataReadings(extractorFunctions, readings, &snapshot) + require.NoError(t, err) + + assert.Equal(t, "test-cluster-id", snapshot.ClusterID) + require.Len(t, snapshot.SecretProviderClasses, 2, "should have 2 secretproviderclasses (deleted one should be excluded)") + + spc1, ok := snapshot.SecretProviderClasses[0].(*unstructured.Unstructured) + require.True(t, ok, "secretproviderclass should be unstructured") + assert.Equal(t, "SecretProviderClass", spc1.GetKind()) + assert.Equal(t, "conjur-spc", spc1.GetName()) + + spc2, ok := snapshot.SecretProviderClasses[1].(*unstructured.Unstructured) + require.True(t, ok, "secretproviderclass should be unstructured") + assert.Equal(t, "SecretProviderClass", spc2.GetKind()) + assert.Equal(t, "vault-spc", spc2.GetName()) +} + +// TestConvertDataReadings_SecretProviderClassPodStatuses tests that secretproviderclasspodstatuses are correctly converted. +func TestConvertDataReadings_SecretProviderClassPodStatuses(t *testing.T) { + extractorFunctions := map[string]func(*api.DataReading, *dataupload.Snapshot) error{ + "ark/discovery": extractClusterIDAndServerVersionFromReading, + "ark/secretproviderclasspodstatuses": func(reading *api.DataReading, snapshot *dataupload.Snapshot) error { + return extractResourceListFromReading(reading, &snapshot.SecretProviderClassPodStatuses) + }, + } + + readings := []*api.DataReading{ + { + DataGatherer: "ark/discovery", + Data: &api.DiscoveryData{ + ClusterID: "test-cluster-id", + ServerVersion: &version.Info{ + GitVersion: "v1.21.0", + }, + }, + }, + { + DataGatherer: "ark/secretproviderclasspodstatuses", + Data: &api.DynamicData{ + Items: []*api.GatheredResource{ + { + Resource: &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": "secrets-store.csi.x-k8s.io/v1", + "kind": "SecretProviderClassPodStatus", + "metadata": map[string]any{ + "name": "my-pod-conjur-spc", + "namespace": "default", + }, + "status": map[string]any{ + "mounted": true, + "podName": "my-pod", + }, + }, + }, + }, + { + Resource: &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": "secrets-store.csi.x-k8s.io/v1", + "kind": "SecretProviderClassPodStatus", + "metadata": map[string]any{ + "name": "other-pod-conjur-spc", + "namespace": "default", + }, + "status": map[string]any{ + "mounted": false, + "podName": "other-pod", + }, + }, + }, + }, + // Deleted secretproviderclasspodstatus should be ignored + { + DeletedAt: api.Time{Time: time.Now()}, + Resource: &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": "secrets-store.csi.x-k8s.io/v1", + "kind": "SecretProviderClassPodStatus", + "metadata": map[string]any{ + "name": "deleted-pod-spc", + "namespace": "default", + }, + }, + }, + }, + }, + }, + }, + } + + var snapshot dataupload.Snapshot + err := convertDataReadings(extractorFunctions, readings, &snapshot) + require.NoError(t, err) + + assert.Equal(t, "test-cluster-id", snapshot.ClusterID) + require.Len(t, snapshot.SecretProviderClassPodStatuses, 2, "should have 2 secretproviderclasspodstatuses (deleted one should be excluded)") + + spcps1, ok := snapshot.SecretProviderClassPodStatuses[0].(*unstructured.Unstructured) + require.True(t, ok, "secretproviderclasspodstatus should be unstructured") + assert.Equal(t, "SecretProviderClassPodStatus", spcps1.GetKind()) + assert.Equal(t, "my-pod-conjur-spc", spcps1.GetName()) + + spcps2, ok := snapshot.SecretProviderClassPodStatuses[1].(*unstructured.Unstructured) + require.True(t, ok, "secretproviderclasspodstatus should be unstructured") + assert.Equal(t, "SecretProviderClassPodStatus", spcps2.GetKind()) + assert.Equal(t, "other-pod-conjur-spc", spcps2.GetName()) +} + // TestConvertDataReadings_ServiceAccounts tests that serviceaccounts are correctly converted. func TestConvertDataReadings_ServiceAccounts(t *testing.T) { extractorFunctions := map[string]func(*api.DataReading, *dataupload.Snapshot) error{ diff --git a/pkg/client/client_cyberark_test.go b/pkg/client/client_cyberark_test.go index 4931c376..cdbdc0fb 100644 --- a/pkg/client/client_cyberark_test.go +++ b/pkg/client/client_cyberark_test.go @@ -91,6 +91,8 @@ var defaultDynamicDatagathererNames = []string{ "ark/esosecretstores", "ark/esoclusterexternalsecrets", "ark/esoclustersecretstores", + "ark/secretproviderclasses", + "ark/secretproviderclasspodstatuses", "ark/roles", "ark/clusterroles", "ark/rolebindings", diff --git a/pkg/datagatherer/k8sdynamic/dynamic.go b/pkg/datagatherer/k8sdynamic/dynamic.go index 6eed8db2..9c35a803 100644 --- a/pkg/datagatherer/k8sdynamic/dynamic.go +++ b/pkg/datagatherer/k8sdynamic/dynamic.go @@ -572,6 +572,14 @@ func (g *DataGathererDynamic) redactList(ctx context.Context, list []*api.Gather if err := Select(RouteSelectedFields, resource); err != nil { return err } + } else if gvk.Kind == "SecretProviderClass" && gvk.Group == "secrets-store.csi.x-k8s.io" { + if err := Select(SecretProviderClassSelectedFields, resource); err != nil { + return err + } + } else if gvk.Kind == "SecretProviderClassPodStatus" && gvk.Group == "secrets-store.csi.x-k8s.io" { + if err := Select(SecretProviderClassPodStatusSelectedFields, resource); err != nil { + return err + } } } diff --git a/pkg/datagatherer/k8sdynamic/fieldfilter.go b/pkg/datagatherer/k8sdynamic/fieldfilter.go index 46e41118..ca79d98d 100644 --- a/pkg/datagatherer/k8sdynamic/fieldfilter.go +++ b/pkg/datagatherer/k8sdynamic/fieldfilter.go @@ -82,6 +82,51 @@ var RedactFields = []FieldPath{ {"metadata", "annotations", "banzaicloud.com/last-applied"}, } +// SecretProviderClassSelectedFields is the list of fields sent from SecretProviderClass objects to the +// backend. Only the provider, secretObjects, and known Conjur-specific parameters are retained. +// Other vendor-specific parameters may contain sensitive material and are not collected. +var SecretProviderClassSelectedFields = []FieldPath{ + {"kind"}, + {"apiVersion"}, + {"metadata", "annotations"}, + {"metadata", "labels"}, + {"metadata", "name"}, + {"metadata", "namespace"}, + {"metadata", "uid"}, + {"metadata", "creationTimestamp"}, + {"metadata", "deletionTimestamp"}, + {"metadata", "resourceVersion"}, + + {"spec", "provider"}, + {"spec", "secretObjects"}, + + {"spec", "parameters", "applianceUrl"}, + {"spec", "parameters", "authnId"}, + {"spec", "parameters", "account"}, + {"spec", "parameters", "conjur.org/configurationVersion"}, + {"spec", "parameters", "sslCertificate"}, + {"spec", "parameters", "identity"}, +} + +// SecretProviderClassPodStatusSelectedFields is the list of fields sent from SecretProviderClassPodStatus +// objects to the backend. The full status block is retained as it contains only runtime metadata +// (mount status, pod name, Conjur variable paths) and no sensitive values. +var SecretProviderClassPodStatusSelectedFields = []FieldPath{ + {"kind"}, + {"apiVersion"}, + {"metadata", "annotations"}, + {"metadata", "labels"}, + {"metadata", "name"}, + {"metadata", "namespace"}, + {"metadata", "ownerReferences"}, + {"metadata", "uid"}, + {"metadata", "creationTimestamp"}, + {"metadata", "deletionTimestamp"}, + {"metadata", "resourceVersion"}, + + {"status"}, +} + type FieldPath []string // Select removes all but the supplied fields from the resource diff --git a/pkg/datagatherer/k8sdynamic/fieldfilter_test.go b/pkg/datagatherer/k8sdynamic/fieldfilter_test.go index 24cba144..7bc412f8 100644 --- a/pkg/datagatherer/k8sdynamic/fieldfilter_test.go +++ b/pkg/datagatherer/k8sdynamic/fieldfilter_test.go @@ -142,6 +142,122 @@ func TestSelect(t *testing.T) { }, )) + t.Run("secret-provider-class", run_TestSelect( + map[string]any{ + "apiVersion": "secrets-store.csi.x-k8s.io/v1", + "kind": "SecretProviderClass", + "metadata": map[string]any{ + "name": "conjur-spc", + "namespace": "example", + "uid": "fake-uid", + "resourceVersion": "fake-resource-version", + "creationTimestamp": "2025-08-15T00:00:01Z", + "deletionTimestamp": "2025-08-15T00:00:02Z", + // Should be dropped + "generation": 5, + }, + "spec": map[string]any{ + "provider": "conjur", + "secretObjects": []any{ + map[string]any{"secretName": "my-k8s-secret"}, + }, + "parameters": map[string]any{ + "applianceUrl": "https://conjur.example.com", + "authnId": "authn-jwt/my-service", + "account": "myorg", + "conjur.org/configurationVersion": "0.2.0", + "sslCertificate": "-----BEGIN CERTIFICATE-----...", + "identity": "host/my-host", + // Should be dropped + "someVendorSpecificField": "should-be-removed", + }, + }, + }, + SecretProviderClassSelectedFields, + map[string]any{ + "apiVersion": "secrets-store.csi.x-k8s.io/v1", + "kind": "SecretProviderClass", + "metadata": map[string]any{ + "name": "conjur-spc", + "namespace": "example", + "uid": "fake-uid", + "resourceVersion": "fake-resource-version", + "creationTimestamp": "2025-08-15T00:00:01Z", + "deletionTimestamp": "2025-08-15T00:00:02Z", + }, + "spec": map[string]any{ + "provider": "conjur", + "secretObjects": []any{ + map[string]any{"secretName": "my-k8s-secret"}, + }, + "parameters": map[string]any{ + "applianceUrl": "https://conjur.example.com", + "authnId": "authn-jwt/my-service", + "account": "myorg", + "conjur.org/configurationVersion": "0.2.0", + "sslCertificate": "-----BEGIN CERTIFICATE-----...", + "identity": "host/my-host", + }, + }, + }, + )) + + t.Run("secret-provider-class-pod-status", run_TestSelect( + map[string]any{ + "apiVersion": "secrets-store.csi.x-k8s.io/v1", + "kind": "SecretProviderClassPodStatus", + "metadata": map[string]any{ + "name": "conjur-spc-pod-status", + "namespace": "example", + "uid": "fake-uid", + "resourceVersion": "fake-resource-version", + "creationTimestamp": "2025-08-15T00:00:01Z", + "deletionTimestamp": "2025-08-15T00:00:02Z", + "ownerReferences": []any{ + map[string]any{"name": "my-pod"}, + }, + // Should be dropped + "generation": 3, + }, + "status": map[string]any{ + "mounted": true, + "podName": "my-pod", + "secretProviderClassName": "conjur-spc", + "targetPath": "/var/lib/kubelet/pods/fake-uid/volumes", + "objects": []any{ + map[string]any{"id": "data/secrets/username"}, + map[string]any{"id": "data/secrets/password"}, + }, + }, + }, + SecretProviderClassPodStatusSelectedFields, + map[string]any{ + "apiVersion": "secrets-store.csi.x-k8s.io/v1", + "kind": "SecretProviderClassPodStatus", + "metadata": map[string]any{ + "name": "conjur-spc-pod-status", + "namespace": "example", + "uid": "fake-uid", + "resourceVersion": "fake-resource-version", + "creationTimestamp": "2025-08-15T00:00:01Z", + "deletionTimestamp": "2025-08-15T00:00:02Z", + "ownerReferences": []any{ + map[string]any{"name": "my-pod"}, + }, + }, + "status": map[string]any{ + "mounted": true, + "podName": "my-pod", + "secretProviderClassName": "conjur-spc", + "targetPath": "/var/lib/kubelet/pods/fake-uid/volumes", + "objects": []any{ + map[string]any{"id": "data/secrets/username"}, + map[string]any{"id": "data/secrets/password"}, + }, + }, + }, + )) + t.Run("route", run_TestSelect( map[string]any{ "apiVersion": "v1", From 89d450dfd4f1c654b42c3d665d50eba88ac445c0 Mon Sep 17 00:00:00 2001 From: yaara sirkis Date: Sun, 21 Jun 2026 11:20:37 +0300 Subject: [PATCH 02/10] fix test --- .../__snapshot__/configmap_test.yaml.snap | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/deploy/charts/disco-agent/tests/__snapshot__/configmap_test.yaml.snap b/deploy/charts/disco-agent/tests/__snapshot__/configmap_test.yaml.snap index 3ee3c884..998d3082 100644 --- a/deploy/charts/disco-agent/tests/__snapshot__/configmap_test.yaml.snap +++ b/deploy/charts/disco-agent/tests/__snapshot__/configmap_test.yaml.snap @@ -133,6 +133,20 @@ custom-cluster-description: group: external-secrets.io version: v1 resource: clustersecretstores + - kind: k8s-dynamic + name: ark/secretproviderclasses + config: + resource-type: + group: secrets-store.csi.x-k8s.io + version: v1 + resource: secretproviderclasses + - kind: k8s-dynamic + name: ark/secretproviderclasspodstatuses + config: + resource-type: + group: secrets-store.csi.x-k8s.io + version: v1 + resource: secretproviderclasspodstatuses kind: ConfigMap metadata: labels: @@ -278,6 +292,20 @@ custom-cluster-name: group: external-secrets.io version: v1 resource: clustersecretstores + - kind: k8s-dynamic + name: ark/secretproviderclasses + config: + resource-type: + group: secrets-store.csi.x-k8s.io + version: v1 + resource: secretproviderclasses + - kind: k8s-dynamic + name: ark/secretproviderclasspodstatuses + config: + resource-type: + group: secrets-store.csi.x-k8s.io + version: v1 + resource: secretproviderclasspodstatuses kind: ConfigMap metadata: labels: @@ -423,6 +451,20 @@ custom-period: group: external-secrets.io version: v1 resource: clustersecretstores + - kind: k8s-dynamic + name: ark/secretproviderclasses + config: + resource-type: + group: secrets-store.csi.x-k8s.io + version: v1 + resource: secretproviderclasses + - kind: k8s-dynamic + name: ark/secretproviderclasspodstatuses + config: + resource-type: + group: secrets-store.csi.x-k8s.io + version: v1 + resource: secretproviderclasspodstatuses kind: ConfigMap metadata: labels: @@ -568,6 +610,20 @@ defaults: group: external-secrets.io version: v1 resource: clustersecretstores + - kind: k8s-dynamic + name: ark/secretproviderclasses + config: + resource-type: + group: secrets-store.csi.x-k8s.io + version: v1 + resource: secretproviderclasses + - kind: k8s-dynamic + name: ark/secretproviderclasspodstatuses + config: + resource-type: + group: secrets-store.csi.x-k8s.io + version: v1 + resource: secretproviderclasspodstatuses kind: ConfigMap metadata: labels: From b1a5d8805c154a833ddd2fe56d6440f0c2c07cea Mon Sep 17 00:00:00 2001 From: yaara sirkis Date: Sun, 21 Jun 2026 11:33:01 +0300 Subject: [PATCH 03/10] Rewrote the if-else chain as a switch statement. --- pkg/datagatherer/k8sdynamic/dynamic.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pkg/datagatherer/k8sdynamic/dynamic.go b/pkg/datagatherer/k8sdynamic/dynamic.go index 9c35a803..4d3f98b5 100644 --- a/pkg/datagatherer/k8sdynamic/dynamic.go +++ b/pkg/datagatherer/k8sdynamic/dynamic.go @@ -544,8 +544,8 @@ func (g *DataGathererDynamic) redactList(ctx context.Context, list []*api.Gather // Redact item if it is a Secret or a Route. for _, gvk := range gvks { - // secret object - if gvk.Kind == "Secret" && (gvk.Group == "core" || gvk.Group == "") { + switch { + case gvk.Kind == "Secret" && (gvk.Group == "core" || gvk.Group == ""): // Note: We must redact data field in all cases! // If encryption is enabled, we encrypt the data and preserve it, but we still need to redact later. // If encryption is enabled and _fails_, we MUST still redact the data field to avoid leaking sensitive information. @@ -567,16 +567,15 @@ func (g *DataGathererDynamic) redactList(ctx context.Context, list []*api.Gather if err := Select(secretSelectedFields, resource); err != nil { return err } - } else if gvk.Kind == "Route" && gvk.Group == "route.openshift.io" { - // route object + case gvk.Kind == "Route" && gvk.Group == "route.openshift.io": if err := Select(RouteSelectedFields, resource); err != nil { return err } - } else if gvk.Kind == "SecretProviderClass" && gvk.Group == "secrets-store.csi.x-k8s.io" { + case gvk.Kind == "SecretProviderClass" && gvk.Group == "secrets-store.csi.x-k8s.io": if err := Select(SecretProviderClassSelectedFields, resource); err != nil { return err } - } else if gvk.Kind == "SecretProviderClassPodStatus" && gvk.Group == "secrets-store.csi.x-k8s.io" { + case gvk.Kind == "SecretProviderClassPodStatus" && gvk.Group == "secrets-store.csi.x-k8s.io": if err := Select(SecretProviderClassPodStatusSelectedFields, resource); err != nil { return err } From 3ae8e2ab9a513835684b97060ef9af53fe993e99 Mon Sep 17 00:00:00 2001 From: YaaraSirkis <129045321+YaaraSirkis@users.noreply.github.com> Date: Mon, 22 Jun 2026 13:18:14 +0300 Subject: [PATCH 04/10] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- pkg/datagatherer/k8sdynamic/dynamic.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/datagatherer/k8sdynamic/dynamic.go b/pkg/datagatherer/k8sdynamic/dynamic.go index 4d3f98b5..21e0fa46 100644 --- a/pkg/datagatherer/k8sdynamic/dynamic.go +++ b/pkg/datagatherer/k8sdynamic/dynamic.go @@ -542,7 +542,7 @@ func (g *DataGathererDynamic) redactList(ctx context.Context, list []*api.Gather resource := item - // Redact item if it is a Secret or a Route. + // Redact item if it is a Secret, Route, SecretProviderClass, or SecretProviderClassPodStatus. for _, gvk := range gvks { switch { case gvk.Kind == "Secret" && (gvk.Group == "core" || gvk.Group == ""): From 9a3ef7ae3237dfdde02f3016d33dc3c005769a7e Mon Sep 17 00:00:00 2001 From: yaara sirkis Date: Mon, 22 Jun 2026 13:20:29 +0300 Subject: [PATCH 05/10] edit comment --- pkg/datagatherer/k8sdynamic/fieldfilter.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/datagatherer/k8sdynamic/fieldfilter.go b/pkg/datagatherer/k8sdynamic/fieldfilter.go index ca79d98d..5f90b54c 100644 --- a/pkg/datagatherer/k8sdynamic/fieldfilter.go +++ b/pkg/datagatherer/k8sdynamic/fieldfilter.go @@ -83,8 +83,8 @@ var RedactFields = []FieldPath{ } // SecretProviderClassSelectedFields is the list of fields sent from SecretProviderClass objects to the -// backend. Only the provider, secretObjects, and known Conjur-specific parameters are retained. -// Other vendor-specific parameters may contain sensitive material and are not collected. +// backend. Metadata is retained for identification; within spec only the provider, secretObjects, and known +// Conjur-specific parameters are retained. var SecretProviderClassSelectedFields = []FieldPath{ {"kind"}, {"apiVersion"}, From 265ed39c2ebb5a668dc1171c62c70fbdd62b17a0 Mon Sep 17 00:00:00 2001 From: yaara sirkis Date: Mon, 22 Jun 2026 16:30:51 +0300 Subject: [PATCH 06/10] remove the filter --- pkg/datagatherer/k8sdynamic/dynamic.go | 10 +- pkg/datagatherer/k8sdynamic/fieldfilter.go | 44 ------- .../k8sdynamic/fieldfilter_test.go | 116 ------------------ 3 files changed, 1 insertion(+), 169 deletions(-) diff --git a/pkg/datagatherer/k8sdynamic/dynamic.go b/pkg/datagatherer/k8sdynamic/dynamic.go index 21e0fa46..b5cb0da9 100644 --- a/pkg/datagatherer/k8sdynamic/dynamic.go +++ b/pkg/datagatherer/k8sdynamic/dynamic.go @@ -542,7 +542,7 @@ func (g *DataGathererDynamic) redactList(ctx context.Context, list []*api.Gather resource := item - // Redact item if it is a Secret, Route, SecretProviderClass, or SecretProviderClassPodStatus. + // Redact item if it is a Secret or Route. for _, gvk := range gvks { switch { case gvk.Kind == "Secret" && (gvk.Group == "core" || gvk.Group == ""): @@ -571,14 +571,6 @@ func (g *DataGathererDynamic) redactList(ctx context.Context, list []*api.Gather if err := Select(RouteSelectedFields, resource); err != nil { return err } - case gvk.Kind == "SecretProviderClass" && gvk.Group == "secrets-store.csi.x-k8s.io": - if err := Select(SecretProviderClassSelectedFields, resource); err != nil { - return err - } - case gvk.Kind == "SecretProviderClassPodStatus" && gvk.Group == "secrets-store.csi.x-k8s.io": - if err := Select(SecretProviderClassPodStatusSelectedFields, resource); err != nil { - return err - } } } diff --git a/pkg/datagatherer/k8sdynamic/fieldfilter.go b/pkg/datagatherer/k8sdynamic/fieldfilter.go index 5f90b54c..21fed3a1 100644 --- a/pkg/datagatherer/k8sdynamic/fieldfilter.go +++ b/pkg/datagatherer/k8sdynamic/fieldfilter.go @@ -82,50 +82,6 @@ var RedactFields = []FieldPath{ {"metadata", "annotations", "banzaicloud.com/last-applied"}, } -// SecretProviderClassSelectedFields is the list of fields sent from SecretProviderClass objects to the -// backend. Metadata is retained for identification; within spec only the provider, secretObjects, and known -// Conjur-specific parameters are retained. -var SecretProviderClassSelectedFields = []FieldPath{ - {"kind"}, - {"apiVersion"}, - {"metadata", "annotations"}, - {"metadata", "labels"}, - {"metadata", "name"}, - {"metadata", "namespace"}, - {"metadata", "uid"}, - {"metadata", "creationTimestamp"}, - {"metadata", "deletionTimestamp"}, - {"metadata", "resourceVersion"}, - - {"spec", "provider"}, - {"spec", "secretObjects"}, - - {"spec", "parameters", "applianceUrl"}, - {"spec", "parameters", "authnId"}, - {"spec", "parameters", "account"}, - {"spec", "parameters", "conjur.org/configurationVersion"}, - {"spec", "parameters", "sslCertificate"}, - {"spec", "parameters", "identity"}, -} - -// SecretProviderClassPodStatusSelectedFields is the list of fields sent from SecretProviderClassPodStatus -// objects to the backend. The full status block is retained as it contains only runtime metadata -// (mount status, pod name, Conjur variable paths) and no sensitive values. -var SecretProviderClassPodStatusSelectedFields = []FieldPath{ - {"kind"}, - {"apiVersion"}, - {"metadata", "annotations"}, - {"metadata", "labels"}, - {"metadata", "name"}, - {"metadata", "namespace"}, - {"metadata", "ownerReferences"}, - {"metadata", "uid"}, - {"metadata", "creationTimestamp"}, - {"metadata", "deletionTimestamp"}, - {"metadata", "resourceVersion"}, - - {"status"}, -} type FieldPath []string diff --git a/pkg/datagatherer/k8sdynamic/fieldfilter_test.go b/pkg/datagatherer/k8sdynamic/fieldfilter_test.go index 7bc412f8..24cba144 100644 --- a/pkg/datagatherer/k8sdynamic/fieldfilter_test.go +++ b/pkg/datagatherer/k8sdynamic/fieldfilter_test.go @@ -142,122 +142,6 @@ func TestSelect(t *testing.T) { }, )) - t.Run("secret-provider-class", run_TestSelect( - map[string]any{ - "apiVersion": "secrets-store.csi.x-k8s.io/v1", - "kind": "SecretProviderClass", - "metadata": map[string]any{ - "name": "conjur-spc", - "namespace": "example", - "uid": "fake-uid", - "resourceVersion": "fake-resource-version", - "creationTimestamp": "2025-08-15T00:00:01Z", - "deletionTimestamp": "2025-08-15T00:00:02Z", - // Should be dropped - "generation": 5, - }, - "spec": map[string]any{ - "provider": "conjur", - "secretObjects": []any{ - map[string]any{"secretName": "my-k8s-secret"}, - }, - "parameters": map[string]any{ - "applianceUrl": "https://conjur.example.com", - "authnId": "authn-jwt/my-service", - "account": "myorg", - "conjur.org/configurationVersion": "0.2.0", - "sslCertificate": "-----BEGIN CERTIFICATE-----...", - "identity": "host/my-host", - // Should be dropped - "someVendorSpecificField": "should-be-removed", - }, - }, - }, - SecretProviderClassSelectedFields, - map[string]any{ - "apiVersion": "secrets-store.csi.x-k8s.io/v1", - "kind": "SecretProviderClass", - "metadata": map[string]any{ - "name": "conjur-spc", - "namespace": "example", - "uid": "fake-uid", - "resourceVersion": "fake-resource-version", - "creationTimestamp": "2025-08-15T00:00:01Z", - "deletionTimestamp": "2025-08-15T00:00:02Z", - }, - "spec": map[string]any{ - "provider": "conjur", - "secretObjects": []any{ - map[string]any{"secretName": "my-k8s-secret"}, - }, - "parameters": map[string]any{ - "applianceUrl": "https://conjur.example.com", - "authnId": "authn-jwt/my-service", - "account": "myorg", - "conjur.org/configurationVersion": "0.2.0", - "sslCertificate": "-----BEGIN CERTIFICATE-----...", - "identity": "host/my-host", - }, - }, - }, - )) - - t.Run("secret-provider-class-pod-status", run_TestSelect( - map[string]any{ - "apiVersion": "secrets-store.csi.x-k8s.io/v1", - "kind": "SecretProviderClassPodStatus", - "metadata": map[string]any{ - "name": "conjur-spc-pod-status", - "namespace": "example", - "uid": "fake-uid", - "resourceVersion": "fake-resource-version", - "creationTimestamp": "2025-08-15T00:00:01Z", - "deletionTimestamp": "2025-08-15T00:00:02Z", - "ownerReferences": []any{ - map[string]any{"name": "my-pod"}, - }, - // Should be dropped - "generation": 3, - }, - "status": map[string]any{ - "mounted": true, - "podName": "my-pod", - "secretProviderClassName": "conjur-spc", - "targetPath": "/var/lib/kubelet/pods/fake-uid/volumes", - "objects": []any{ - map[string]any{"id": "data/secrets/username"}, - map[string]any{"id": "data/secrets/password"}, - }, - }, - }, - SecretProviderClassPodStatusSelectedFields, - map[string]any{ - "apiVersion": "secrets-store.csi.x-k8s.io/v1", - "kind": "SecretProviderClassPodStatus", - "metadata": map[string]any{ - "name": "conjur-spc-pod-status", - "namespace": "example", - "uid": "fake-uid", - "resourceVersion": "fake-resource-version", - "creationTimestamp": "2025-08-15T00:00:01Z", - "deletionTimestamp": "2025-08-15T00:00:02Z", - "ownerReferences": []any{ - map[string]any{"name": "my-pod"}, - }, - }, - "status": map[string]any{ - "mounted": true, - "podName": "my-pod", - "secretProviderClassName": "conjur-spc", - "targetPath": "/var/lib/kubelet/pods/fake-uid/volumes", - "objects": []any{ - map[string]any{"id": "data/secrets/username"}, - map[string]any{"id": "data/secrets/password"}, - }, - }, - }, - )) - t.Run("route", run_TestSelect( map[string]any{ "apiVersion": "v1", From 974e66a191320c035677566edea77ccc316e778f Mon Sep 17 00:00:00 2001 From: yaara sirkis Date: Mon, 22 Jun 2026 16:37:55 +0300 Subject: [PATCH 07/10] . --- pkg/datagatherer/k8sdynamic/dynamic.go | 9 +++++---- pkg/datagatherer/k8sdynamic/fieldfilter.go | 1 - 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/datagatherer/k8sdynamic/dynamic.go b/pkg/datagatherer/k8sdynamic/dynamic.go index b5cb0da9..4721b205 100644 --- a/pkg/datagatherer/k8sdynamic/dynamic.go +++ b/pkg/datagatherer/k8sdynamic/dynamic.go @@ -542,10 +542,10 @@ func (g *DataGathererDynamic) redactList(ctx context.Context, list []*api.Gather resource := item - // Redact item if it is a Secret or Route. + // Redact item if it is a Secret or a Route. for _, gvk := range gvks { - switch { - case gvk.Kind == "Secret" && (gvk.Group == "core" || gvk.Group == ""): + // secret object + if gvk.Kind == "Secret" && (gvk.Group == "core" || gvk.Group == "") { // Note: We must redact data field in all cases! // If encryption is enabled, we encrypt the data and preserve it, but we still need to redact later. // If encryption is enabled and _fails_, we MUST still redact the data field to avoid leaking sensitive information. @@ -567,7 +567,8 @@ func (g *DataGathererDynamic) redactList(ctx context.Context, list []*api.Gather if err := Select(secretSelectedFields, resource); err != nil { return err } - case gvk.Kind == "Route" && gvk.Group == "route.openshift.io": + } else if gvk.Kind == "Route" && gvk.Group == "route.openshift.io"{ + // route object if err := Select(RouteSelectedFields, resource); err != nil { return err } diff --git a/pkg/datagatherer/k8sdynamic/fieldfilter.go b/pkg/datagatherer/k8sdynamic/fieldfilter.go index 21fed3a1..46e41118 100644 --- a/pkg/datagatherer/k8sdynamic/fieldfilter.go +++ b/pkg/datagatherer/k8sdynamic/fieldfilter.go @@ -82,7 +82,6 @@ var RedactFields = []FieldPath{ {"metadata", "annotations", "banzaicloud.com/last-applied"}, } - type FieldPath []string // Select removes all but the supplied fields from the resource From 5110978f5ba4e9b98cf9dcc003f6373288a7fd74 Mon Sep 17 00:00:00 2001 From: yaara sirkis Date: Mon, 22 Jun 2026 16:39:03 +0300 Subject: [PATCH 08/10] . --- pkg/datagatherer/k8sdynamic/dynamic.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/datagatherer/k8sdynamic/dynamic.go b/pkg/datagatherer/k8sdynamic/dynamic.go index 4721b205..b9707b40 100644 --- a/pkg/datagatherer/k8sdynamic/dynamic.go +++ b/pkg/datagatherer/k8sdynamic/dynamic.go @@ -567,8 +567,8 @@ func (g *DataGathererDynamic) redactList(ctx context.Context, list []*api.Gather if err := Select(secretSelectedFields, resource); err != nil { return err } - } else if gvk.Kind == "Route" && gvk.Group == "route.openshift.io"{ - // route object + } else if gvk.Kind == "Route" && gvk.Group == "route.openshift.io" { + // route object if err := Select(RouteSelectedFields, resource); err != nil { return err } From e0a77699ab8159969e4d3f3c9d9509e3d07ec6cf Mon Sep 17 00:00:00 2001 From: yaara sirkis Date: Mon, 22 Jun 2026 16:43:20 +0300 Subject: [PATCH 09/10] . --- pkg/datagatherer/k8sdynamic/dynamic.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/datagatherer/k8sdynamic/dynamic.go b/pkg/datagatherer/k8sdynamic/dynamic.go index b9707b40..b3192355 100644 --- a/pkg/datagatherer/k8sdynamic/dynamic.go +++ b/pkg/datagatherer/k8sdynamic/dynamic.go @@ -544,7 +544,7 @@ func (g *DataGathererDynamic) redactList(ctx context.Context, list []*api.Gather // Redact item if it is a Secret or a Route. for _, gvk := range gvks { - // secret object + // secret object if gvk.Kind == "Secret" && (gvk.Group == "core" || gvk.Group == "") { // Note: We must redact data field in all cases! // If encryption is enabled, we encrypt the data and preserve it, but we still need to redact later. @@ -568,7 +568,7 @@ func (g *DataGathererDynamic) redactList(ctx context.Context, list []*api.Gather return err } } else if gvk.Kind == "Route" && gvk.Group == "route.openshift.io" { - // route object + // route object if err := Select(RouteSelectedFields, resource); err != nil { return err } @@ -801,4 +801,4 @@ func isIncludedNamespace(namespace string, namespaces []string) bool { func isNativeResource(gvr schema.GroupVersionResource) bool { _, ok := kubernetesNativeResources[gvr] return ok -} +} \ No newline at end of file From 277d447a563bc70ae53a0fdf6c5f22ff25c34590 Mon Sep 17 00:00:00 2001 From: yaara sirkis Date: Mon, 22 Jun 2026 17:03:45 +0300 Subject: [PATCH 10/10] gci error --- pkg/datagatherer/k8sdynamic/dynamic.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/datagatherer/k8sdynamic/dynamic.go b/pkg/datagatherer/k8sdynamic/dynamic.go index b3192355..6eed8db2 100644 --- a/pkg/datagatherer/k8sdynamic/dynamic.go +++ b/pkg/datagatherer/k8sdynamic/dynamic.go @@ -801,4 +801,4 @@ func isIncludedNamespace(namespace string, namespaces []string) bool { func isNativeResource(gvr schema.GroupVersionResource) bool { _, ok := kubernetesNativeResources[gvr] return ok -} \ No newline at end of file +}