Detect cluster-wide CVEs in 30 seconds with kubeagent scan-apps
Most CVE scanners want a sidecar and a registry hook. kubeagent scan-apps reads what's already in the cluster and cross-checks OSV.dev + endoflife.date.
If you’ve ever tried to answer the question “is anything in this cluster vulnerable to CVE-2024-XXXX?” without a commercial scanner, you know the gap. You can kubectl get pods -A -o jsonpath=... and grep image tags, but you still need to know which versions of which apps are affected, whether the version is EOL, and which workloads actually run it.
kubeagent scan-apps does that walk for you, end to end, in about half a minute on a small cluster. No in-cluster agent, no registry credentials, no service account — just your existing kubectl context. Here’s exactly what it does.
What it inventories
The command walks every namespace your kubeconfig can reach and identifies open-source applications running in the cluster using three signals, in order of confidence:
app.kubernetes.io/nameandapp.kubernetes.io/versionlabels — when present, these are the most reliable identifier. Most well-behaved Helm charts set them.- Helm release secrets — Helm v3 stores release metadata as
kind: Secret, type: helm.sh/release.v1in the release’s namespace. Parsing the chart name and version out of these is fast and doesn’t require Helm to be installed locally. - Container image tags — for everything that doesn’t expose labels or Helm metadata, KubeAgent falls back to the image reference.
redis:7.2.4-alpineis unambiguous;myorg/api:latestis silently skipped (no useful version info).
The result is a deduplicated list of (app, version) pairs along with the workloads that run them — Kind/namespace/name tuples — and the ingress hostnames that resolve to those workloads (computed by joining Ingress → Service → Pod via labels). The “ingress” join is the one that turns a finding from a number into an action: PostgreSQL 13 → chat.kamva.org makes it obvious which finding has a public attack surface.
Where the security findings come from
Two public, free data sources:
- OSV.dev — Google’s open-source vulnerability database. For each
(app, version), KubeAgent POSTs a single batched query and gets back a list of OSV IDs (oftenCVE-*orGHSA-*) that affect that exact version. - endoflife.date — community-maintained EOL data for hundreds of OSS projects. For each
(app, version), KubeAgent checks whether the major.minor cycle is still in support, in security-only support, or fully EOL.
Both API calls are cached at ~/.kubeagent/cache/{osv,eol}/ with a 24-hour TTL. Re-running the scan reuses the cache, so the second run is essentially free. If the API is unreachable, KubeAgent falls back to the cached data (even if stale) and clearly marks the finding’s age so you don’t make a stale decision unknowingly.
What the output looks like
The terminal output is color-coded by severity:
[critical] PostgreSQL 13 — EOL since 2025-11-13 — 2 workload(s)
→ Deployment/prod/chat-db (chat.kamva.org)
→ StatefulSet/staging/postgres-staging
[warning] Redis 7.0.5 — 4 known CVEs (CVE-2023-...) — 5 workload(s)
→ StatefulSet/prod/dove-redis-master, +4 more
[warning] nginx 1.21.6 — 2 known CVEs — 7 workload(s)
[info] Prometheus 2.45.0 — 0 CVEs, in support
The same data lands in markdown at ~/.kubeagent/clusters/<context>/applications.md. That file is automatically loaded as context by the diagnoser the next time KubeAgent investigates an incident — so when Postgres starts misbehaving, Claude already knows the version is EOL and can call that out in the diagnosis instead of suggesting a config tweak that won’t ship in an unmaintained branch.
The --report flag
scan-apps on its own is a deterministic inventory. scan-apps --report is the same inventory plus a prioritized remediation plan generated by Claude. The guardrail: the model is only allowed to rank and explain facts already in the scan payload — it can’t invent CVE IDs, suggest a “latest” version it doesn’t have evidence for, or recommend a workload not in the inventory. The output is saved as report.md next to applications.md.
A typical report opens with an executive summary (“2 EOL components are public-facing; address those first”), then groups findings by EOL + public-facing, high-CVE + public-facing, internal-only, and version sprawl (same app, multiple versions across the cluster — usually a tech-debt smell).
The base command runs login-free; --report uses the proxy for billing because it calls Claude.
Why we built this instead of recommending a third party
Two reasons:
- The diagnoser was already going to need it. A model investigating an incident in a cluster running PostgreSQL 11 and CVE-2024-10977 needs to know both things. Putting the inventory + CVE detection in the CLI means the diagnoser has the data for free, in the same knowledge base.
- The boundary is right. A registry-scanning SaaS sees your image manifests. An in-cluster scanner gets cluster-wide read on Pod specs, ConfigMaps, and Secrets.
scan-appsruns from your terminal using kubectl you already have — no new credential surface, no new vendor in your supply chain.
How to run it
npm install -g kubeagent
kubeagent login
kubeagent scan-apps # inventory + findings, terminal output + applications.md
kubeagent scan-apps --report # add AI-prioritized remediation plan
scan-apps also runs as part of kubeagent onboard, so if you’ve already onboarded a cluster you have the inventory and can rerun it any time. Start on the free plan if you want to point it at your own cluster — you’ll have a CVE/EOL inventory before your coffee’s finished brewing.
For the incident workflow that uses this inventory as context, read the Kubernetes troubleshooting guide. For the CLI’s dependency boundary, see why an npm supply-chain attack cannot directly reach your cluster.