Skip to content

fix: preserve failing-container identity when applying exceptions - #199

Merged
matthyx merged 1 commit into
kubescape:mainfrom
Karman580:fix/container-exception-path-matching
Nov 11, 2026
Merged

matthyx merged 1 commit into
kubescape:mainfrom
Karman580:fix/container-exception-path-matching

Conversation

@Karman580

Copy link
Copy Markdown
Contributor

Overview

Fixes #198.

Result.SetExceptions applies exceptions through ResourceAssociatedRule.setExceptions, and that function called GetResourceExceptions. GetResourceExceptions has no failing-container context, so a containerName exception matched whenever any container in the workload had that name. On a pod with a privileged app and a plain sidecar, an exception for sidecar therefore passed C-0057. Kubescape applies exceptions only through this path, so the precise matching in SetRuleResponsExceptions (#169, #196) was never used during a scan.

Changes:

  • exceptions.GetResourceExceptionsForPaths, a new exported function. It is GetResourceExceptions with the failing containers resolved from the given paths by the existing extractFailingContainerNames. GetResourceExceptions is unchanged.
  • ResourceAssociatedRule.setExceptions passes every path recorded on the rule to the new function: failedPath, deletePath, reviewPath and fixPath.path. Kubescape stores rule paths as delete, review and fix paths and leaves failedPath empty, so reading only failedPath would still fall back to the workload-wide check.
  • compareContainerName: when the failing containers are known, the exception must cover all of them. A rule result carries one status per resource, so an exception that names only some of the failing containers cannot pass the whole rule without hiding the others. A regex such as app|sidecar still covers several containers on purpose.
  • ephemeralContainerNames decodes the list through JSON, as GetContainers already does. Kubescape writes ephemeral containers back as a typed []corev1.EphemeralContainer after stripping env data. The old []interface{} assertion returned nothing for that slice, so an exception naming an ephemeral container never matched.

Behaviour that is unchanged:

  • Exceptions without containerName.
  • alertOnly semantics.
  • SetRuleResponsExceptions for a single failing container.
  • Findings whose paths carry no container index, such as spec.hostNetwork. These still use the workload-wide fallback.

How to Test

New tests:

  • TestSetExceptionsContainerNamePrecision (reporthandling/results/v1/resourcesresults) drives Result.SetExceptions on a pod with setup (init), app, sidecar and debugger (ephemeral). It covers:
    • an exception for another container, for the failing container, and for an unknown name;
    • no containerName;
    • two failing containers, with an exception for one of them and with an app|sidecar regex;
    • init and ephemeral container paths;
    • failed, review and fix path kinds;
    • alertOnly;
    • a finding with no container path.
  • TestComparator_compareContainerName_everyFailingNameMustMatch and TestEphemeralContainerNames_typedSlice (exceptions).

Before the fix, on main at 142c5dd:

$ go test ./reporthandling/results/v1/resourcesresults/ ./exceptions/ -run 'TestSetExceptionsContainerNamePrecision|everyFailingNameMustMatch|TestEphemeralContainerNames_typedSlice' -v
--- FAIL: TestSetExceptionsContainerNamePrecision (0.00s)
    --- FAIL: TestSetExceptionsContainerNamePrecision/exception_for_another_container_does_not_hide_the_failure (0.00s)
    --- PASS: TestSetExceptionsContainerNamePrecision/exception_for_the_failing_container_applies (0.00s)
    --- PASS: TestSetExceptionsContainerNamePrecision/exception_for_an_unknown_container_does_not_apply (0.00s)
    --- PASS: TestSetExceptionsContainerNamePrecision/exception_without_containerName_still_covers_the_whole_resource (0.00s)
    --- FAIL: TestSetExceptionsContainerNamePrecision/exception_covering_one_of_two_failing_containers_does_not_apply (0.00s)
    --- PASS: TestSetExceptionsContainerNamePrecision/exception_covering_every_failing_container_applies (0.00s)
    --- FAIL: TestSetExceptionsContainerNamePrecision/exception_for_a_regular_container_does_not_hide_an_init_container (0.00s)
    --- PASS: TestSetExceptionsContainerNamePrecision/exception_for_the_failing_init_container_applies (0.00s)
    --- FAIL: TestSetExceptionsContainerNamePrecision/exception_for_a_regular_container_does_not_hide_an_ephemeral_container (0.00s)
    --- PASS: TestSetExceptionsContainerNamePrecision/exception_for_the_failing_ephemeral_container_applies (0.00s)
    --- FAIL: TestSetExceptionsContainerNamePrecision/failedPath_is_read (0.00s)
    --- FAIL: TestSetExceptionsContainerNamePrecision/reviewPath_is_read (0.00s)
    --- FAIL: TestSetExceptionsContainerNamePrecision/fixPath_is_read (0.00s)
    --- PASS: TestSetExceptionsContainerNamePrecision/alertOnly_on_the_failing_container_keeps_the_finding_failing (0.00s)
    --- PASS: TestSetExceptionsContainerNamePrecision/finding_without_a_container_path_keeps_the_workload-wide_match (0.00s)
FAIL	github.com/kubescape/opa-utils/reporthandling/results/v1/resourcesresults	0.550s
--- FAIL: TestComparator_compareContainerName_everyFailingNameMustMatch (0.00s)
--- FAIL: TestEphemeralContainerNames_typedSlice (0.00s)
FAIL	github.com/kubescape/opa-utils/exceptions	1.024s

After the fix, every subtest above passes:

--- PASS: TestSetExceptionsContainerNamePrecision (0.00s)
ok  	github.com/kubescape/opa-utils/reporthandling/results/v1/resourcesresults	0.768s
--- PASS: TestEphemeralContainerNames (0.00s)
--- PASS: TestComparator_compareContainerName_everyFailingNameMustMatch (0.00s)
--- PASS: TestEphemeralContainerNames_typedSlice (0.00s)
ok  	github.com/kubescape/opa-utils/exceptions	1.559s

go build ./... and go test -race ./... pass, with all 18 packages ok. staticcheck reports nothing on the two changed packages, the same as on main.

End to end: I built kubescape master 90cbf064 with a local replace pointing at this branch and reran the scans from #198 (kubescape scan control C-0057 <pod> --severity-threshold high --format json --exceptions <file>):

Pod Exception containerName Before After
privileged app + sidecar sidecar passed, exit 0 failed, exit 1
privileged app + sidecar app passed, exit 0 passed, exit 0
privileged app + sidecar ghost failed, exit 1 failed, exit 1
both privileged sidecar passed, exit 0 failed, exit 1
both privileged app|sidecar not run passed, exit 0
privileged init setup app passed, exit 0 failed, exit 1
privileged init setup setup passed, exit 0 passed, exit 0
privileged ephemeral debugger app passed, exit 0 failed, exit 1
privileged ephemeral debugger debugger failed, exit 1 passed, exit 0
privileged app + sidecar, Deployment sidecar passed, exit 0 failed, exit 1

Also with the patched build:

  • kubescape's go test ./core/pkg/opaprocessor/ ./core/pkg/resultshandling/... passes.
  • C-0041 (spec.hostNetwork, no container path) with a sidecar exception still returns passed, the same as before.

Compatibility

  • GetResourceExceptions keeps its signature and behaviour.
  • SetRuleResponsExceptions now also needs every failing container to be covered. Its single-container tests are unchanged.
  • Two separate exceptions, one for app and one for sidecar, do not combine to cover a finding on both containers. A single exception has to name both, for example with app|sidecar. This errs toward reporting the finding.

After a release, kubescape needs an opa-utils bump to pick this up. I'll follow up there with an integration test through updateResults.

@coderabbitai

coderabbitai Bot commented Nov 10, 2026 •

Copy link
Copy Markdown

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: dfbaeba0-3f5c-418c-b893-6ab5b86a4cb8
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

Result.SetExceptions matched containerName exceptions through
GetResourceExceptions, which has no failing-container context and accepts
the exception when any container in the workload has the excepted name.
An exception for a benign sidecar therefore passed a finding raised for
another container in the same pod.

- Add GetResourceExceptionsForPaths and use it from
  ResourceAssociatedRule.setExceptions with every path recorded on the
  rule (failed, delete, review and fix paths).
- When the failing containers are known, require the exception to cover
  all of them, so it cannot hide a container it does not name.
- Decode ephemeral container names through JSON so typed slices resolve.

Fixes kubescape#198

Signed-off-by: Karman Singh Talwar <flyingoffkarmansinghtalwar@gmail.com>
@Karman580
Karman580 force-pushed the fix/container-exception-path-matching branch from 58d53d6 to 0507c65 Compare November 10, 2026 19:52
@Karman580

Copy link
Copy Markdown
Contributor Author

@matthyx the pr-checks workflow for this PR is waiting for maintainer approval, since this is my first contribution here. Could you approve the run and take a look when you have time? This covers the Result.SetExceptions path from your review comment on #169, which kubescape uses during scans.

@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.

Approving at head 0507c65 (base main).

Need: Confirmed (#198). Result.SetExceptions used the workload-wide GetResourceExceptions, so a containerName exception for sidecar hid a failure on app. The precise matching from #169/#196 only ran in SetRuleResponsExceptions. Same class of bug that matthyx flagged in #169 review.

Soundness: The new GetResourceExceptionsForPaths reuses extractFailingContainerNames over failed/delete/review/fix paths. The "all failing containers must match" rule is conservative (errs toward reporting). Paths without a container index keep the old fallback. The typed-slice JSON decode for ephemeral containers is correct and consistent with GetContainers. No public API breakage. No duplicate or superseded PRs found (searched open/closed/merged PRs and issues; #169/#196 are prior related merges, not rejections).

Validation: I checked out the head and ran go build ./... and go test ./exceptions/ ./reporthandling/..., all ok. With the three source files reverted to main, the three new tests fail (TestSetExceptionsContainerNamePrecision, ...everyFailingNameMustMatch, TestEphemeralContainerNames_typedSlice), so they are real regression tests. CI at this head: build, DCO and GitGuardian pass (CodeRabbit skipped). I did not repeat the author's end-to-end kubescape runs.

Blockers: none. Nonblocking: the author plans a kubescape-side integration test after the bump, and no further tracking is needed. Two separate exceptions (one for app, one for sidecar) deliberately do not combine; this is documented in the PR.

@matthyx matthyx moved this to Waiting on Author in KS PRs tracking Nov 11, 2026
@matthyx
matthyx merged commit 2284a07 into kubescape:main Nov 11, 2026
4 of 5 checks passed
@matthyx matthyx moved this from Waiting on Author to To Archive in KS PRs tracking Nov 11, 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.

containerName exception suppresses findings from other containers when applied through Result.SetExceptions

2 participants