Safeguard
Product•

Scan Images in Amazon ECR, Azure Container Registry, Docker Hub and GCP with Safeguard

The exact read-only credential each registry needs, how to connect Amazon ECR, Azure Container Registry, Docker Hub and Google Artifact Registry to Safeguard, and the limits to plan around.

Safeguard Security Team
39 min read

To scan the images you keep in Amazon ECR, Azure Container Registry, Docker Hub and Google Artifact Registry, give each registry its own dedicated, read-only identity: an IAM user with AmazonEC2ContainerRegistryReadOnly for ECR, a service principal with AcrPull for ACR, a personal access token with Repo Read-only for Docker Hub, and a service account with roles/artifactregistry.reader for Artifact Registry. Point your scanner at each registry with that identity, record every scan against the image digest rather than the tag, and decide up front how new tags get rescanned: on push where the registry can tell you, on a schedule where it cannot, and in the pipeline for the images you build yourself.

That is the short answer. The rest of this post is the per-registry setup in full: the narrowest permission that works on each cloud, the credential types that fail and why, a same-day checklist that works with any scanner, and then the Safeguard flow for all four registry cards, card by card, with the limits stated plainly.

Key takeaways

  • The four registries authenticate four different ways: an AWS access key, an Entra ID service principal or repository-scoped token, a Docker Hub personal access token, and a Google service account key. Setup for one teaches you little about the others.
  • Least privilege is reachable on all four: ECR read actions scoped to repository ARNs, AcrPull on one registry, a Docker Hub token with Repo Read-only and an expiry, and Artifact Registry Reader at the repository level.
  • Tags move and digests do not. A scan that records the digest describes one image forever.
  • Safeguard's Amazon ECR integration (GA), Azure Container Registry integration (GA), Docker Hub integration (GA) and GCP Cloud Source integration for Google Artifact Registry (GA) list your repositories and tags, turn each selected image into a project, and scan it for an SBOM and vulnerability findings. All four only read.
  • The Azure Container Registry card pins each scan to the digest and can scan on push through an ACR webhook. The other three rescan on a schedule or by hand.
  • On the ECR, Docker Hub and Artifact Registry cards, the connected credential lists repositories and tags but the scan's image pull does not use it today, so plan to cover private images with the CLI step in your pipeline.
  • SBOM generation and analysis (GA), SCA and vulnerability management with EPSS and KEV (GA) and reachability analysis and prioritisation (GA) run on every project the cards create.
  • The Safeguard CLI (GA) scans images in your pipeline, and policies and gates (GA) decide the outcome: Block, Warn or Notify, with approval for known risks through exceptions.

The situation

Very few organisations keep their images in one place. The platform team standardises on Amazon ECR. An acquired product line arrives with its own Azure Container Registry. Internal tools are published to a Docker Hub organisation nobody has audited in a while, and a GKE group pushes to Artifact Registry in two regions while older manifests still reference gcr.io.

The problem is the scanner. Whoever set it up usually did it for the registry they knew, with whatever credential was quickest: an access key on a broad IAM user, the ACR admin user, a Docker Hub password, a service account with Editor on the project. One registry gets scanned, the other three do not, or they get scanned by an identity that can push and delete.

A security review then asks two questions. Which images, in which registries, have we scanned, and against which exact content? And what can the scanning identity do if its secret leaks? Answering both means treating each registry as its own setup problem.

Four registries, four identity models

Before any step-by-step, it helps to see the four side by side. The shape of the credential decides almost everything else: how you scope it, how it expires, how you rotate it.

RegistryRead-only identityNarrowest grantExpires on its own?
Amazon ECRIAM user access keyECR read actions on the repository ARNs, plus ecr:GetAuthorizationToken on all resourcesNo
Azure Container RegistryEntra ID service principal, or a repository-scoped tokenAcrPull on one registry, or a scope map with content/read and metadata/read per repositoryOnly if you set an expiry
Docker HubPersonal access tokenRepo Read-only, on an account that sees only what should be scannedOnly if you pick an expiry
Google Artifact RegistryService account JSON keyArtifact Registry Reader on each repositoryNo, unless an organisation policy sets one

Every registry has a read-only role that is enough for listing and pulling. Most of these secrets never expire unless someone sets a date, so rotation is yours to own. And the scoping unit differs: ECR and Artifact Registry scope to repositories in IAM, ACR through a scope map or an ABAC condition, and Docker Hub only through which account holds the token.

The rules behind it

NIST SP 800-190, the Application Container Security Guide, names registry risks directly, among them insecure connections to registries, stale images and insufficient authentication and authorisation restrictions. A scanner is one of the readers, so the same rule applies to it: read access and nothing more.

NIST SP 800-218, the Secure Software Development Framework, asks producers to protect code from unauthorised access and tampering and to identify and confirm vulnerabilities on an ongoing basis. For images, "ongoing" means a new advisory can make yesterday's clean image vulnerable without a byte of it changing, which is why rescans matter even for images nobody has rebuilt.

The OCI Distribution Specification is where the tag and digest distinction comes from. A tag is a name that can be moved to a different manifest at any time; a digest is a content hash of the manifest. Any claim that has to stand up later, such as "this release was clean when we shipped it," needs the digest.

Step by step, with or without Safeguard

The steps below work with any scanner. They are what a careful team does before it connects a tool to anything.

1. Inventory what you have

List every registry that holds images you run or ship. For each one, write down the account, subscription, namespace or project, the region where it applies, and the teams that push to it. ECR and Artifact Registry are regional, so a single AWS account or Google Cloud project can have repositories in several regions that a single-region connection will not see. ACR is one registry per login server, and a subscription can hold many. Docker Hub is organised by namespace: your personal Docker ID and every organisation you belong to.

Do not forget the public images you depend on. A base image pulled from Docker Hub or ECR Public is part of your supply chain even though you never pushed it.

2. Create a dedicated scanning identity per registry

One identity per registry, used only by the scanner, named so its purpose is obvious in an audit: safeguard-ecr-read, safeguard-acr, a Docker Hub token described as "Safeguard," safeguard-ar-read. You can then revoke it without breaking a deployment pipeline, and read its activity in the audit log on its own.

3. Grant the narrowest read that works, per registry

This is where the four diverge, so each gets its own section.

Amazon ECR. The AWS managed policy AmazonEC2ContainerRegistryReadOnly contains everything a scanner needs and other read-only actions besides. If you want exactly the actions and nothing else, an inline policy can grant ecr:DescribeRepositories to list repositories, ecr:DescribeImages to list tags, ecr:BatchGetImage to read manifests, ecr:GetDownloadUrlForLayer to download layers and ecr:BatchCheckLayerAvailability, which is part of AWS's standard pull set. The one action that cannot be scoped to repositories is ecr:GetAuthorizationToken, which AWS requires on all resources:

{
  "Version": "2012-10-17",
  "Statement": [
    { "Effect": "Allow", "Action": "ecr:GetAuthorizationToken", "Resource": "*" },
    {
      "Effect": "Allow",
      "Action": [
        "ecr:DescribeRepositories",
        "ecr:DescribeImages",
        "ecr:BatchGetImage",
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchCheckLayerAvailability"
      ],
      "Resource": "arn:aws:ecr:<region>:<account-id>:repository/*"
    }
  ]
}

Replace the wildcard with specific repository ARNs to narrow it further. Full-access or PowerUser policies work but hand the scanner push and delete rights, and root user access keys should never be created at all.

Azure Container Registry. For a registry in the Azure public cloud, a Microsoft Entra ID app registration with a client secret, assigned AcrPull on the one registry, is the cleanest choice. If the registry uses the RBAC Registry plus ABAC Repository Permissions mode, the equivalent data-plane role is Container Registry Repository Reader, which you can scope with an ABAC condition to the repositories you want scanned, and Container Registry Repository Catalog Lister if the scanner should be able to list every repository. None of these is a control-plane role; the scanner needs nothing from Azure Resource Manager.

For named repositories only, a repository-scoped token with content/read and metadata/read in its scope map is the alternative, though it cannot list the registry catalog. Avoid the admin user: it is one checkbox, and it grants push and pull to every repository.

Docker Hub. Create a personal access token with the Repo Read-only permission and an expiration date. Docker lets you choose 30 days, 90 days or a custom date up to one year; the default, None, never expires, and you cannot change a token's expiry later. The important subtlety is that Docker scopes a token's repository permission to every repository the account can reach, so the real scoping control is the account. Use an account that is a member of only the namespaces the scanner should see.

Google Artifact Registry. Create a service account used only for scanning and grant it roles/artifactregistry.reader. Grant it on each repository to be scanned rather than on the project, using repository-level IAM. The service account key is a JSON file that Google lets you download once. User credentials, an OAuth client file or a short-lived access token are not service account keys and will not work in a tool that expects one.

gcloud iam service-accounts create safeguard-ar-read --project <project-id>
gcloud artifacts repositories add-iam-policy-binding <repository> \
  --location <region> --project <project-id> \
  --member "serviceAccount:safeguard-ar-read@<project-id>.iam.gserviceaccount.com" \
  --role roles/artifactregistry.reader
gcloud iam service-accounts keys create key.json \
  --iam-account safeguard-ar-read@<project-id>.iam.gserviceaccount.com

4. Test the credential before you hand it over

Check each credential with a plain login first. For Docker Hub, docker login with your Docker ID and the token as the password. For Artifact Registry:

cat key.json | docker login -u _json_key --password-stdin https://<region>-docker.pkg.dev

For ECR, aws sts get-caller-identity confirms the account and aws ecr describe-repositories in the right region confirms listing. For ACR, a docker login against the login server with the service principal's client ID and secret does the same. If the plain login fails, the scanner will too.

5. Pin every scan to a digest

Whatever tool you use, make sure each result records the manifest digest the tag pointed at when the scan ran. Without it, a scan result for api:latest is a statement about an image you can no longer identify. With it, you can answer "was the image we deployed on Tuesday affected?" by looking up the digest your orchestrator actually ran. ECR also supports making tags immutable per repository, which removes the moving-tag problem at the source for the repositories where you can turn it on.

6. Decide how new tags get scanned

There are three ways a scanner hears about a new image, and most teams need at least two of them:

  • On push. The registry calls the scanner when a tag is pushed. This is the fastest path, and it depends on the registry offering a webhook or event you can wire up.
  • On a schedule. The scanner relists repositories and rescans on a timer. This catches new tags late, but it also catches new advisories against old images, which push events never do.
  • In the pipeline. The build step scans the image before it is pushed. This gives build-time evidence for the images you produce yourself, and it does not depend on the registry at all.

7. Write the gate rule down before you need it

Decide in advance what blocks a release, what only warns, and who may accept a known risk and for how long. A rule written during an incident is a rule written under pressure. Typical starting points are "no critical vulnerabilities in production images" as a blocking rule and "no high vulnerabilities with a known exploited status" as a warning, with accepted risks carrying a written justification and an expiry date.

A same-day checklist

  • List every registry, account, region and namespace that holds images you run or ship.
  • Create one scanning identity per registry, used by nothing else.
  • Grant the read-only role on that registry, scoped to repositories where the cloud allows it.
  • Set an expiry on every secret that supports one, and a rotation reminder on the two that do not.
  • Test each credential with docker login before you use it.
  • Confirm your scanner records digests, not just tags.
  • Choose on push, schedule or pipeline for each registry, and write down why.
  • Write the gate rule and the exception process before the first scan comes back.

Should you scan in the registry or in the pipeline?

Both. A registry scan covers everything already there, including images pushed by hand, images built before pipeline scanning existed, and old images that a newly published advisory now affects. A pipeline scan covers each image the moment it is built and is the right place to fail a build, but it only sees what your pipelines produce. The registry scan answers "what do we have," and the pipeline scan answers "should this go out." The trade-offs are covered in more depth in Should you scan container images in the registry or in CI?

How do push triggers differ across the four?

Azure Container Registry has first-class webhooks: a webhook with the push action, a service URI and custom headers, scoped to the repositories and tags that should trigger it. Amazon ECR emits an event to Amazon EventBridge when each image push completes, Artifact Registry can publish repository changes to Pub/Sub, and Docker Hub repositories support webhooks on push. The last three need glue between the event and the scanner, and whether a scanner consumes them is a property of the scanner. Where push triggers are not available, a schedule plus a pipeline step covers the same ground.

How Safeguard does this

Safeguard has a card for each of the four registries under Integrations, on the Project/Product tab. Each card shows "Secure by default" with Docs, Connect and Set up CI/CD. The cards share a shape: you pick Public or Private, enter the registry details and a credential, verify, select repositories and tags, and choose a connect option. Each image you select becomes a project, and Safeguard scans it for an SBOM and vulnerability findings.

On every project the cards create, SBOM generation and analysis (GA) produces the SBOM, SCA and vulnerability management with EPSS and KEV (GA) turns its components into findings, and reachability analysis and prioritisation (GA) helps you order them. The connect step offers a fast, progressive or deep scan. All four cards only read from the registry; none of them pushes, deletes, untags or changes registry settings.

Here is how the four compare before the card-by-card detail:

Card in the appPublic tab takesPrivate tab takesRescan new tags
Amazon ECRAn ECR Public image URLAccount ID, region, access key ID and secret access keySchedule or manual
Azure Container RegistryAn image URL on a registry with anonymous pullLogin server plus a service principal, a repository-scoped token or the admin userScan on push webhook, schedule or manual
Docker HubA Docker Hub image page URLDocker Hub username and personal access tokenSchedule or manual
GCP Cloud SourceAn Artifact Registry or gcr.io image or repository URLProject name, project ID, region and a service account JSON keySchedule or manual

Part one: Amazon ECR

The Amazon ECR integration (GA) connects Safeguard to your images in private ECR repositories and in ECR Public. On the Public tab you enter an ECR Public image URL, either a public.ecr.aws or a gallery.ecr.aws address with the alias and repository, and no credential. On the Private tab you enter a name, the AWS account ID, the region, and the access key ID and secret access key of an IAM user, and Safeguard lists every repository the key can describe in that account and region.

The permissions in Safeguard's docs match the inline policy above: ecr:DescribeRepositories, ecr:GetAuthorizationToken and ecr:DescribeImages are required, and the three pull actions are recommended for when the image is pulled for a scan. Safeguard never needs push, delete or policy-change actions, or anything outside ECR.

The docs give the AWS CLI version of the setup:

aws iam create-user --user-name safeguard-ecr-read
aws iam attach-user-policy --user-name safeguard-ecr-read \
  --policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
aws iam create-access-key --user-name safeguard-ecr-read
# Your account ID, for the form:
aws sts get-caller-identity --query Account --output text

We have not run these commands for this post; they are copied from Safeguard's docs. In the console the path is the same: create the user with no console access, attach the policy on its Permissions tab, then create an access key on its Security credentials tab, choosing Other on the best-practices page. AWS shows the secret once.

To connect it in Safeguard:

  1. Open Integrations and choose the Project/Product tab.
  2. Click Connect on the Amazon ECR card.
  3. For public images, choose Public, enter a name, paste the ECR Public URL and click Add, then Next.
  4. For private repositories, choose Private and fill in Name, Account ID, Region, AWS Access Key ID and AWS Secret Access Key.
  5. Click Verify Credentials. "AWS credentials validated successfully" means Safeguard could describe your repositories.
  6. Click Next, select the repositories and tags to scan, review, and choose a connect option: fast, progressive or deep scan.

Three ECR-specific points from the docs are worth planning around. The form takes an IAM user access key; it has no field for a session token or a role ARN, so temporary credentials from STS or IAM Identity Center will not work. A connection is for one account and one region, so repositories in another region need their own connection, and a "Failed to fetch tags" error is often exactly that. And the ECR card has no webhook: each selected image is scanned when you connect it, and new tags are picked up by a schedule under Integrations, Scheduled, or a manual rescan from the project.

Part two: Azure Container Registry

The Azure Container Registry integration (GA) connects Safeguard to the images in an ACR registry. It does two things the other three cards in this post do not: it pins each scan to the image digest the tag points at when the scan starts, and the scan pulls the image with the credential you connect. Safeguard's docs also note that this card is switched on per workspace, so if you do not see it under Integrations, ask your Safeguard contact.

On the Public tab you paste an image URL such as myregistry.azurecr.io/team/app:1.0. That only works when anonymous pull is turned on for the registry, which Azure offers on the Standard and Premium tiers. On the Private tab you enter the login server and pick an authentication method:

Method in the dialogUse it whenWhat you enter
Service principalRecommended for registries on *.azurecr.ioTenant (directory) ID, client (application) ID and client secret
Admin user or token, with a repository-scoped tokenYou want access to named repositories onlyThe token name as username, its password, and the repositories one per line
Admin user or token, with the admin userQuick setup on a registry you controlThe registry name as username and either admin password

The docs recommend the service principal. Safeguard uses the Microsoft Entra client credentials flow and the registry's token exchange, with no Azure Resource Manager role. It needs AcrPull, or Container Registry Repository Reader in ABAC mode, and never AcrPush, AcrDelete, Owner or Contributor.

The docs give the Azure CLI for both credential types:

# Service principal with AcrPull on one registry
ACR_ID=$(az acr show --name <registry> --query id --output tsv)
az ad sp create-for-rbac --name safeguard-acr --role AcrPull --scopes "$ACR_ID" --years 1
# prints appId (client ID), password (client secret) and tenant

# Or a repository-scoped token
az acr token create --name safeguard --registry <registry> \
  --repository <team/app> content/read metadata/read
az acr token credential generate --name safeguard --registry <registry> \
  --expiration-in-days 90 --password1

# Public tab only: allow anonymous pull (Standard or Premium tier)
az acr update --name <registry> --anonymous-pull-enabled

These are copied from the docs and were not run for this post. When you create the client secret in the portal, paste its Value into Safeguard, not its Secret ID.

To connect it in Safeguard:

  1. Open Integrations and choose the Project/Product tab.
  2. Click Connect on the Azure Container Registry card.
  3. For public images, choose Public, enter a name, paste the image URL and click Add, then Next.
  4. For private images, choose Private, enter a name and the login server, for example myregistry.azurecr.io, and pick the authentication method.
  5. Click Verify Credentials. "Credentials verified." means the registry accepted them, and the dialog shows how many repositories are visible.
  6. Click Next, select repositories and tags, review, and choose fast, progressive or deep scan.

Scan on push. This is the card's distinctive feature. In the connection's "Scan on push (optional)" panel, Safeguard shows a service URI on api.safeguard.sh that ends in the connection's ID, a header named X-Safeguard-Webhook-Token with its secret value, and the Azure CLI command to create the webhook:

az acr webhook create --registry <registry> --name safeguard<id> --actions push \
  --uri "<Service URI>" --headers "X-Safeguard-Webhook-Token=<secret>" --scope "<repository>:*"

The webhook's scope decides which repositories and tags start a scan. Each push scans the pushed tag and records it as a version named from the tag plus the first characters of the digest, so two pushes to the same tag show up as two versions. Creating the webhook needs a role that can manage registry webhooks; Safeguard's own credential does not have one and should not. Without a webhook, rescan on a schedule under Integrations, Scheduled.

Two Azure-specific limits from the docs. The service principal method supports *.azurecr.io only, so Azure China and Azure Government registries cannot use it. And a registry that allows only private endpoints, or whose firewall blocks Safeguard, will fail to connect even with the right credential; Safeguard needs to reach the registry over the network. Managed identity and workload identity federation are not offered on this card.

Part three: Docker Hub

The Docker Hub integration (GA) connects Safeguard to images on Docker Hub. On the Public tab you paste a Docker Hub page URL, either an official image such as https://hub.docker.com/_/nginx or a user or organisation image under /r/. On the Private tab you enter your Docker Hub username and a personal access token, and Safeguard lists the repositories in that namespace with their tags.

The token needs Repo Read-only, which Safeguard uses to sign in for a short-lived bearer token and to list repositories and tags. Leave "Allow Cloud Sandboxes access" off.

Token typeWorks with the card?
Personal access token, Repo Read-onlyYes, recommended
Personal access token with Read and Write, or Read, Write and DeleteWorks, not recommended; Safeguard never writes or deletes
Personal access token, Repo Public Read-onlyPartly; only public repositories are listed
Organisation access tokenNo; Docker Hub rejects it on the repository-listing path Safeguard uses
Account passwordWorks, not recommended; not available when the organisation enforces SSO
A GitHub token or another vendor's tokenNo; Safeguard detects a pasted GitHub token and says so

Docker has no CLI command that creates a token, so make it in Account settings, Personal access tokens, with an expiration date and Repo Read-only.

To connect it in Safeguard:

  1. Open Integrations and choose the Project/Product tab.
  2. Click Connect on the Docker Hub card.
  3. For public images, choose Public, enter a name, paste the Docker Hub URL, click Add, then Next.
  4. For private repositories, choose Private and enter a name, your Docker Hub username and the access token.
  5. Click Verify Credentials. "Docker Hub credentials verified successfully!" means Docker Hub accepted the token.
  6. Click Next, select repositories and tags, review, and choose fast, progressive or deep scan.

Use your Docker ID as the username, not your email address. The card has no webhook, so new tags come in through a schedule under Integrations, Scheduled, or a manual rescan.

Part four: Google Artifact Registry

In the app, this card is named GCP Cloud Source. Despite the name, it connects Safeguard to Docker images in Google Artifact Registry, on <region>-docker.pkg.dev hosts, and to public images on gcr.io hosts. It does not connect Cloud Source Repositories, which hold source code, so do not reach for it to scan a Git repository.

On the Public tab you paste an image or repository URL such as us-docker.pkg.dev/<project>/<repository>/<image> or gcr.io/<project>/<image>. That needs the repository to allow public reads, which in Artifact Registry means allUsers holds Artifact Registry Reader on it. On the Private tab you enter the project name, project ID and region, then drop the service account JSON key on the form. "Service account JSON uploaded successfully" means the file is a valid key.

The role is Artifact Registry Reader, used to check the key, list repositories and tags, and read images. Safeguard's access token uses Google's standard cloud-platform scope, so what it can do is limited by the service account's roles, which is why the role should stay read-only and repository-scoped.

The form wants the JSON key file exactly as Google provides it. A base64-encoded key, an OAuth client file, user credentials or an access token are rejected, and Workload Identity Federation is not offered on this card.

To connect it in Safeguard:

  1. Open Integrations and choose the Project/Product tab.
  2. Click Connect on the GCP Cloud Source card.
  3. For public images, choose Public, paste a repository URL and click Add, then Next.
  4. For private repositories, choose Private, fill in Project Name, Project ID and Region, and drop the key file on Service Account JSON.
  5. Click Next, select the images and tags, review, and choose fast, progressive or deep scan.

Like ECR, a connection covers one project and one region; repositories in a second region need a second connection. The card has no webhook, so new tags come in through a schedule or a manual rescan.

Part five: the private-pull limit, and how to plan for it

Read this before rolling the cards out. On the Amazon ECR, Docker Hub and GCP Cloud Source cards, the credential you connect lists repositories and tags, but the scan's image pull does not use it today. A private image the scanner cannot pull is reported as a failed scan with the registry's own error, rather than silently showing nothing. The Azure Container Registry card is different: its scan pulls with the connected credential. Cover private images on the other three by scanning them where they are built, which is part six.

Part six: scan the image in the pipeline

Every registry card has a Set up CI/CD button. You pick your CI, the image and the environments, and it gives you the steps. Under the hood it is one Safeguard CLI (GA) step after the image is built:

safeguard ci scan --image "ghcr.io/acme/api:$TAG" --environment prod --wait --fail-on critical

That example is from Safeguard's container image CI docs, which use a GitHub registry path; for these registries the image reference would be your ECR, ACR, Docker Hub or Artifact Registry path. The step scans the image on the runner and records it in Safeguard under the image's repository, one version per environment and tag, so api:1.4.2 built for prod becomes version prod-1.4.2. A latest or missing tag uses the build's commit instead.

The docs give the setup:

  1. Get a secret key with scans:trigger and scans:read. Admins create one in the Set up CI/CD sheet or under Settings, Developer.
  2. Store it in your CI as SAFEGUARD_API_KEY. The docs show this for GitHub Actions, GitLab CI/CD, Bitbucket Pipelines, Jenkins and Azure Pipelines.
  3. Add the step after the image is built, once per environment, each with its own --environment.

This is what covers the private-pull limit. When the step runs on the machine that built the image, the CLI reads it from the local Docker daemon, with nothing to set up. When it runs elsewhere, set SAFEGUARD_REGISTRY_USERNAME and SAFEGUARD_REGISTRY_PASSWORD as CI secrets so the scan can pull it. Only the results leave the runner. The docs suggest failing only where it matters: --fail-on critical in production and no --fail-on in development.

Part seven: put a gate on it

Policies and gates (GA) decide what a finding means for a release. Per Safeguard's docs, policies evaluate your SBOMs and generate findings, and gates are the checkpoints that enforce them. Conditions include vulnerability severity, CVSS score, EPSS score, license, component age and maintenance, and templates include No Critical Vulnerabilities, License Compliance and No Abandoned Packages.

Each policy has one Action:

ActionWhen the policy fails
BlockThe gate fails
WarnThe gate still passes, with a warning that names the policy
NotifyThe gate still passes, and Safeguard sends a notification to the workspace's configured channels: the in-app bell, connected chat integrations, and email by each person's preferences

Approval for a known, accepted risk runs through exceptions, not through switching a gate off. You click Request Exception, select the policy and the finding, give a business justification and an expiration date, and submit it for approval. Approvers are notified, they grant or deny it, and the decision is recorded in an audit trail.

In the pipeline, the CLI can fail on the gate instead of, or as well as, on a severity:

safeguard ci scan --wait --fail-on-gate <gate name or id | all>

That exits 0 when the gate is Passed or Warning, and non-zero when it is Failed, Error or Not evaluated. Safeguard's docs note that policy outcomes are turned on per workspace, so if the Outcome column is missing from your Gates and Policies tables, ask your Safeguard contact.

Part eight: fix where the code lives

A registry scan tells you what is wrong with an image, but the fix belongs in the source repository that builds it. If that repository is connected to Safeguard, autonomous remediation with Griffin fix pull requests (GA) can open the dependency fix there; it ships switched off, with every category set to Manual. Once the fix merges, the pipeline builds a new image and the next scan records the new digest.

A day-one rollout plan

A sequence that gets all four registries covered in a day without surprises:

  1. Morning: identities. Create the four scanning identities, test each with docker login, and record a rotation date.
  2. Late morning: ACR. Connect it with a service principal, run a fast scan on the repositories that matter most, and have an administrator create the scan-on-push webhook.
  3. Midday: the other three. Connect ECR, Docker Hub and Artifact Registry, one connection per account and region or namespace, and note which private images report a failed pull.
  4. Afternoon: schedules and pipelines. Set rescan schedules under Integrations, Scheduled, then use Set up CI/CD to add the CLI step where images could not be pulled, starting with production.
  5. End of day: gates. Link a Block policy for critical vulnerabilities and a Warn policy for the next tier to a gate, and add --fail-on-gate to the production step once you have seen results.

Troubleshooting the errors you will actually see

The messages below are among those Safeguard's docs list for these four cards.

CardMessage you seeLikely causeFix
ECRInvalid AWS credentials. Please check your details.AWS refused the key, or it lacks ecr:DescribeRepositories or ecr:GetAuthorizationTokenCheck the key is active and attach the read-only policy
ECRFailed to fetch tagsThe key lacks ecr:DescribeImages, or the repository is in another regionAdd the action, or connect the other region separately
ACRThese credentials could not be verified.Wrong secret, a disabled token, an expired password or a missing roleCheck the role or scope map, and paste the secret's Value
ACRThis token can't list repositories.A repository-scoped token was usedAdd one repository per line under Repositories
ACRAzure error AADSTS53003A Conditional Access policy blocks the service principalExclude the service principal from that policy
Docker HubDocker Hub rejected the credentials for userWrong username, an expired or revoked token, or extra charactersRe-copy the raw token and use your Docker ID, not your email
GCP Cloud SourceInsufficient permissionsThe service account has no read roleGrant roles/artifactregistry.reader
GCP Cloud SourceInvalid Artifact Registry URLThe URL is missing the region, project or repositoryCopy the repository path from the console

Rotation and disconnect

All four cards rotate the same way. Create the new secret in the cloud first, find the connection under Project Configurations on the Project/Product tab, choose Update from its menu, paste or upload the new secret and save, and only then delete the old secret in the cloud. In that order there is never a window without a valid credential. Docker Hub tokens cannot have their expiry extended, so rotation there is always a new token.

To disconnect, choose Delete from the connection's menu under Project Configurations and confirm Delete configuration, then delete the credential in the cloud. On ACR, also delete the push webhook with az acr webhook delete.

On storage, the four cards behave alike. Safeguard stores the connection's settings, the SBOM and findings of each scanned image, and the credential, encrypted at rest and never shown again after you save it. It does not store image layers; the image is read for the scan and not kept. The docs state that no customer data from these integrations is sent to third-party LLMs.

What Safeguard does not do

  • It does not pull private images with your connected credential on ECR, Docker Hub or Artifact Registry today. The credential lists repositories and tags. Private images the scanner cannot pull show up as failed scans. Use the CLI step in the pipeline for those.
  • It does not take temporary or federated credentials on these cards. No ECR session tokens or role assumption, no ACR managed identity or workload identity federation, no Artifact Registry Workload Identity Federation. Each card takes a long-lived secret whose rotation you own.
  • It does not scan on push for ECR, Docker Hub or Artifact Registry. Those cards rescan on a schedule or by hand. Only the Azure Container Registry card has a push webhook.
  • It does not cover Azure China or Azure Government registries with a service principal. The service principal method supports *.azurecr.io only.
  • It does not connect Google Cloud Source Repositories. The GCP Cloud Source card is for container images, despite its name.
  • It does not decide your exceptions. Gates apply Block, Warn and Notify as you configure them. Who may accept a risk, for how long and on what evidence is your decision.

Common mistakes

  • Using the ACR admin user because it is one checkbox. It grants push and pull to every repository. A service principal with AcrPull takes a few minutes longer and grants a fraction of the access.
  • Leaving a Docker Hub token's expiry at None. It never expires, and you cannot add an expiry later.
  • Reaching for a Docker Hub organisation access token. It looks right for an organisation namespace and is rejected on the repository-listing path.
  • Granting Artifact Registry Reader on the whole project. Repository-level IAM is available and narrower.
  • Assuming a registry inventory means every private image was scanned. On three of the four cards, check for failed pulls and cover those images in the pipeline.
  • Gating on tags. Record and gate on what the digest says; a tag can move after the gate passed.

FAQ

How do I scan Amazon ECR images with Safeguard?

Create a dedicated IAM user with AmazonEC2ContainerRegistryReadOnly, create an access key, and connect it on the Amazon ECR card under Integrations with your account ID and region. Select the repositories and tags to scan. For ECR Public images, use the Public tab with a public.ecr.aws or gallery.ecr.aws URL and no credential.

Can Safeguard scan ACR images on every push?

Yes. The Azure Container Registry card's "Scan on push (optional)" panel gives you a service URI, a header secret and the az acr webhook create command. Each push scans the pushed tag as its own version.

What Docker Hub token does Safeguard need?

A personal access token with Repo Read-only and an expiration date. Organisation access tokens do not work with the card.

Does the GCP Cloud Source card scan Google Cloud Source Repositories?

No. Despite its name, it connects Google Artifact Registry and public gcr.io images. It does not connect source code repositories.

Why did a private image show a failed scan after I connected ECR, Docker Hub or Artifact Registry?

On those three cards the connected credential lists repositories and tags, but the scan's image pull does not use it today. Scan those images in your pipeline with the Safeguard CLI, which reads from the local Docker daemon or pulls with registry credentials you set as CI secrets.

How do I stop a vulnerable image from being deployed?

Link a policy with the Block action to a gate, and add --fail-on-gate to the safeguard ci scan step in your pipeline so the job fails when the gate fails. Use Warn or Notify for issues that should not stop a release, and handle accepted risks through exceptions.

Four registries, four read-only identities, and one place where the results land. What follows is the part that matters once those scans come back and you have to decide what to do about the packages they flag.

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 categories include dependencies, licenses, security posture and findings, and each one is set separately. 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.

3. Gold Registry: start from components that are already fixed

Gold Registry provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs and a completed license check, with signed build provenance (signed with Safeguard's key and verifiable with cosign, SLSA Build Level 3 as the design target) 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. Ask which packages it covers before you plan around it.

Your situationWhat to use
A fixed version existsAutonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version
You need to know today whether you are affectedGold Open Source lookup and "Am I affected?" checker, free and without login
You want vulnerable versions to stop shippingSafeguard policies and gates in CI
Upstream has no fix yet, or will not ship oneA custom Gold request; fork and patch
You want to start clean on new projectsPull 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: install, open the interactive terminal, and ask it to fix a lodash CVE in a repository. It reads the finding and advisory, asks before any command that changes something, edits package.json, runs the install and tests with approval, and pushes nothing. It is the same CLI the pipeline step above calls.

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, then scans from the terminal. It is useful when you want to try an image scan from a workstation before wiring the CLI step into a pipeline.

Click through it at your own pace, or open it full screen.

The third demo connects Claude to the Safeguard MCP server with OAuth and chooses which tools Claude can use. Once your registry images are projects in Safeguard, that connection is how an assistant reaches their findings.

Click through it at your own pace, or open it full screen.

Quick check

Why can ecr:GetAuthorizationToken not be scoped to a repository ARN?

Because AWS requires that action on all resources. It returns a registry-wide authorisation token rather than acting on a single repository, so the inline policy grants it on "*" and scopes every other action to repository ARNs.

Why does a repository-scoped ACR token need you to name the repositories?

Because repository-scoped permissions cannot list the registry catalog. A token with content/read and metadata/read can pull and list tags in the repositories its scope map names, but it cannot discover which repositories exist.

Why should an image scan record a digest rather than a tag?

Because, under the OCI Distribution Specification, a tag is a movable pointer to a manifest, while a digest is a hash of the manifest's content. A scan tied to a digest describes exactly one image; a scan tied to a tag describes whatever the tag pointed at that day.

Related reading

Sources

Download Safeguard at safeguard.sh/download, and read the product documentation at docs.safeguard.sh.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.