To track container images per environment in Safeguard, add one safeguard ci scan --image step after each image build, give it an --environment name such as prod, and Safeguard records that build as its own version, for example prod-1.4.2. Add --wait --fail-on critical only where a vulnerable image should stop the pipeline. Connect your registries under Integrations to scan images that already exist.
That is the short answer. The rest of this post covers why the standards ask for it, how it works today, and where the limits are.
Key takeaways
- Container image scanning (SCA of container layers, plus registry integrations) is GA. It produces an SBOM and dependency vulnerability findings for each image.
- The CI step is the Safeguard CLI (GA) running
safeguard ci scan --image ... --environment ... --wait --fail-on ...on your runner. Only results leave the runner. - Every environment gets its own version (
prod-1.4.2,staging-1.4.2,dev-<commit>), which is how Safeguard answers "what runs where". - Fail thresholds are per step, so production can fail on critical while development only reports.
- Vulnerability management with EPSS and KEV enrichment (GA), policies and gates (GA) and SBOM generation (GA) apply to image versions the same way they apply to code.
- Fixing image findings uses base image updates, Gold Open Source lookups (GA), Gold Registry images (GA) and autonomous remediation fix pull requests (GA, ships switched off with every category set to Manual).
- Self-healing containers are early access. They are interesting, but they are not where a team should start.
- Image scanning does not see the host kernel, the container runtime or how the container is configured when it runs.
The problem: images change meaning after you build them
A container image is a frozen bundle of an operating system userland, language runtimes, libraries and your application. The day you build it, its contents are fixed. The vulnerability data about those contents is not. A library that was clean on Monday can have a published CVE on Thursday, and the image in production has not changed at all.
Most teams also run the same application in several places at once. Development might run whatever was built from the last commit. Staging runs a release candidate. Production runs last week's release, or the release before that if a rollback happened. When a new critical CVE lands, the urgent question is not "is this library in our code?" but "which of the images actually running in production contain it, and which tag is that?"
Without per environment tracking, the answers come from places that disagree: the registry, which knows every tag ever pushed, and deployment manifests that may reference a tag that has since moved. A scan result attached to api:latest tells you little, because latest has pointed at many images this month.
There is a second, quieter problem. Teams that scan images often apply one threshold everywhere. Either every pipeline fails on any high severity finding, which trains developers to ignore red builds in development, or nothing fails anywhere, which means a critical finding can walk straight into production. Neither matches how risk actually differs between a sandbox and a customer facing service.
What NIST SP 800-190 says about image vulnerabilities and provenance
NIST Special Publication 800-190, the Application Container Security Guide, was published in September 2017 and is still the federal reference for container risk. It lists five image risks in section 3.1: image vulnerabilities (3.1.1), image configuration defects (3.1.2), embedded malware (3.1.3), embedded clear text secrets (3.1.4) and use of untrusted images (3.1.5).
On vulnerabilities, section 3.1.1 makes the point above in formal terms: an image built with up to date components will, at some point, contain components with newly discovered vulnerabilities. Because containers are not patched in place, the fix has to be made in the image and the image redeployed. NIST describes the common risk as deployed containers inheriting vulnerabilities from the image version they were started from.
The countermeasures in section 4.1.1 read almost like a requirements list for the workflow in this post. NIST asks for tools that:
- Integrate with the whole image lifecycle, from the build, through the registries the organisation uses, to runtime.
- Show vulnerabilities at every layer of the image, not only the base layer, with central visibility across the organisation.
- Enforce policy through "quality gates" at each stage of build and deployment. NIST's own example is a build rule that stops images with vulnerabilities above a chosen CVSS threshold.
Section 4.1.5 covers untrusted images. It recommends keeping a set of trusted images and registries, identifying each image by cryptographic signature, validating signatures before execution, and ongoing monitoring of those repositories so images are maintained as vulnerabilities change.
Section 4.2.2, on stale images in registries, is the one most teams skip. NIST recommends pruning unsafe images and deploying by immutable names that point to discrete versions, rather than trusting a latest tag that is "only a label" and no guarantee of freshness. That is the foundation for knowing what runs where.
NIST lists two risks this post does not address: embedded malware and clear text secrets inside images. They are real, and they need their own controls. This post is about dependency vulnerabilities, versions and thresholds.
What the CIS Docker Benchmark adds
The CIS Docker Benchmark is a community consensus set of secure configuration recommendations for Docker hosts, daemons and images. The current version listed on the CIS site is 1.9.0. The benchmark itself is a PDF behind a download form, so check the exact recommendation numbers in the version you use.
Its image and build file recommendations cover ground that complements vulnerability scanning: using trusted base images, keeping unnecessary packages out of images, scanning images and rebuilding them to pick up security patches, and running as a non root user. The benchmark treats scanning and rebuilding as a routine, not a one time event, which is the same lesson as NIST 3.1.1.
Most of the CIS benchmark concerns host and daemon configuration. Those checks are outside what an image scan can see, a point we return to in the limits section.
What the NSA and CISA Kubernetes Hardening Guide adds
NSA and CISA published a joint Kubernetes Hardening Guide in August 2021 and updated it to version 1.2 in August 2022, according to CISA's announcement of the update. The guide is about hardening clusters: pod security, network separation, authentication and authorisation, audit logging and keeping the cluster upgraded. In that model, image security is one layer among many, and the cluster cannot compensate for an image that ships known vulnerable components.
The practical overlap with this post is simple. If the cluster is where workloads run, the cluster needs to know which image digest is running in each environment, and the build pipeline needs to stop images that should never reach the cluster in the first place.
What the OCI image spec and Kubernetes say about digests
The Open Container Initiative image specification defines a content descriptor with a required digest property, which acts as a content identifier. Retrieved content "SHOULD be verified against this digest" when it comes from untrusted sources, according to the OCI descriptor spec. The OCI annotations spec also defines org.opencontainers.image.base.name and org.opencontainers.image.base.digest, which record the image an image was built from. Those two annotations are the cleanest way to answer "which of our images are built on the base image that just got a CVE?"
The Kubernetes documentation on images says the same thing from the runtime side. Digests are hashes of the image content and are immutable; tags can be moved to point at different images. If you specify both a tag and a digest, only the digest is used for pulling. If you specify neither, Kubernetes assumes the latest tag.
A per environment inventory needs both: tags for people, digests for machines.
What SLSA and NIST SSDF add
SLSA v1.0 defines build levels for provenance. Build L1 means provenance exists showing how the artifact was built. Build L2 adds signed provenance from a hosted build platform. Build L3 adds hardened builds. For containers, provenance is what links a digest in production back to a source commit and a build.
NIST SP 800-218, the Secure Software Development Framework, frames the process side:
- PO.4.1 asks organisations to define criteria for software security checks and track them through the lifecycle. Example 2 mentions vulnerability severity scores as a measure, which is exactly what a fail threshold is.
- PO.4.2 asks for processes and toolchains that gather the information needed to apply those criteria, and suggests automating the decisions.
- PW.4.4 asks teams to verify that acquired open source and third party components meet their requirements throughout their life cycles. Example 1 is to check regularly for publicly known vulnerabilities.
- RV.1.1 asks teams to monitor vulnerability databases and other sources for vulnerabilities in the software and its third party components, and to investigate credible reports.
- PS.3.1 and PS.3.2 cover archiving each release and keeping its provenance data.
Read together, the standards ask for the same four things: know what is in each image, know which image runs where, gate images by policy at each stage, and keep watching after the build.
Requirements vs what Safeguard does vs what you still own
| Requirement (source) | What Safeguard does today | What you still own |
|---|---|---|
| Visibility at every image layer (NIST SP 800-190 4.1.1, item 2) | Container image scanning (GA) builds an SBOM of the image and matches it against vulnerability databases | Choosing which images and registries to connect |
| Lifecycle integration from build to registry (800-190 4.1.1, item 1) | CI step after the build (CLI, GA); registry cards under Integrations (GA) | Adding the step to each pipeline and keeping credentials current |
| Quality gates with a severity threshold (800-190 4.1.1, item 3; SSDF PO.4.1) | --fail-on <severity> per CI step; platform policies and gates (GA) | Deciding the threshold per environment and the exception process |
| Immutable, discrete versions (800-190 4.2.2; OCI digests) | One Safeguard version per environment and tag; Harbor and Azure Container Registry scans pinned to the digest | Deploying by digest or immutable tag in your manifests |
| Trusted images and signature validation (800-190 4.1.5; SLSA) | Attestation and signing (GA) and Gold Registry images (GA) | Enforcing signature checks at your cluster's admission point |
| Continuous checking of components (SSDF PW.4.4, RV.1.1) | Vulnerability management with EPSS and KEV enrichment (GA); scheduled rescans of registry images | Setting rescan schedules and acting on findings |
| Rebuild with patches (800-190 3.1.1; CIS Docker Benchmark) | Fix pull requests via autonomous remediation (GA, ships off); self-healing containers (early access) | Merging, rebuilding, testing and redeploying |
Which registries connect to Safeguard?
Safeguard's container image documentation says every container registry card under Integrations has a Set up CI/CD button, and names Amazon ECR, Docker Hub, Azure Container Registry, Google Artifact Registry, Harbor, Quay "and the rest" among them. The docs also have a generic OCI Registry card for anything that speaks the OCI distribution API.
The details differ by card, and those differences matter.
| Registry card | How you connect | What to know |
|---|---|---|
| Amazon ECR | Public: an ECR Public URL. Private: AWS account ID, region and an IAM access key with read only ECR access | Assuming an IAM role is not available yet. No webhooks; rescan on a schedule |
| Docker Hub | Public: a Docker Hub URL. Private: username and a personal access token with Repo Read only | Organisation access tokens do not work. No webhooks; rescan on a schedule |
| Azure Container Registry | Login server plus a service principal, a repository scoped token or the admin user | Switched on per workspace. Scans pinned to the digest the tag points at |
| Harbor (2.0 or later) | Harbor URL plus a robot account or user | Switched on per workspace. Scans pinned to digest. Optional scan on push webhook |
| OCI Registry (generic) | Registry URL plus username and token, or anonymous for public images | Registries with their own card work better through that card |
| Google Artifact Registry, Quay and others | Listed as registry cards in the container image docs | Check each card's docs page for its credentials |
Every registry integration page we read for this post says the same thing about access: Safeguard only reads. It never pushes, deletes or retags images. For each image you select, Safeguard creates a project and scans it for an SBOM and vulnerability findings. Image layers are read for the scan and not stored. The pages also state that no customer data from these integrations is sent to third party LLMs.
One limit to know before you connect a private registry
The Amazon ECR, Docker Hub and generic OCI Registry docs pages each carry the same note: Safeguard uses the credential you connect to list repositories and tags, but the scan's image pull does not use that credential yet. A private image the scanner cannot pull is reported as a failed scan with the registry's error.
The Harbor and Azure Container Registry pages say the opposite for those two cards: the scan pulls the image with the credential you connect. If your private images live behind ECR, Docker Hub or a generic OCI registry and the registry scan fails, the CI step described below is the dependable route, because it scans the image on your own runner.
Least privilege for each registry credential
The docs are specific about what each credential needs. A few examples:
- Amazon ECR. A dedicated IAM user with the AWS managed policy
AmazonEC2ContainerRegistryReadOnly, or an inline policy withecr:GetAuthorizationToken,ecr:DescribeRepositoriesandecr:DescribeImages(required), plusecr:BatchGetImage,ecr:GetDownloadUrlForLayerandecr:BatchCheckLayerAvailability(recommended). Every action exceptecr:GetAuthorizationTokencan be scoped to repository ARNs. Root access keys should never be used, and temporary STS credentials do not work because the form has no session token field. - Docker Hub. A personal access token with Repo Read only and an expiry date. Docker scopes the token to every repository the account can reach, so the docs suggest using an account that can see only what Safeguard should scan.
- Harbor. A project robot account with only Repository List and Pull and Artifact List and Read, and an expiry of 90 days or less.
- OCI Registry. A robot or service account with pull access to the repositories you want scanned. Catalog access is optional; the catalog is not part of the OCI specification, and without it you add repositories by URL.
These are read only grants. If a reviewer asks why Safeguard needs push rights, the answer from the docs is that it does not.
Step by step: add the image scan to CI
This is the core of per environment tracking. The steps below follow Safeguard's CI/CD Integration and Container images docs pages exactly.
-
Create a secret key with the right scopes. The key needs
scans:triggerandscans:read. Admins can create one in the Set up CI/CD sheet on a registry card, or under Settings › Developer. Other users ask an admin. The CI/CD docs add that you can lock a key to one project. -
Store the key in your CI as
SAFEGUARD_API_KEY. The docs have per system guides for GitHub Actions, GitLab CI/CD, Bitbucket Pipelines, Jenkins and Azure Pipelines. Any other CI that runs a shell step on a Linux or macOS runner withbashandcurlalso works. There is no plugin, Action or Pipe to install. -
Decide where the image is read from. If the scan step runs on the machine that built the image, it reads from the local Docker daemon and there is nothing to set up. Otherwise it pulls from the registry, and you set
SAFEGUARD_REGISTRY_USERNAMEandSAFEGUARD_REGISTRY_PASSWORDas CI secrets. On GitLab's own registry, the docs say to use$CI_REGISTRY_USERand$CI_REGISTRY_PASSWORD. -
Add the step after the build, in each environment's pipeline. The single command the docs show is:
safeguard ci scan --image "ghcr.io/acme/api:$TAG" --environment prod --wait --fail-on critical
The full GitHub Actions example from the docs installs the CLI and runs the scan right after the build:
- name: Build
run: docker build -t "ghcr.io/acme/api:${{ github.sha }}" .
- name: Safeguard image scan (prod)
env:
SAFEGUARD_API_KEY: ${{ secrets.SAFEGUARD_API_KEY }}
SAFEGUARD_INSTALL_DIR: ${{ runner.temp }}/safeguard-bin
IMAGE: ghcr.io/acme/api:${{ github.sha }}
run: |
set -eu
curl -fsSL https://cli.safeguard.sh/install | bash
"$SAFEGUARD_INSTALL_DIR/safeguard" ci scan --image "$IMAGE" --environment prod --wait --fail-on critical
-
Push and read the result. The scan runs on the runner. Only the results leave it, and no repository integration is needed. Safeguard records the image under its repository as the project (for example
ghcr.io/acme/api) and records this build as one version for the environment and tag. -
Pin the CLI version once it works. The CI/CD docs recommend
SAFEGUARD_VERSION=<version>on the install line so a new CLI release does not change a build under you. -
Use the Set up CI/CD button if you prefer. Each registry card has one. You pick your CI, the image and the environments, and Safeguard gives you the steps to paste.
The flags that matter
| Flag | What it does, per the docs |
|---|---|
--image <ref> | The image to scan. Implies an on runner scan |
--environment <name> | The environment this build is for; names the version |
--wait | Waits for the result. Fails the step if the scan fails or runs longer than --timeout (default 30m) |
--fail-on <severity> | With --wait, fails on a vulnerability at or above critical, high, medium or low |
--fail-on-gate <gate> | With --wait, fails when a named policy gate (or all) is not Passed or Warning |
--project <name> | Records the image under another project instead of the image repository |
When the step fails for the wrong reason
The container image docs list three messages worth knowing:
reading image …means the image was not in the local daemon and could not be pulled. Set the registry credentials, or run the step where the image was built.BAD_ENVIRONMENTmeans the environment name has characters other than letters, digits and dashes.--image and --path are two different targetsmeans both were passed. Use--imagefor an image, or--local --pathfor code.
The general CI/CD page adds the key problems: AUTHENTICATION_REQUIRED or INVALID_SECRET_KEY when the key is missing, invalid, expired or revoked; MISSING_SCOPE when the key lacks scans:trigger or scans:read; and PROJECT_NOT_ALLOWED when a key locked to one project is used for another.
Why one version per environment and tag answers "what runs where?"
Safeguard's docs define the version naming precisely:
| Image | --environment | Version Safeguard records |
|---|---|---|
ghcr.io/acme/api:1.4.2 | prod | prod-1.4.2 |
ghcr.io/acme/api:1.4.2 | staging | staging-1.4.2 |
ghcr.io/acme/api:latest | dev | dev-<commit> |
A latest tag, or no tag at all, takes the build's commit instead. That one rule removes the most common source of confusion in image inventories: a result attached to a moving tag.
The design has three useful consequences.
The same image can appear in more than one environment, on purpose. If 1.4.2 is in staging today and promoted to production tomorrow, you get staging-1.4.2 and prod-1.4.2 as separate versions. Each carries the findings as of the scan that recorded it. When someone asks whether production is running the build that staging approved, you can compare the two.
Production answers are not polluted by development churn. Development builds land as dev-<commit>, one per commit. They do not overwrite the production version, so a dashboard filtered to production versions shows what you shipped, not what someone pushed ten minutes ago.
Incident questions get a direct answer. When a CVE lands, the question "which production images contain this component?" becomes a filter on versions named prod-... under each image project, rather than a guess based on which tags exist in the registry.
The model has one assumption you need to respect: the environment name is what you say it is. If a pipeline labels a build prod and then deploys it somewhere else, Safeguard has no way of knowing. The version records what the pipeline asserted when it scanned. Keep the scan step in the same pipeline stage that deploys to that environment, and the labels stay honest.
Tags, digests and the registry view
The CI step names versions from tags and commits. The registry cards add digest information for two registries. Harbor and Azure Container Registry scans are pinned to the digest the tag points at when the scan starts, and Harbor's optional scan on push webhook records a pushed tag as the version <tag>-<first 12 characters of the digest>.
For registries without webhooks (the ECR and Docker Hub docs say this explicitly), each selected image is scanned when you connect it. To pick up new tags, set a schedule under Integrations › Scheduled, or rescan by hand from the project.
Two habits make the inventory trustworthy on your side:
- Deploy by digest, or by immutable tags that are never reused, as NIST SP 800-190 section 4.2.2 recommends. A tag like
1.4.2that is rebuilt and repushed makesprod-1.4.2ambiguous. - Record the base image with the OCI
org.opencontainers.image.base.nameandorg.opencontainers.image.base.digestannotations when you build. That gives you a second way to find affected images when a base image CVE lands.
How do you set different fail thresholds per environment?
The docs give the pattern in one sentence: use one step per environment, each with its own --environment, and fail only where it matters, for example --fail-on critical in production and no --fail-on in development.
Here is that pattern written out using only the flags the docs document. These are three separate steps, one in each environment's pipeline:
# Development pipeline: record and report, never fail on findings
safeguard ci scan --image "ghcr.io/acme/api:$TAG" --environment dev --wait
# Staging pipeline: fail on high and critical
safeguard ci scan --image "ghcr.io/acme/api:$TAG" --environment staging --wait --fail-on high
# Production pipeline: fail on critical
safeguard ci scan --image "ghcr.io/acme/api:$TAG" --environment prod --wait --fail-on critical
Two details in that block are deliberate.
First, the development step keeps --wait. Without --fail-on, --wait still fails the step if the scan itself fails or times out, which tells you the pipeline is broken rather than silently skipping the scan. If you want development to be entirely non blocking, the CI/CD docs say dropping --fail-on reports without failing; your CI's "allow failure" setting covers the timeout case.
Second, staging is stricter than production in this example. That is a choice, not a typo. Some teams prefer to catch high severity findings in staging, where a fix is cheap, and reserve the production gate for critical findings so an urgent hotfix is not blocked by a high finding that has an approved exception. Others set production stricter than staging. Either is defensible. What matters is that the rule is written down, which is what SSDF PO.4.1 asks for.
A threshold decision table
| Environment | Typical threshold | Reason |
|---|---|---|
| Developer sandbox, preview branches | No --fail-on; report only | Fast feedback without training people to ignore red builds |
| Shared dev or integration | No --fail-on, or --fail-on critical | Keeps the worst issues visible early without blocking every merge |
| Staging or pre production | --fail-on high or --fail-on critical | The cheapest place to stop a release candidate |
| Production | --fail-on critical, plus a gate if your workspace uses policy outcomes | The control that has to hold, with a documented exception path |
Severity thresholds versus policy gates
--fail-on looks only at the severity of dependency vulnerabilities. That is simple and predictable, and it maps directly to the NIST SP 800-190 example of a CVSS threshold.
Policies and gates (GA) are the richer option. Safeguard's Policies & Gates docs describe policy conditions on vulnerability severity, CVSS score, EPSS score, license, component age, maintenance status and SLSA level, and policy actions of Block, Warn or Notify. With policy outcomes switched on for your workspace, safeguard ci scan --wait --fail-on-gate <gate> fails a CI job when the named gate fails.
One caution from the docs: the Policies & Gates page says gates are evaluated by the platform on the scans it runs for your connected repositories, and the CLI docs say policies and gates are evaluated in the platform, not by the CLI. Before you rely on --fail-on-gate for an on runner image scan, confirm in your workspace that the gate evaluates image versions the way you expect. --fail-on <severity> is the documented path for images today.
Exceptions belong in the platform, not in a pipeline variable. The Policies & Gates docs describe an exception request with a business justification and an expiry date, an approver, and an audit trail. That is a cleaner answer for an auditor than a commented out flag.
Where EPSS and KEV fit
Vulnerability management in Safeguard (GA) enriches findings with EPSS and CISA KEV data. FIRST describes EPSS as an estimate of the probability that a published CVE will be exploited in the next 30 days. CISA describes its KEV catalog as the authoritative source of vulnerabilities exploited in the wild and recommends it as an input to prioritisation.
The CI --fail-on flag does not take EPSS or KEV as inputs; it takes severity. Use EPSS and KEV to decide which findings in a passing image to fix first, and, if you need an exploitation aware block, express it as a policy and gate rather than a severity flag. Reachability analysis (GA) is the other prioritisation signal in Safeguard; it is covered in our post on prioritising vulnerabilities with reachability, EPSS and KEV.
How base image updates, Gold images and fix pull requests reduce image findings
Scanning tells you what is wrong. The count of findings only drops when the image changes. There are four levers, roughly in the order most teams should use them.
1. Rebuild on a current base image
Most image findings come from the base image's operating system packages. The single most effective change is to rebuild on the latest patch of the same base image tag, then rescan. Because the CI step runs after every build, the new version in each environment shows whether the rebuild cleared the findings.
If you pin base images by digest (which you should), bumping the digest is a source change. That makes it reviewable, and it makes the change show up in your Dockerfile history.
2. Check a base image before you adopt it
Gold Open Source (GA) at gold.safeguard.sh is free and needs no login. Safeguard's Gold docs say you can search a package, container image or CVE and see its known vulnerabilities with severity and fixed versions, and whether a CVE is on the CISA KEV list. Checking a candidate base image there before it lands in a Dockerfile is cheaper than scanning it after.
3. Start from Gold Registry images
Gold Registry (GA) offers hardened, drop in replacements for upstream packages and container images, held to the Gold bar described in the block further down this post. Swapping a base image for a Gold equivalent is a one line change in the FROM instruction, and the CI scan will show the effect on the next build. Our post on reading a Gold Registry evidence page covers what the evidence proves.
4. Let fix pull requests handle application dependencies
The application layer of an image (your npm ci, pip install or go build output) is fixed in source, not in the image. Autonomous remediation (GA) opens tested, policy gated pull requests that move a vulnerable dependency to a safe version, on one repository or in bulk. It ships switched off, with every category set to Manual, so nothing changes until your team turns it on and chooses how much runs on its own. When the pull request merges, the next build produces a new image and a new version per environment. The full category and strategy matrix is in our post on autonomous remediation.
| Finding sits in | Lever | Where the change is made |
|---|---|---|
| Base image OS packages | Rebuild on a current base, or swap to a Gold Registry image | Dockerfile FROM line |
| Language dependencies inside the image | Autonomous remediation fix pull request (GA, ships off) | Lockfile and manifest in the source repo |
| A component with no upstream fix | Custom Gold request; fork and patch (early access) | Gold Registry, then your FROM line or lockfile |
| Packages installed but never used | Remove them | Dockerfile |
What self-healing containers add, and why not to lead with them
Self-healing containers are early access. Safeguard describes them in one line: detect, plan, rebuild, promote, with advisory and autonomous modes.
The docs page fills in the shape. In advisory mode, Safeguard detects a new CVE in an image, plans a patch and files a pull request against your Dockerfile or Kubernetes manifest repository, and people review and merge. Autonomous modes go further, rebuilding the image, running your tests and promoting it, with promotion to production either gated on human approval or continuous with policies still applied. Configuration lives in a .safeguard/self-heal.yaml file or under ESSCM › Asset › Self-Healing › Configure.
There are good reasons not to start there:
- It is early access. Depth varies, and the product may change. Ask for a scoped demo on your own images before planning around it.
- It depends on the basics. Self-healing acts on findings from scanning, so the per environment inventory and thresholds above have to exist first. Without them there is nothing reliable to heal.
- It changes who deploys. Even advisory mode creates a new stream of pull requests. Autonomous modes push images to your registry under new tags. That needs agreement from whoever owns your release process, your test coverage and your rollback plan.
- Advisory mode is close to what you already get. A team that rebuilds on current bases and uses fix pull requests already has most of the benefit with a human in every loop.
A sensible order is: per environment CI scans, then thresholds and gates, then base image hygiene and fix pull requests, and only then a self-healing pilot in advisory mode on one non critical image.
What Safeguard does not do
Container image scanning is one control. These are the limits we want every reader to plan around.
-
It does not see the host kernel or the container runtime. An image scan reads what is inside the image. The kernel the container shares with its host, the container runtime, the orchestrator and the node operating system are outside the image, and a vulnerability there will not appear in an image finding. Patch and scan hosts with the tools your platform team uses for that.
-
It does not check how a container is configured when it runs. Privileged mode, host mounts, added capabilities, network policy, environment variables and resource limits are set at deploy time. The CIS Docker Benchmark and the NSA and CISA Kubernetes Hardening Guide cover those controls. The image scan step in this post reports dependency vulnerabilities; it is not a configuration benchmark audit.
-
A scan reflects the image at scan time. New CVEs are published after the build. A
prod-1.4.2version scanned last month only reflects last month's data until it is rescanned. For registry connected images, set a schedule under Integrations › Scheduled; for CI recorded images, make sure a rebuild or rescan happens on a cadence you choose. -
The CI step only fails the job. It does not stop an image that bypassed the pipeline, or one pulled straight from a public registry, from running in your cluster. Enforcing "only signed, scanned images run here" happens at your cluster's admission point. Safeguard's attestation and signing capability (GA) produces the signed evidence; configuring the admission policy is your platform team's job.
-
Private image pulls on some registry cards do not use your connected credential yet. The ECR, Docker Hub and generic OCI Registry docs say so directly. A private image the scanner cannot pull is reported as a failed scan. Use the CI step for those images until that changes.
-
--fail-onis a severity threshold, nothing more. It does not take EPSS, KEV or reachability into account. If you need those in a block decision, use a policy and gate, and confirm the gate evaluates image versions in your workspace. -
Environment labels are only as honest as your pipeline. Safeguard records the environment you pass. It does not discover where an image is actually deployed through the CI step.
-
Self-healing containers are early access. Treat them as a pilot, not as the control your compliance story depends on.
-
This post makes no claims about secrets or malware in images. NIST SP 800-190 lists both as image risks. They need their own controls and their own evaluation.
Common mistakes
Scanning latest. A result attached to a moving tag tells you almost nothing. Safeguard falls back to the commit for latest builds for this reason, but the better fix is to deploy immutable tags or digests.
One threshold everywhere. A single --fail-on high across every pipeline usually ends in developers disabling the step. Set thresholds per environment and write down why.
Putting the scan step in a different pipeline from the deploy. If the scan says prod but the deploy happens in a separate job that pulls whatever tag exists at the time, the version can describe an image that never ran. Scan in the same pipeline, on the same tag, that deploys.
Giving the registry credential push rights. Every registry page in Safeguard's docs says read is enough. A token with write or delete permissions is a liability with no benefit.
Forgetting the timeout. --wait fails the step after 30 minutes by default. On a very large image or a busy runner, raise --timeout rather than dropping --wait, or you lose the result entirely.
Skipping exceptions. When a critical finding has no fix and the release must ship, teams often remove the flag for one build and forget to restore it. Use a time limited exception in the platform instead.
Rebuilding without changing the base. If the Dockerfile pins a base image digest, rebuilding produces the same base. Bump the digest, or switch to a Gold Registry image, then rebuild.
FAQ
How do I scan a Docker image in CI with Safeguard?
Add safeguard ci scan --image "<your image ref>" --environment <name> --wait as a step after docker build, with a secret key holding scans:trigger and scans:read stored as SAFEGUARD_API_KEY. Add --fail-on critical (or another severity) where the build should stop. The scan runs on the runner and only results leave it.
What permissions does the Safeguard API key need for image scans?
scans:trigger and scans:read. Admins create it in the Set up CI/CD sheet on a registry card or under Settings › Developer. The CI/CD docs say a key can be locked to one project.
Can Safeguard scan images in a private registry?
Yes, two ways. The CI step can read from the local Docker daemon, or pull from the registry with SAFEGUARD_REGISTRY_USERNAME and SAFEGUARD_REGISTRY_PASSWORD. Registry cards can also list private repositories. For ECR, Docker Hub and generic OCI registries, the docs say the scan's pull does not use the connected credential yet, so the CI step is the dependable route for private images there.
How does Safeguard name image versions per environment?
<environment>-<tag>. An image tagged 1.4.2 scanned with --environment prod becomes prod-1.4.2. A latest tag or no tag uses the build's commit, as in dev-<commit>.
Can I fail production builds on critical CVEs but only report in development?
Yes. Use one step per environment. Give the production step --fail-on critical and leave --fail-on off the development step. Safeguard's docs describe exactly this pattern.
Which container registries does Safeguard support?
The docs list registry cards for Amazon ECR, Docker Hub, Azure Container Registry, Google Artifact Registry, Harbor and Quay among others, plus a generic OCI Registry card for registries that implement the OCI distribution API. Harbor and Azure Container Registry are switched on per workspace.
Does Safeguard rescan container images when new CVEs are published?
Registry connected images can be rescanned on a schedule under Integrations › Scheduled, or by hand. Harbor supports an optional scan on push webhook. The ECR and Docker Hub docs say those integrations have no webhooks. For images recorded from CI, a new build or rescan produces a new result.
Does a Safeguard image scan check the Kubernetes or Docker configuration?
No. The image scan in this post reports dependency vulnerabilities in the image's contents. Runtime configuration, host settings and cluster hardening are separate controls covered by the CIS Docker Benchmark and the NSA and CISA Kubernetes Hardening Guide.
Is self-healing for containers generally available?
No. Self-healing containers are early access. Start with CI scans, thresholds and fix pull requests, and pilot self-healing in advisory mode later.
Closing the gap between a fix and your image
Image findings drop when the image changes, and most of what changes an image is a dependency or a base you did not write. The block below is how Safeguard helps close the gap between a published fix and the image you ship, and what happens when no fix exists yet.
Fix it without waiting for upstream
Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.
1. Autonomous remediation: pull requests, not tickets
When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation covers eight categories, including dependencies, licenses, security posture and findings. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.
2. Gold Open Source: the safe version in seconds, free
gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds. The safeguard-sh/gold-scan-action GitHub Action can fail a build on a known exploited vulnerability, on any known vulnerability, or never, so a version you have just removed cannot quietly come back in next week's merge.
3. Gold Registry: start from components that are already fixed
registry.safeguard.sh provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs, zero malware and a completed license check, with SLSA L3 or higher provenance, a Sigstore signature and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.
When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. This fork and patch flow is in early access today, so ask which packages it covers before you plan around it.
| Your situation | What to use |
|---|---|
| A fixed version exists | Autonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version |
| You need to know today whether you are affected | Gold Open Source lookup and "Am I affected?" checker, free and without login |
| You want vulnerable versions to stop shipping | The Gold scan GitHub Action in CI, plus Safeguard policies and gates |
| Upstream has no fix yet, or will not ship one | A custom Gold request; fork and patch (early access) |
| You want to start clean on new projects | Pull hardened components and images from Gold Registry |
One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.
See how Safeguard would help
The first demo is a real Safeguard CLI session. It installs the CLI, opens the interactive terminal with safeguard, and asks it to fix the lodash CVE in a repository. The assistant reads the finding and advisory, asks before each command, edits package.json to lodash 4.17.21, runs npm install and npm test with approval, prints a summary and pushes nothing. It ends with /permissions, /model and /scan .. The same CLI binary is what runs safeguard ci scan --image in your pipeline, and the same kind of dependency fix is how the application layer of an image gets cleaned up before the next build.
Click through it at your own pace, or open it full screen.
The second demo sets up the local runner on your own machine, approves it with its device code, and then scans from the terminal. It does not show a container image scan. It shows the same principle the CI image step relies on: the scan runs on a machine you control and only results go to the platform, which matters when your images contain proprietary code.
Click through it at your own pace, or open it full screen.
To try this on your own images, install the CLI from safeguard.sh/download and follow the container images guide in the Safeguard docs. For related reading, see our container scanning launch post, scanning a container image in CI with the Safeguard CLI, scanning a container image with the Safeguard CLI, how container image scanning works and pinning, signing and verifying base images. In this series, turning plain English policies into release gates goes deeper on gates, and choosing between a repo connection, the CLI and the local runner covers where scans run.
Quick check
An image tagged latest is scanned for the dev environment. What version does Safeguard record?
A version named after the environment and the build's commit, in the form dev followed by the commit. Safeguard uses the commit because a latest tag, or a missing tag, does not identify a specific image.
Your production step fails on critical findings. A new critical CVE is published a week after that image was deployed. Does the pipeline protect you?
No. The threshold was applied at scan time. You need a rescan (a schedule for registry connected images, or a rebuild and rescan through CI) to see the new finding, and a fix or rebuild to remove it.
Sources
- Safeguard docs: Container images (CI/CD Integration)
- Safeguard docs: CI/CD Integration
- Safeguard docs: Amazon ECR integration
- Safeguard docs: Docker Hub integration
- Safeguard docs: Harbor integration
- Safeguard docs: OCI Registry integration
- Safeguard docs: Azure Container Registry integration
- Safeguard docs: Policies and Gates
- Safeguard docs: Safeguard CLI
- Safeguard docs: Self-Healing Containers
- Safeguard docs: Gold
- Safeguard docs: Continuous Scanning
- Gold Open Source
- Safeguard download page
- NIST SP 800-190, Application Container Security Guide (CSRC page)
- NIST SP 800-190 full text (PDF)
- NIST SP 800-218, Secure Software Development Framework (CSRC page)
- NIST SP 800-218 full text (PDF)
- CIS Docker Benchmark
- CISA: Updated Kubernetes Hardening Guide
- OCI image spec: Descriptors
- OCI image spec: Annotations
- SLSA v1.0: Security levels
- Kubernetes docs: Images
- FIRST: Exploit Prediction Scoring System
- CISA: Known Exploited Vulnerabilities Catalog
Last reviewed: 24 October 2026. Product capabilities change; check docs.safeguard.sh for the current state.