Skip to content

feat(security): bridge live-only Kubescape findings into deduped backlog issuesΒ #2854

Open
3 of 3 issues completed
Open

Description

@devantler

πŸ€– Generated by the Agentic Engineer

Part of #2451 (second of its two remaining slices). The first slice β€” the SARIF β†’ Code Scanning gate β€” shipped in #2829/#2846 and is closed.

Evidence

The prevention gate now covers what a repo scan can see. It cannot see anything that only exists once workloads are running, and the survey the engineer drains its backlog from is GitHub-only. So live-only findings currently reach the backlog exactly when a human happens to file them, which is the structural reason posture drift went unnoticed for long stretches.

The API surface the bridge needs is present on the live cluster (verified 2026-07-29 by read-only api-resources, spdx.softwarecomposition.kubescape.io/v1beta1):

  • vulnerabilitymanifestsummaries, vulnerabilitysummaries β€” CVE state per workload/cluster
  • configurationscansummaries, workloadconfigurationscansummaries β€” posture control state
  • plus the runtime alert state under kubescape.io/v1

So this slice is buildable against real objects rather than a hypothesis.

Problem

Findings that exist only at runtime never become tracked work. Without an ingestion path they are invisible to oldest-first draining, and posture regressions are noticed by chance rather than by process.

Proposed direction

A scheduled job that reads those summaries read-only and opens, updates, and closes themed security issues β€” one per control or CVE theme, never one per resource.

Idempotency is the whole difficulty and should be treated as the primary design constraint, not an afterthought:

  • a stable fingerprint per theme, so a re-run updates rather than re-files
  • search-before-create against open and recently-closed issues
  • close-when-gone, so a resolved finding stops occupying the backlog
  • ship report-only first β€” emit what it would file, verify the fingerprints are stable across consecutive runs, and only then enable issue writes

Success signal

Baseline: live-only findings entering the backlog by process today β‰ˆ none. Target: every themed live-only finding is represented by exactly one open issue while it exists, and none after it is resolved. Guardrail: the run-over-run issue-churn count stays at zero for unchanged cluster state β€” a bridge that re-files or re-opens on a quiet cluster is a failure, not a partial success.

Acceptance criteria

  • Report-only mode lands first and demonstrates stable fingerprints across at least two consecutive runs on unchanged state.
  • Enabled writes produce at most one open issue per theme; a resolved finding is closed automatically.
  • No issue spam: unchanged cluster state produces zero writes.
  • Findings bodies carry the sanitized minimum β€” control/CVE class and affected public component β€” with detailed reachability evidence kept out of the public issue.

Rough size: M–L. Feature-flagged (report-only is the default-off state), per the feature-flag-first rule.

Activity

  1. moved this to πŸ“₯ Backlog in 🌊 Project Boardon Jul 29, 2026
  2. coderabbitai commented on Jul 29, 2026

    @coderabbitai
    Contributor
    πŸ”— Related PRs

    #2316 - fix(kubescape): make ClusterSecurityException posture exceptions take effect [merged]
    #2443 - fix(kubescape): keep scheduled posture scans local so results persist [merged]
    #2794 - fix(opencost): restore usage metrics [merged]
    #2830 - feat(ci): publish Kubescape posture findings to Code Scanning [merged]
    #2847 - fix(ci): report what the Kubescape scan actually found [merged]


    πŸ“ Issue Planner

    Check the box below or use the @coderabbitai plan command to generate an implementation plan and prompts that you can use with your favorite coding assistant.

    • Create Plan

    πŸ§ͺ Issue enrichment is currently in open beta.

    You can configure auto-planning by selecting labels in the issue_enrichment configuration.

    To disable automatic issue enrichment, add the following to your .coderabbit.yaml:

    issue_enrichment:
      auto_enrich:
        enabled: false

    πŸ’¬ Have feedback or questions? Drop into our discord!

  3. self-assigned this
    on Aug 1, 2026
  4. moved this from πŸ“₯ Backlog to πŸ‘€ In Review in 🌊 Project Boardon Aug 1, 2026
  5. moved this from πŸ‘€ In Review to πŸƒπŸ»β€β™‚οΈ In Progress in 🌊 Project Boardon Aug 1, 2026
  6. devantler commented on Aug 1, 2026

    @devantler
    ContributorAuthor

    πŸ€– Generated by the Agentic Engineer

    Claimed and in progress β€” the report-only half is up as a draft in #2876 (branch claude/kubescape-backlog-bridge-2854). This issue deliberately stays open for the write half.

    Shipped in #2876: theme derivation (one entry per failed control / per CVE severity class, never per resource), stable fingerprints excluding counts and timestamps, sanitized component rendering, -mode gating with writes refused, and CI coverage.

    Still outstanding here: search-before-create against open and recently-closed issues, at-most-one-open-issue-per-theme on enabled writes, and close-when-gone.

    One design constraint discovered while building, worth knowing before touching this: Kubescape's apiserver returns spec-stripped objects on LIST. The same workload reads critical=0 via kubectl get <crd> -A -o json and critical=18 via a per-object GET; vulnerabilitymanifests likewise read 0 matches on LIST and 509 on GET. Anything built here must read per-object or it will report a spotless cluster indefinitely. #2876 rejects that input shape rather than trusting it.

  7. moved this from πŸƒπŸ»β€β™‚οΈ In Progress to πŸ‘€ In Review in 🌊 Project Boardon Aug 1, 2026
  8. devantler commented on Aug 2, 2026

    @devantler
    ContributorAuthor

    πŸ€– Generated by the Agentic Engineer

    Acceptance criterion 1 is now demonstrated on live prod data

    The tool's own -mode=write refusal names this as the gate for the next slice: "the issue-writing
    half lands behind the same flag once fingerprint stability is demonstrated on consecutive runs."

    That demonstration had not been run. It has now.

    Inputs. Every Kubescape summary object fetched individually from admin@prod β€” 2198
    workloadconfigurationscansummaries + 114 vulnerabilitymanifestsummaries = 2312 objects. Per-object
    GETs throughout, never a LIST: the storage apiserver returns spec-stripped skeletons on LIST, which is
    the false-all-clear this command's own guard exists to prevent. Exceptions from
    go run ./scripts/generate-kubescape-exceptions (21 policies).

    Result. 63 themes, 1408 posture findings excepted, exit 0 on every run.

    Two consecutive fresh fetches β€” necessary, but weak on its own

    Fetch β†’ run β†’ re-fetch β†’ run produced a byte-identical report.

    Stated honestly: the two fetches were themselves byte-identical (Kubescape had not rescanned in the
    interval), so this run pair only shows the pipeline is deterministic over identical bytes. That is
    worth having, and it is not by itself evidence that volatile fields are excluded from the
    fingerprint. So I ran two controls that are.

    Control A β€” volatile fields perturbed, fingerprints must not move

    resourceVersion, uid and creationTimestamp rewritten to sentinels on all 2312 objects
    (verified landed: 2198 posture files carry the sentinel resourceVersion).

    Result: all 63 fingerprints unchanged. This is the substantive stability guarantee β€” it holds even
    when the apiserver re-emits every object with fresh metadata, which is precisely what a scheduled
    re-run will see.

    Control B β€” identity changed, fingerprints must move by an expected amount

    Control C-0002 renamed to C-9999 across the 180 objects carrying it.

    Result: exactly 1 fingerprint removed, 1 added. Without this, Control A's green would be equally
    consistent with a fingerprint that ignores its input entirely.

    Control Variable changed Expected Observed
    β€” nothing (re-fetch) identical byte-identical report
    A volatile metadata, 2312 objects no fingerprint moves 0 of 63 moved
    B control identity, 180 objects exactly 1 out / 1 in 1 out / 1 in

    Incidental finding β€” the fail-closed guard works

    The first attempt at Control B renamed only the map key and left the inner controlID. The tool
    refused the whole run: "keys a control as C-9999 but its controlID is C-0002; refusing to guess which
    control an exception should match."
    Exactly right, and worth recording as covered behaviour.

    What this unblocks

    Criterion 1 is met. The remaining criteria are all write-side (one open issue per theme,
    close-when-gone, zero writes on unchanged state, sanitized bodies), so the issue-writing half is now
    free to proceed behind the same default-off flag.

    Two notes for whoever takes it:

    • 63 themes is the cold-start volume. First enablement files 63 issues at once. Worth deciding
      deliberately whether to cap the first run or seed it in severity order, because "no issue spam" is
      easy to satisfy on steady state and easy to violate on day one.
    • 1408 of the posture findings are excepted, so the themed output is already the unexcepted
      remainder. Any write-side dedup must key off the same fingerprint, not off the raw finding set.

    Evidence gathered read-only. No cluster mutation, no issue writes. Detailed per-resource output is
    deliberately not reproduced here β€” the sanitized minimum only, per the publication rule.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Fields

Priority

None yet

Start date

None yet

Target date

None yet

Effort

None yet

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions