Skip to main content

Command Palette

Search for a command to run...

The question the Security Advisory dashboard can't answer

Updated
7 min read

Update — June 21, 2026

After publishing this, I kept hitting one edge case: the chainctl images advisories list approach means a round-trip per image, and for large registries (300+ repos) the parallelism ceiling starts to matter. More importantly, it requires chainctl installed and authenticated, which makes it awkward to drop into a CI pipeline without extra setup.

It turns out the Chainguard API has a cleaner path. The /vulnerabilities/v2beta1/advisories endpoint accepts an aliases filter:

GET /vulnerabilities/v2beta1/advisories?aliases=CVE-2026-41178

That query returns every advisory in the Chainguard catalog that matches the CVE — server-side, paginated, no per-image calls needed. Combine that with /registry/v2beta1/repos to get your org's image list, intersect the two sets in memory, and you have the same answer in two API calls regardless of how many images your registry has.

That's cgr-impact.py in the repo — pure Python, no dependencies beyond stdlib. It only needs a CHAINGUARD_TOKEN environment variable, which makes it straightforward to integrate anywhere:

bash

export CHAINGUARD_TOKEN=$(chainctl auth token)
python3 cgr-impact.py --parent my-org.com --cve CVE-2026-41178

The tradeoff is detail. The chainctl-based script scans each image individually so it can tell you the exact package version installed and whether it's the tag you actually run. The API approach matches on artifact names from the global catalog — it tells you the repo is affected but not which specific tag triggered it. For triage purposes that's usually enough. For remediation tracking you want the full detail.

Both tools are in the repo. The README has a comparison table if you're trying to decide which fits your situation.

Matching package names to images

If you run Chainguard images in production, you already know the security advisories page. It's the canonical, daily-updated record of every known vulnerability across the catalog: CGA ID, CVE ID, severity, affected package, and remediation status. As of this writing it lists well over 800,000 advisories. It is genuinely excellent reference data.

But notice what every row is keyed on: a package.

CGA-x6gc-2vwc-3wfx | CVE-2014-3517 | openstack-nova-2026.1 | Unspecified

That row tells you a package is affected. It does not tell you whether you are running anything that contains that package.

The dashboard knows everything about the catalog and nothing about your registry — because it can't. It has no idea which 80 or 800 images your org actually pulls.

So when an advisory lands and someone in the incident channel asks the only question that matters in that moment: "which of the images we run are affected?" the dashboard leaves you to figure it out yourself.

Why this is annoying in practice

The pivot you have is backwards. The advisory data is indexed CVE → package. The question you need answered is CVE → images in my registry. There's no "show me every image in my org that contains this package" button, and the console's per-image advisory view means you'd be opening images one at a time and reading down the list. For a handful of images that's tolerable. For a real registry it's a tab-juggling afternoon, and you'll still miss one.

The obvious scripted fix is worse than it sounds. Pull every image and run syft, or crane export the filesystem and grep — and now you're downloading hundreds of megabytes per image. A 100-image registry turns into a 20-minute coffee break, which is exactly the kind of latency you don't want sitting between "advisory published" and "we know our exposure."

You don't actually need the image bytes to answer this question. You need its bill of materials. And Chainguard already attaches one.

The fast path: two chainctl commands

The whole approach rests on two calls that most people don't think to combine.

  1. Get the ground-truth list of what you run.

chainctl images list --parent

This returns every repo in your org with its real tags and lastUpdated timestamps — not a guess, not crane ls output you have to sanity-check, but the registry's own view of what exists.

  1. Read each image's advisories without pulling the image.

chainctl images advisories list

This is the key. It fetches the SBOM attestation attached to the image — a small metadata artifact, not the image itself — then queries the advisory database server-side and hands back structured JSON: package names, versions, CGA IDs, CVE aliases, and status (detected, fixed, pending_upstream_fix, false_positive_determination). It runs in about 3.5 seconds per image because nothing large ever crosses the wire.

Those two commands are the entire foundation. The CVE-to-image pivot the dashboard doesn't give you is sitting right there in tooling you already have authenticated.

What the tool actually does

find-images-with-package.sh is a wrapper that turns those two commands into the answer you wanted. It flips the index — CVE → affected images in your registry — and adds the three things that make it usable at scale:

It runs in parallel. Twenty advisory lookups at a time means a ~100-image registry resolves in roughly 20–30 seconds instead of 20 minutes. The --parallel flag tunes it up for big registries or down for slow links.

It picks canonical tags. Chainguard images carry a lot of tag aliases for the same digest — 1.29, 1.29.1, v1.29, v1.29.1-r3 all point at one image. Scanning all of them is wasted work. The tool reduces each repo to its minimal canonical set (prefer latest if present; otherwise the shortest tag per major.minor) and drops -dev, -debug, .att, and .sig tags. You scan each real image once.

It needs almost nothing installed. Just chainctl and python3. No crane, no syft, no jq.

Using it

Look up the ID on the advisory dashboard and pass it straight through:

./find-images-with-package.sh --parent my-org.com --cve CVE-2026-2303

It takes a CVE, GHSA, or CGA ID interchangeably:

./find-images-with-package.sh --parent my-org.com --cve CGA-pcxj-4r8g-gw43 ./find-images-with-package.sh --parent my-org.com --cve GHSA-447v-2qg4-h8hc

Or search by the package name the advisory lists — handy when one advisory names several affected packages and you want to check each:

./find-images-with-package.sh --parent my-org.com --package openssl

Add --output affected-images.txt and you've got a clean list to feed straight into a remediation ticket.

A run looks like this:

Fetching image list for: cgr.dev/my-org.com Found 77 repos (82 tags to scan). Checking advisories for 'CVE-2026-2303' (20 parallel)...

Results ──────────────────────────────────────────────────────────── ✔ argocd-repo-server [latest] argo-cd-3.2-compat 3.2.3-r1 | CGA-j6cj-9c6p-fwrc | fixed ✔ thanos [latest] thanos 0.41.0-r16 | CGA-hwwj-6chc-r6vx | fixed ────────────────────────────────────────────────────────────

Summary Repos checked : 77 Matches : 2 No SBOM : 3 (custom/external images, cannot scan)

Two affected images out of 77, named, versioned, with their advisory status, in under a minute. That's the difference between "we'll get back to you on our exposure" and an answer in the same conversation.

Where it stops

Worth being honest about the edges:

Images without a Chainguard SBOM attestation can't be scanned. Custom or externally-mirrored images simply have no bill of materials to read; they show up in the summary as No SBOM: N so you know what wasn't covered rather than silently assuming it's clean. uniqueTags registries get skipped. Orgs configured with only timestamped tags have no stable aliases to reduce to, so those repos fall out of scope.

Neither of those is a flaw in the approach so much as a boundary of what attestation-based scanning can see — and surfacing them explicitly beats a tool that quietly pretends to have checked everything.

The takeaway

The advisory dashboard answers "what's wrong with this package?" Your incident response needs "what's wrong with my registry?" — and the data to bridge the two is already in the SBOM attestations Chainguard ships with every image. chainctl images list plus chainctl images advisories list, run in parallel over your real tags, closes that gap in seconds. The repo is here if you want to point it at your org.

15 views