Skip to content

Kubescape findings surface only as a compliance score, so a regression names no resource or line #2862

Description

@devantler

🤖 Generated by the Agentic Engineer

Part of #2451 — its decomposition item (a) SARIF→Code-Scanning PR gate.

Evidence

ci.yamls 🔎 Scan manifests (Kubescape NSA) — hard gate runs ksail workload scan --framework nsa --exceptions ... --compliance-threshold 95. That is the entire finding surface today: a single pass/fail score in a job log.

Measured on main at 5596d879d (ksail 7.178.1, exceptions applied): the scan produces 4 findings across 2 controlsC-0034 and one other — in k8s/bases/apps/umami/cron-job.yaml, k8s/bases/infrastructure/vault-backup/{job,cron-job}.yaml, and k8s/bases/infrastructure/vault-config/job.yaml. None of that detail reaches anybody. The gate passes at 95, so all four stay invisible; if a fifth appeared and dropped the score, CI would say only "below threshold".

Audience and problem

The engineer draining this backlog, and the maintainer reviewing a PR. A score tells you that posture moved, never what moved — so triaging a regression means re-running the scan locally and diffing by hand. Findings also have no identity across runs: nothing dedups them, nothing closes them when fixed, and nothing shows them on the PR that caused them.

Hypothesis

Kubescape can already emit SARIF 2.1.0 (--format sarif), and it carries per-resource artifactLocation.uri + region.startLine. Uploading that to GitHub Code Scanning gives per-line PR annotations, native dedup via partialFingerprints, auto-close-when-gone, and a persistent Security-tab view — none of which we have to build or maintain.

Success signal

Kubescape findings appear in the repo Security tab and as PR annotations, each linking to the correct existing file and line; a fixed finding disappears without manual bookkeeping.

Smallest useful change

Add two steps to the existing validate job (the scan is ~5s, so it re-scans rather than changing the gates behaviour):

  1. emit SARIF and re-root its URIs to the repository root;
  2. upload it with github/codeql-action/upload-sarif under a distinct kubescape-nsa category.

⚠️ Known trap, already measured: Kubescape writes artifactLocation.uri relative to the scanned root (spec.workload.sourceDirectory = k8s), e.g. bases/infrastructure/vault-backup/job.yaml. Code Scanning resolves URIs against the repository root, where that path does not exist. Uploaded unmodified, every annotation silently matches no file — the feature would look shipped and show nothing.

Acceptance criteria

  • Kubescape SARIF uploads to Code Scanning on PRs, under its own category, without disturbing CodeQL default setup.
  • Every SARIF URI resolves to a file that exists at the repository root (asserted, with a control proving the re-rooting is load-bearing).
  • The existing compliance-threshold gate keeps its current behaviour and human-readable output.
  • SARIF is still produced when the gate fails — that is when findings matter most.
  • Fork PRs, which get a read-only token, skip the upload instead of failing.

Size

S — one workflow change. Items (b) the CRD→issue bridge and (c) Kyverno-Enforce graduation remain open under #2451.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    Status
    ✅ Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions