Skip to content

fix(remediation): honour selector.namespace when resolving findings-driven targets - #407

Merged
matthyx merged 1 commit into
kubescape:mainfrom
yugal07:fix/selector-namespace-scope
Aug 22, 2026
Merged

fix(remediation): honour selector.namespace when resolving findings-driven targets#407
matthyx merged 1 commit into
kubescape:mainfrom
yugal07:fix/selector-namespace-scope

Conversation

@yugal07

@yugal07 yugal07 commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

The problem

OperatorActionSelector.Namespace is part of the operatorAction contract and is documented as limiting a findings-driven selector to a single namespace:

// Namespace optionally limits the selector to a single namespace.
Namespace string `json:"namespace,omitempty"`

resolveSelectorTargets never read it. It always listed cluster-wide:

summaries, err := actionHandler.ksStorageClient.SpdxV1beta1().
    WorkloadConfigurationScanSummaries("").List(ctx, metav1.ListOptions{})

So a request to quarantine workloads failing C-0016 in payments would resolve to every workload failing C-0016 anywhere in the cluster, and nothing in the response or the events would indicate the scope was wider than asked for. For an action set whose whole point is to mutate running workloads, silently ignoring the field that bounds the blast radius is the wrong failure mode.

The fix

Two parts, because there are two different namespaces in play.

The listing is now scoped server-side to selector.Namespace, which is both correct and cheaper than filtering client-side. An empty Namespace still lists across all namespaces, so cluster-wide selectors behave exactly as before.

The resolver also drops any summary whose label-derived target namespace falls outside the requested one. That namespace comes from the kubescape.io/workload-namespace label and is what the remediator actually acts on, so if it ever disagreed with the namespace the summary object is stored in, the server-side scoping alone would not be enough. This is defence in depth rather than a bug I observed in practice, but the cost is one comparison and the downside of getting it wrong is mutating a workload in a namespace the caller did not name.

Context

Part of the phased operatorAction remediation framework:

Found while wiring the CLI side of findings-driven targeting in kubescape/kubescape#3474. That PR adds --control and --min-severity, and deliberately rejects --target-namespace combined with a selector precisely because of this bug, since offering the flag while the operator ignores it would be worse than not offering it at all.

Once this merges and ships in a release, the CLI guard can be lifted and --target-namespace passed through. The guard needs to outlive this fix by a release, because a CLI that sends selector.namespace to an older operator would hit exactly the silent widening described above.

Testing

Five new tests in mainhandler/findings_test.go:

  • a namespaced selector resolves only to workloads in that namespace
  • an empty namespace still resolves cluster-wide, so existing behaviour is unchanged
  • namespace scoping applies to a severity-only selector, not just a control-based one
  • a namespace with no matching findings resolves to nothing, which the caller already reports as an error
  • a summary whose labels point outside the requested namespace is dropped

I confirmed the three scoping tests fail against main without the change and pass with it, so they are genuine regression tests rather than tests written to match current behaviour. Full mainhandler and mainhandler/remediators suites pass.

Summary by CodeRabbit

  • Bug Fixes
    • Findings-based selectors now correctly respect the requested namespace.
    • Empty namespaces continue to support cluster-wide resolution.
    • Targets outside the requested namespace are excluded from summaries.
  • Tests
    • Added coverage for namespace filtering, cluster-wide behavior, missing findings, and mismatched target labels.

…riven targets

resolveSelectorTargets always listed WorkloadConfigurationScanSummaries
across every namespace and never read selector.Namespace, even though the
field is part of the operatorAction contract and documented as scoping a
selector to a single namespace.

A caller asking to remediate one namespace therefore got every matching
workload in the cluster, with nothing in the output to indicate the wider
scope. Scope the listing server-side, and drop any summary whose
label-derived target namespace falls outside the requested one, since that
is the namespace the remediator actually acts on. An empty Namespace keeps
the existing cluster-wide behaviour.

Signed-off-by: yugal07 <yashsadhwani544@gmail.com>
@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e4517357-c99a-40fb-b9ff-42880461d11d

📥 Commits

Reviewing files that changed from the base of the PR and between e7f562d and e2983f6.

📒 Files selected for processing (2)
  • mainhandler/findings.go
  • mainhandler/findings_test.go

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

Findings-driven selector resolution now scopes summary queries and resolved targets to the selector namespace. An empty namespace remains cluster-wide. Tests cover control, severity, empty-result, cluster-wide, and mismatched-label cases.

Changes

Findings namespace scoping

Layer / File(s) Summary
Namespace-scoped selector resolution
mainhandler/findings.go
Selector resolution lists summaries within selector.Namespace. Empty namespaces remain cluster-wide. Targets with mismatched labeled namespaces are discarded.
Namespace filtering validation
mainhandler/findings_test.go
Tests verify control and severity filtering, cluster-wide behavior, empty results, and rejection of mismatched summary namespaces.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to e2983

The change narrows findings-driven remediation to the requested namespace while preserving cluster-wide behavior for empty selectors, with no actionable merge-blocking risk remaining beyond normal checks and review.

Suggested reviewers: doraem-on

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 7 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the primary change: honoring selector namespaces during findings-driven target resolution.
Linked Issues check ✅ Passed The changes satisfy [#391] by scoping findings-driven resolution to selector.namespace while preserving cluster-wide behavior for an empty namespace.
Out of Scope Changes check ✅ Passed The changes are limited to namespace filtering in findings-driven resolution and related regression tests, matching the linked issue objectives.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@matthyx matthyx left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the diff, the resolver logic, and the new tests.

  • Listing is now scoped server-side via WorkloadConfigurationScanSummaries(selector.Namespace), and an empty namespace correctly falls back to cluster-wide, matching the pre-existing behavior for unscoped selectors.
  • The added defense-in-depth check (target.Namespace != selector.Namespace) is sound: it guards against a summary's label-derived namespace disagreeing with the object's own namespace, which is the namespace the remediator actually acts on.
  • The five new tests in mainhandler/findings_test.go cover the namespaced case, the cluster-wide/empty-namespace case, namespace scoping combined with MinSeverity, a namespace with no matches, and the mislabelled-summary defense-in-depth case. Good regression coverage, and the description notes the scoping tests were confirmed to fail on main without the fix.
  • All CI checks (build, tests, CodeQL, GitGuardian, DCO, FOSSA) are green. CodeRabbit's automated review found no actionable comments — only a generic "docstring coverage" heuristic warning, which isn't a real blocker for this package's conventions.

No blockers. This is a correct, well-tested, appropriately-scoped fix. Approving.

@matthyx
matthyx merged commit fa4d821 into kubescape:main Aug 22, 2026
11 checks passed
@matthyx matthyx moved this to To Archive in KS PRs tracking Aug 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: To Archive

Development

Successfully merging this pull request may close these issues.

2 participants