#3823·kubescape

bug : Image exceptions silently do nothing for archive scans

Author: AdeshDeshmukhCreated Sep 13, 2026Updated Sep 13, 2026
Labelsbug

Description

Ran into this while gating CI on tarball scans: kubescape scan image ./app.tar --exceptions exc.json succeeds, but none of my exceptions are applied. No error, no warning — just a clean-looking report.

From what I can tell, exception matching only understands registry-style references (reference.ParseNormalizedNamed in getAttributesFromImage, core/core/image_scan.go on master as of Sep 2026). Anything like docker-archive: / oci-archive: / oci-dir: / dir: / sbom: or a bare local path fails to parse, the error gets logged and swallowed, and matching runs against empty Attributes{}. Since empty-pattern regexStringMatch("", x) is true, my empty-target policies over-match and my specific ones under-match. Both silently.

Environment

OS: Ubuntu 22.04 LTS Version: v3.0.x (built from commit 153e9d9f)

Steps To Reproduce

  1. Write an exceptions file for a CVE you know is in the image.
  2. kubescape scan image quay.io/kubescape/kubescape-cli:v3.0.0 --exceptions /tmp/exc.json -v — exception honored.
  3. Save the same image to ./app.tar, run kubescape scan image ./app.tar --exceptions /tmp/exc.json -v — success, exception never applied, exit 0. Same with docker-archive:./app.tar.
  4. Mixed kubescape scan image ./app.tar nginx:1.27 --exceptions /tmp/exc.json — nginx fine, tarball silently skipped.

Expected behavior

Fail loudly per image instead of silently: report non-registry + --exceptions as a per-image Image Exceptions/Unsupported Input error (name the input, the detected scheme, and the remedies), still scan registry siblings, keep exit non-zero. If every image is non-registry, fail before the Grype DB download. Registry repos that happen to end in .tar (e.g. team/my.tar:v1) must keep working.

Actual Behavior

Exit 0 with a full report; the tarball half never had exceptions evaluated. My gate was inert and I didn't know it.

Additional context

Couldn't find an open/closed issue or PR covering this (searched archive + exception + docker-archive; closest were #3682, #3353, #3233 — all different). Nothing assigned.

Proposed approach:

  1. One classifier classifyImageInput(img, stat) — case-insensitive scheme set (docker-archive|oci-archive|oci-dir|oci-layout|dir|file|sbom), tar-suffix only when the path exists or is unambiguously a file path (absolute / ./-relative, so tagged team/my.tar:v1 stays registry), bare existing local paths via injectable stat, ParseNormalizedNamed as fallback. Empty input gets its own error.
  2. getUniqueVulnerabilitiesAndSeverities([],[],error) — error on policies + non-registry with message image exceptions cannot target non-registry input %q (detected %q): scan by registry reference or remove --exceptions, including the exceptions file path. No empty-attributes fallback.
  3. Per-job ExceptionErr on ImageScanJob — one archive never poisons registry siblings; worker checks it before platform/mapping/scan; new ErrCategoryExceptionUnsupported so dashboards can separate this from real failures.
  4. ScanImageContext short-circuits all-archive + policies before DB load; mixed scans return sibling report + joined error (thresholds over succeeded scans only).
  5. Tests: classifier table (injectable stat), archive+policy error, archive+nil ok, mixed-jobs test, threshold (false, err) pin; existing suites unmodified. Docs: 2-line note in scan image --help + exceptions README.

Happy to implement it in that shape if maintainers agree with fail-per-image over fail-whole-scan...! :)