🤖 Generated by the Agentic Engineer
Problem
k8s/bases/infrastructure/cluster-security-exceptions/controller-rbac.yaml suppresses six controls through a match.namespaceSelector. That file already documents why such a selector cannot suppress an RBAC-anchored finding, and says to migrate the six "if they surface failing objects".
They do — all six. Measured 2026-07-27 against admin@prod:
| Control |
Name |
Failing objects |
Anchored on an RBAC object |
Cluster-scoped (empty namespace) |
| C-0053 |
Access container service account |
155 |
100% |
103 |
| C-0037 |
CoreDNS poisoning |
41 |
100% |
30 |
| C-0188 |
CIS-5.1.4 minimize access to create pods |
20 |
100% |
13 |
| C-0031 |
Delete Kubernetes events |
18 |
100% |
14 |
| C-0063 |
Portforwarding privileges |
9 |
100% |
5 |
| C-0035 |
Access restriction to infrastructure — admin access |
6 |
100% |
5 |
249 (control, object) pairs over 156 unique objects. Zero are ordinary namespaced workloads; every one carries kubescape.io/rbac-resource: "true". So the namespaceSelector is not merely imprecise on these — it can never match any of them, and all six exceptions are inert while appearing configured.
(Object names are deliberately not listed. An enumeration of which bindings can reach service-account tokens or create pods is a weakness map; the aggregate counts and the anchor analysis are the sanitized minimum needed to review a fix.)
The trap in the data
The summaries are labelled kubescape.io/workload-kind: ServiceAccount with a populated workload-namespace, which makes a namespaceSelector look viable. But matching keys on the kubescape.io/wlid annotation, which reads wlid://cluster-prod/namespace-/clusterrolebinding-<name> — binding kind, empty namespace. Any migration must name ClusterRoleBinding/RoleBinding kinds, not ServiceAccount.
Also note for anyone re-measuring: the aggregated storage API returns spec-stripped objects on LIST — controls reads null and severities read all-zero across every item. Those numbers are only real when each object is fetched by name. A LIST-based sweep here reports a clean cluster.
What is NOT yet established — read before migrating
The four controls already migrated to explicit kind+name match.resources CRs have not gone to zero:
| Migrated control |
Still failing |
| C-0015 (list secrets) |
70 |
| C-0007 (delete-capable roles) |
42 |
| C-0002 (exec into container) |
9 |
| C-0187 (wildcard RBAC) |
6 |
They are anchored the same way. This is not evidence the migration approach is broken — the residue may simply fall outside those CRs' named scope, which would be correct behaviour. But it does mean "migrate to kind+name" is currently an unproven remediation, and migrating six more controls on that assumption risks writing six more exceptions that also do not take effect.
Direction — measure before migrating
The first shippable step is verification, not a migration:
- For one already-migrated control (C-0187 is smallest at 6), determine whether each still-failing object is inside or outside its CR's named scope. In-scope residue means the mechanism does not work and the whole approach needs rethinking; out-of-scope residue means it works and the CRs are simply incomplete.
- Only then migrate the six, kinds named correctly per the
wlid finding above.
- Re-measure and record before/after counts per control.
Acceptance criteria
Rough size: M. Step 1 is small and decides the shape of the rest.
Evidence freshness
Scanner proven alive rather than assumed: kubescape v4.0.10, scheduler CronJob last job Complete, newest summary objects written 2026-07-27T10:08–10:09Z, aggregate scan summary non-zero (212 high / 840 medium / 89 low), and 2206/2206 objects returned populated control maps on direct GET.
Related
Problem
k8s/bases/infrastructure/cluster-security-exceptions/controller-rbac.yamlsuppresses six controls through amatch.namespaceSelector. That file already documents why such a selector cannot suppress an RBAC-anchored finding, and says to migrate the six "if they surface failing objects".They do — all six. Measured 2026-07-27 against
admin@prod:249 (control, object) pairs over 156 unique objects. Zero are ordinary namespaced workloads; every one carries
kubescape.io/rbac-resource: "true". So the namespaceSelector is not merely imprecise on these — it can never match any of them, and all six exceptions are inert while appearing configured.(Object names are deliberately not listed. An enumeration of which bindings can reach service-account tokens or create pods is a weakness map; the aggregate counts and the anchor analysis are the sanitized minimum needed to review a fix.)
The trap in the data
The summaries are labelled
kubescape.io/workload-kind: ServiceAccountwith a populatedworkload-namespace, which makes a namespaceSelector look viable. But matching keys on thekubescape.io/wlidannotation, which readswlid://cluster-prod/namespace-/clusterrolebinding-<name>— binding kind, empty namespace. Any migration must nameClusterRoleBinding/RoleBindingkinds, notServiceAccount.Also note for anyone re-measuring: the aggregated storage API returns spec-stripped objects on LIST —
controlsreads null and severities read all-zero across every item. Those numbers are only real when each object is fetched by name. A LIST-based sweep here reports a clean cluster.What is NOT yet established — read before migrating
The four controls already migrated to explicit kind+name
match.resourcesCRs have not gone to zero:They are anchored the same way. This is not evidence the migration approach is broken — the residue may simply fall outside those CRs' named scope, which would be correct behaviour. But it does mean "migrate to kind+name" is currently an unproven remediation, and migrating six more controls on that assumption risks writing six more exceptions that also do not take effect.
Direction — measure before migrating
The first shippable step is verification, not a migration:
wlidfinding above.Acceptance criteria
controller-rbac.yamlno longer carries exception entries that cannot match anythingRough size: M. Step 1 is small and decides the shape of the rest.
Evidence freshness
Scanner proven alive rather than assumed: kubescape v4.0.10, scheduler CronJob last job Complete, newest summary objects written 2026-07-27T10:08–10:09Z, aggregate scan summary non-zero (212 high / 840 medium / 89 low), and 2206/2206 objects returned populated control maps on direct GET.
Related