Skip to content

feat(exceptions): support container-level exceptions via containerName attribute#169

Merged
matthyx merged 4 commits into
kubescape:mainfrom
Tomgrinds777:fix/container-level-exceptions
May 13, 2026
Merged

feat(exceptions): support container-level exceptions via containerName attribute#169
matthyx merged 4 commits into
kubescape:mainfrom
Tomgrinds777:fix/container-level-exceptions

Conversation

@Tomgrinds777
Copy link
Copy Markdown
Contributor

@Tomgrinds777 Tomgrinds777 commented May 12, 2026

Overview

Exceptions in Kubescape currently work at the pod/workload level. If a pod has multiple containers and only one of them needs to be excluded from a check, there's no way to do that.

The containerName attribute key already exists in armoapi-go (identifiers.AttributeContainerName), but it was falling into the labels bucket in DigestAttributesDesignator and being compared against pod labels — which always fails.

This PR wires it up properly.

What changed:

  • exceptions/comparator.go — added compareContainerName that checks all containers and init containers in a workload using regex (same as namespace/name/kind matching)
  • exceptions/exceptionprocessor.gometadataHasException now strips containerName from the labels map before label comparison and runs compareContainerName separately

Example exception that now works:

{
  "designators": [{
    "designatorType": "Attributes",
    "attributes": {
      "namespace": "kube-system",
      "name":      "kube-proxy",
      "containerName": "kube-proxy"
    }
  }]
}

Related issues/PRs

Closes kubescape/kubescape#1684

Summary by CodeRabbit

  • New Features

    • Exception rules support matching by container name (regular & init), including regex/wildcards, combined with other attributes like namespace.
  • Improvements

    • Exception evaluation now determines which container(s) actually failed and applies exceptions precisely; container-scoped checks no longer fall back incorrectly.
    • Container-name filtering option enables restricting matches to an explicit list when provided.
  • Tests

    • Added comprehensive tests validating container-name matching, filtering behavior, and precision with response vectors.

Review Change Stack

…e attribute

When an exception designator includes a containerName attribute, it now
matches only workloads that have a container (or init container) with that
name, rather than treating it as a Kubernetes label.

Previously containerName fell into the labels bucket in
DigestAttributesDesignator and was compared against pod labels, which
always failed. Now metadataHasException strips containerName from the
labels map before the label comparison and runs compareContainerName
against the pod's containers and initContainers instead.

compareContainerName supports regex patterns, consistent with how
namespace, name, and kind matching already work.

Signed-off-by: Sarang Bodhe <221710715+workwithsarang@users.noreply.github.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

Warning

Rate limit exceeded

@workwithsarang has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 52 minutes and 35 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: deabe752-4713-41a1-8d24-5ca6a5962788

📥 Commits

Reviewing files that changed from the base of the PR and between 8b6a830 and 020001e.

📒 Files selected for processing (1)
  • exceptions/exceptionprocessor_test.go
📝 Walkthrough

Walkthrough

Adds container-name-aware exception matching: comparator.compareContainerName performs regex matching (with optional failing-name filter). The exception processor extracts failing container names from FailedPaths, propagates them through exception evaluation, and updates metadata/hasException flows and tests accordingly.

Changes

Container-level exception filtering

Layer / File(s) Summary
Container name comparison
exceptions/comparator.go, exceptions/comparator_test.go
compareContainerName checks provided failingNames first; otherwise inspects workload containers and initContainers, regex-matching against names. Tests include pod helper and cover exact, init, regex, no-match, and failingNames-filter cases.
Exception processor integration
exceptions/exceptionprocessor.go, exceptions/exceptionprocessor_test.go
Adds rexContainerPath and extractFailingContainerNames; threads failingContainerNames through SetRuleResponsExceptions/GetResourceExceptions/hasException/iterateRegoResponseVector; isolates container-name from other label/annotation matches and validates it via compareContainerName. Updates test imports, call sites, and adds container-scoped precision tests.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • matthyx

Poem

🐰 I hopped through pods at break of day,
Names and regex led the way.
Init or main, each one I check,
Exceptions now land on the correct deck.
Hop, hop — tests pass and errors stray!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. 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 and specifically describes the main feature: adding container-level exception support via the containerName attribute.
Linked Issues check ✅ Passed All coding requirements from issue #1684 are met: compareContainerName method validates container names, exceptionprocessor extracts failing containers from FailedPaths, and containerName-based matching prevents unintended container exclusions.
Out of Scope Changes check ✅ Passed All changes are within scope: comparator and processor modifications handle container-level exceptions, test additions cover containerName matching and precision scenarios, and no unrelated refactoring is present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 and usage tips.

Copy link
Copy Markdown
Contributor

@matthyx matthyx left a comment

Choose a reason for hiding this comment

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

I found two blocker-level issues with the new containerName matching.

if isTypeWorkload(workload) && len(attributes.GetLabels()) > 0 {
if !p.compareLabels(workload, attributes.GetLabels()) && !p.compareAnnotations(workload, attributes.GetLabels()) {
return false // labels nor annotations do not match
if isTypeWorkload(workload) {
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.

This only enforces containerName for workload objects. hasException() still falls back from a RegoResponseVector to metadataHasException() on the base vector object, so a designator that includes containerName plus a base-object field like name can still match after the related-object scan misses because the container constraint is silently skipped in that fallback path. I reproduced this with a small test: a vector named base-subject with only an app pod in relatedObjects still returns true for {name: base-subject, containerName: missing}.

Comment thread exceptions/comparator.go Outdated
return designatorCluster != "" && c.regexCompare(designatorCluster, clusterName)
}

func (c *comparator) compareContainerName(workload workloadinterface.IMetadata, containerName string) bool {
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.

This only proves that the workload contains some matching container. The exception is still attached to the whole RuleResponse, so on a pod with app + sidecar, a containerName: sidecar exception will also suppress a finding that was raised for app. I reproduced that with SetRuleResponsExceptions: a response whose alert message points at container 'app' still gets excepted as soon as the pod also contains sidecar. We need a way to match against the failing container identity, not just container membership in the pod.

… only

Two bugs were found in the original containerName matching:

1. RegoResponseVector fallback bypass: when iterateRegoResponseVector
   found no match, hasException fell back to checking the base vector
   object which is not a workload. Because isTypeWorkload returns false
   for RegoResponseVector, the containerName check was silently skipped,
   allowing a designator with {name: X, containerName: missing} to match
   just because the vector's name was X. Fix: return false immediately
   when containerName is in the designator and the related-object scan
   found no match.

2. Exception applied to wrong container: compareContainerName checked
   whether the pod *contained* a container with the given name, not
   whether that container *caused* the failing finding. A pod with
   containers [app, sidecar] and a containerName:sidecar exception would
   suppress a finding for app as long as sidecar was present anywhere in
   the pod. Fix: SetRuleResponsExceptions now parses FailedPaths to
   resolve which container indices produced the finding, maps them to
   names, and passes those names to compareContainerName. If failingNames
   is provided, only those containers are candidates for the match.

Signed-off-by: Sarang Bodhe <221710715+workwithsarang@users.noreply.github.com>
@Tomgrinds777
Copy link
Copy Markdown
Contributor Author

Thanks for the detailed analysis — both issues are now fixed.

Issue 1 (RegoResponseVector fallback bypass): When containerName is in the designator and iterateRegoResponseVector finds no match in the related objects, hasException now returns false immediately instead of falling through to the base vector object. Added TestHasException_RegoResponseVector_ContainerNameFallbackBlocked to cover the exact scenario you described ({name: base-subject, containerName: missing} on a vector that only has an app container).

Issue 2 (wrong container excepted): SetRuleResponsExceptions now extracts the failing container names from FailedPaths by parsing containers[N]/initContainers[N] indices and mapping them to names via the workload spec. compareContainerName receives that list and, when non-empty, only matches against those containers. The public GetResourceExceptions API is unchanged (passes nil, which falls back to full workload scan). Added TestSetRuleResponsExceptions_ContainerNamePrecision with three sub-cases: finding on sidecar (match), finding on app (no match), no paths (fallback match).

Copy link
Copy Markdown
Contributor

@matthyx matthyx left a comment

Choose a reason for hiding this comment

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

Rechecked on the latest commit. The earlier base-object fallback bug is fixed, but one blocker remains in the RegoResponseVector path.

// Resolve which containers actually produced the finding so that a
// containerName exception is only applied when the excepted container
// is the one that failed, not just any container in the pod.
failingContainerNames := extractFailingContainerNames(results[i].FailedPaths, workloads[w])
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.

This still loses container precision for RegoResponseVector alerts. Here workloads[w] is the vector object, so extractFailingContainerNames() sees no containers and returns nil. That nil then flows through iterateRegoResponseVector() into metadataHasException(), so compareContainerName() falls back to scanning every container in the related pod. I reproduced it locally: a vector wrapping a pod with containers [app, sidecar], FailedPaths = ["spec.containers[0].securityContext.privileged"], and an exception {containerName: sidecar} still sets the exception. We need to derive failing container names from the related workload in the vector path (and add a regression test for AlertObject.ExternalObjects / RegoResponseVector).

@matthyx matthyx moved this to Needs Reviewer in KS PRs tracking May 13, 2026
…ted objects

extractFailingContainerNames called GetContainers() on the vector object
itself, which has no containers at the top level. This returned nil,
causing compareContainerName to fall back to scanning every container in
the related pod and incorrectly applying a containerName exception to the
wrong container.

Fix: when the workload is a RegoResponseVector, recurse into its related
objects and collect failing container names from each one instead.

Add TestSetRuleResponsExceptions_RegoResponseVector_ContainerNamePrecision
to cover the exact scenario: vector wrapping a pod with [app, sidecar],
FailedPaths pointing at containers[0]=app, exception targeting sidecar —
the exception must not be applied.

Also fix the RegoResponseVector construction in both new tests: the type
check requires top-level "name" and "relatedObjects" keys, not the nested
metadata.name used by regular Kubernetes objects.

Signed-off-by: Sarang Bodhe <221710715+workwithsarang@users.noreply.github.com>
Covers the AlertObject.ExternalObjects path — a RegoResponseVector
delivered via ExternalObjects (single map, not list) must also respect
container-name precision when matching exceptions.

Signed-off-by: Sarang Bodhe <221710715+workwithsarang@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@matthyx matthyx left a comment

Choose a reason for hiding this comment

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

Rechecked the latest head. The RegoResponseVector containerName precision issue is fixed, and I do not see any blocker-level issues remaining.

@matthyx matthyx merged commit 80426b3 into kubescape:main May 13, 2026
4 checks passed
@matthyx matthyx moved this from Needs Reviewer to To Archive in KS PRs tracking May 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

Support excluding some containers of pods from privileged check

2 participants