A vulnerability caught in a pull request is a version bump. The same vulnerability caught by a runtime alert three weeks after deployment is an incident review, a hotfix, and possibly a customer notification. IBM's Cost of a Data Breach research has consistently shown that issues found and contained earlier cost dramatically less, and the same economics apply to container images: the cheapest place to fix a bad image is the pipeline that built it. Yet most teams either scan too late or scan so noisily that developers tune the results out. The NVD enrichment backlog that began in February 2024 made the noise worse, leaving thousands of CVEs published without the severity metadata teams rely on to triage. This guide covers how to scan Docker images in CI/CD so the gate is fast, the findings are trustworthy, and the build only fails when it should.
Step 1: Scan on every build, not on a weekly cron
A base image that was clean on Monday can have a critical CVE disclosed by Wednesday. Scan the image you just built, every time you build it, and fail the job on new, fixable, critical findings.
# Build, then gate the build on the scan result
docker build -t myapp:$GIT_SHA .
safeguard scan --image myapp:$GIT_SHA \
--fail-on critical \
--only-fixable
--only-fixable avoids failing a build for a CVE that has no patch available yet, which would otherwise leave developers with a red pipeline and nothing to do.
Step 2: Wire the gate into your pipeline
Add the scan as a required job before the push-to-registry step. A GitHub Actions example:
jobs:
build-and-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: docker build -t myapp:${{ github.sha }} .
- name: Scan image
run: |
safeguard scan --image myapp:${{ github.sha }} \
--fail-on critical --only-fixable
Step 3: Generate an SBOM as a build artifact
Produce the SBOM at build time, when the image's contents are unambiguous, and store it alongside the image. That turns "are we affected by this new CVE" into a lookup instead of a re-scan.
safeguard sbom --image myapp:$GIT_SHA \
--format cyclonedx \
--output sbom.json
Step 4: Re-scan images already in the registry
New CVEs are disclosed daily against images you built weeks ago and have not rebuilt. Schedule a re-scan of the registry so a six-month-old "approved" image cannot quietly accumulate criticals.
# Nightly re-scan of the deployed tag
safeguard scan --registry registry.example.com/myapp --tag prod
Step 5: Prioritize by reachability, not raw CVSS
A typical image scan returns hundreds of findings. FIRST's EPSS data shows only a small fraction of published CVEs are ever exploited in the wild, and CISA's KEV catalog lists the ones under active attack. Rank findings by whether the vulnerable code is reachable and whether it is being exploited, not by CVSS alone, or developers will learn to ignore the gate.
Where to scan across the pipeline
| Stage | What it catches | Gate action |
|---|---|---|
| Pre-commit / local | obvious issues before push | warn |
| Pull request | new CVEs in the diff | block merge on new criticals |
| Build | full image + dependency tree | fail on fixable criticals |
| Registry (scheduled) | newly disclosed CVEs | ticket / alert |
| Admission control | unsigned or over-threshold images | reject deploy |
How Safeguard scans your images
Safeguard runs as a single step in your pipeline and does the whole job: it parses filesystem layers and language manifests to inventory every component, matches them against vulnerability data enriched independently of the NVD backlog, and runs reachability analysis so the gate fails on findings that are actually exploitable from your code — not on the majority that sit in unreachable paths. It emits a CycloneDX or SPDX SBOM as a build artifact, re-scans registry images on a schedule, and opens an auto-fix pull request with the minimal bump when a fix exists, so a failed gate comes with a ready remedy. Drop it into any pipeline with the Safeguard CLI, gate pull requests with Secure Containers, and cover application dependencies with software composition analysis. See how the prioritized gate differs from a raw scanner in the Safeguard vs Trivy comparison, or browse the full comparison hub.
Frequently Asked Questions
What severity threshold should fail a build?
Start by failing only on new, fixable critical findings, then tighten to high once the pipeline is stable and the queue is manageable. Failing on every finding, or on unfixable ones, trains developers to bypass the gate, which is worse than a looser threshold they respect.
Should scanning block the pipeline or just warn?
Warn in early stages and on informational findings; block at the build gate on fixable criticals with a reachable path. The point of blocking is to stop a genuinely exploitable image from reaching a registry, not to make every CVE a merge blocker.
How do I stop the scanner from failing on vulnerabilities with no fix?
Use a fixable-only filter so the gate fails only when a patched version exists. Track unfixable findings separately as accepted risk with an expiry, so they are reviewed again rather than ignored forever.
Is scanning in CI enough, or do I still need runtime scanning?
CI scanning is the highest-leverage control because it is the cheapest place to fix, but it is not the only one. Re-scan the registry for newly disclosed CVEs, and verify images at admission control, so a vulnerability disclosed after build still gets caught before it runs.
Want a pipeline gate that fails only on what matters? Connect a repository at app.safeguard.sh/register, and read the CI/CD integration steps in the documentation at docs.safeguard.sh.