A container image bundles far more than your code. It carries a base OS, system libraries, language runtimes, and every dependency your application resolved at build time. Any one of those layers can contain a known vulnerability, and a FROM node:20 you set months ago quietly accumulates new CVEs as advisories are published against it. Scanning the image — not just the source repo — is the only way to see the whole picture. The steps below apply the same way regardless of base distro, so a Rocky Linux container scan works with the exact same commands as a Debian or Alpine one, since the CLI identifies the OS package manager automatically. This guide walks through scanning an image end to end, interpreting the findings, and blocking risky images before they get promoted to production.
Prerequisites
- Docker (or another OCI-compatible runtime) installed and an image built locally or pushed to a registry.
- The Safeguard CLI installed:
curl -sSfL https://get.safeguard.sh/install.sh | sh. - Registry credentials available if you are scanning a private image (a
docker loginsession is sufficient — the CLI reuses it).
Step 1: Build or pull the image you want to scan
Use a concrete, immutable reference. Scanning latest makes results irreproducible, so tag with a version or digest:
docker build -t registry.example.com/checkout:1.8.2 .
Step 2: Run the image scan
Point the CLI at the image reference. It inspects every layer, identifies the OS package manager, and resolves application dependencies inside the image:
sg image scan registry.example.com/checkout:1.8.2
You will see output grouped by layer and severity:
Scanning registry.example.com/checkout:1.8.2 (linux/amd64)
base: debian 12.5 — 312 OS packages
app: npm — 1,046 components
findings:
critical: 2
high: 9
medium: 21
top issues:
[CRITICAL] CVE-2025-12345 — openssl 3.0.11 — heap overflow (OS layer)
[HIGH] CVE-2024-21538 — cross-spawn 7.0.3 — RCE (app layer)
exit: 2 (findings above policy threshold)
Step 3: Separate OS findings from app findings
The two layers have different owners. OS-package CVEs are usually fixed by bumping your base image; application CVEs are fixed in your lockfile. Split them so you route each to the right fix:
sg image scan registry.example.com/checkout:1.8.2 --group-by layer --format json \
| jq '.findings | group_by(.layer)'
A large batch of OS findings almost always means an outdated base image — that is the single highest-leverage fix.
Step 4: Generate an SBOM from the image
You can produce a bill of materials for exactly what shipped in the image, base layers included:
sg image scan registry.example.com/checkout:1.8.2 --sbom --format cyclonedx-json \
--output image-sbom.cdx.json
This image SBOM is more complete than a source SBOM because it captures the base-OS packages your Dockerfile pulled in but never listed explicitly.
Step 5: Gate the image in your build pipeline
Fail the build when the image exceeds your risk threshold so a vulnerable image never reaches the registry:
sg image scan registry.example.com/checkout:1.8.2 --fail-on high
Exit code 2 means "findings above threshold." Wire that into your pipeline exactly like a failing test — a non-zero exit blocks the push step.
Verify the result
Confirm the scan covered what you expected:
# The scanned digest matches the image you built
docker inspect --format='{{index .RepoDigests 0}}' registry.example.com/checkout:1.8.2
# Re-run after a base-image bump and confirm OS findings drop
sg image scan registry.example.com/checkout:1.8.2 --fail-on critical
If a base-image upgrade cleared your OS findings but the exit code is still non-zero, the remaining issues are in the application layer — fix those in your lockfile and re-scan.
How Safeguard streamlines this
Point-in-time scanning tells you an image was clean when you built it — not that it is clean now. Secure Containers continuously re-evaluates every image you have scanned as new advisories land, so a base image that was clean last week gets flagged the moment a CVE is published against it, without a rebuild. Because the CLI uses the same engine for image, source, and SBOM scans, an application dependency flagged in your repo shows up as the identical finding in the built image — no reconciling two tools. And since container findings feed the same SCA reachability model, you can tell whether a vulnerable library is actually invoked by your entrypoint versus merely present in a base layer, which is what lets you fix the two critical issues that matter instead of chasing thirty that do not. See how it scales on the pricing page.
Ready to scan your first image? Connect a registry at app.safeguard.sh/register.
Frequently Asked Questions
Should I scan the source repo or the container image? Both, because they catch different things. A source scan sees your application dependencies before build; an image scan additionally sees the base OS, system libraries, and anything the Dockerfile installed. The image is what actually runs in production, so it is the authoritative artifact to gate on.
How do I fix operating-system-layer vulnerabilities? Most OS-layer CVEs are resolved by updating your base image to a patched tag or digest, then rebuilding. If a fix is not yet available upstream, switching to a minimal or distroless base image often eliminates the vulnerable package entirely by removing it from the image.
Does scanning require running the container? No. The scan analyzes the image's layers and metadata statically, without executing the container. That makes it safe to run in CI on untrusted images and fast enough to gate every build.
Why does my image show more vulnerabilities than my repository? Because the image includes everything your base layers and system package manager pulled in, not just the dependencies your code declared. Those base-OS packages are real, shippable attack surface, which is exactly why scanning the built image matters.
Can I scan an image in a private registry?
Yes. Authenticate to the registry first (for example with docker login) and the CLI reuses those credentials to pull and inspect the image. Only the metadata needed for analysis is read; the image is not stored elsewhere.
Does this work for RPM-based distros like a Rocky Linux container scan?
Yes. The CLI detects the underlying package manager — dpkg on Debian/Ubuntu, apk on Alpine, rpm/dnf on Rocky Linux, RHEL, and Amazon Linux — and resolves OS-package CVEs against the correct vendor advisory feed automatically. You do not need a different command or flag per distro.
Explore Secure Containers, the Safeguard CLI, and SCA, or compare plans on the pricing page. Step-by-step setup lives in the Safeguard docs.