# Chainguard Actions overview

URL: https://deploy-preview-4144--ornate-narwhal-088216.netlify.app/chainguard/actions/overview.md
Last Modified: October 5, 2026
Tags: Chainguard Actions, Overview

Learn how Chainguard Actions provides hardened drop-in replacements for popular GitHub Actions to protect your CI/CD pipelines from supply chain attacks.

Chainguard Actions are a set of hardened drop-in replacements for popular GitHub Actions. Each action preserves the same inputs and outputs as the upstream version, but has been examined and revised to better protect your CI/CD pipelines from supply chain attacks. The only change in your workflow configuration is the name of the action in the uses: line.
The catalog holds more than 1,000 hardened actions. Coverage spans GitHub first-party (actions/*), cloud-provider (aws-actions/*, azure/*, google-github-actions/*), Docker, HashiCorp, and security tools actions (Trivy, Grype, CodeQL, Semgrep), as well as a growing catalog of community actions.
Each hardened action:
Is pulled from the upstream source at a pinned commit, then reviewed by a static ruleset and an AI-powered analysis pass Has every internal uses: and container image reference pinned to an immutable SHA digest Ships with a HARDENING.md report documenting exactly what was checked and fixed Ships with a signed SLSA provenance attestation recording the upstream source and the ruleset version applied (releases published before signing began don&rsquo;t carry one) Is re-reviewed and re-hardened whenever upstream publishes a new version or Chainguard adds a new rule Chainguard Actions protect against common threats including tag hijacking, dependency confusion, pull_request_target abuse, and secret exfiltration.
This page provides enough to get you started. Browse the Chainguard Actions organization to find a specific action and read its hardening report.
What hardening checks and fixes Every action in the catalog goes through the same two-stage review. A deterministic static pre-pass runs first and catches patterns mechanically. An AI-powered analysis pass then evaluates the action against the policy ruleset. Findings from either stage carry an ID that appears in the action&rsquo;s HARDENING.md report, so you can trace any change back to the check that produced it.
Policy checks Check Finding IDs Severity What it catches Unpinned uses unpinned-uses High A uses: reference or a runs.image: container reference pointing at a mutable tag or branch instead of an immutable commit SHA or image digest. The docker:// prefix is optional, so image: ghcr.io/example/tool:latest is a finding too. Script injection script-injection High Expressions such as ${{ inputs.name }} interpolated directly into a run: block, where the shell can parse attacker-controlled text as commands. Unsafe shell unsafe-shell High Remote content piped straight into an interpreter, such as curl ... | bash. Hardcoded credentials hardcoded-credentials High Literal secrets assigned to names containing password, secret, token, api_key, or aws_secret. GitHub environment injection github-env-injection High Untrusted values written to $GITHUB_ENV, $GITHUB_PATH, or $GITHUB_OUTPUT without newline sanitization, which lets an attacker inject variables into later steps. Suspicious run content suspicious-run-content High Malicious patterns in run: blocks, including obfuscated execution, process memory access, dynamic evaluation, credential file access, outbound exfiltration, reverse shells, persistence, and environment secret scraping. Permissions permissions, missing-permissions, broad-permissions Medium Workflows that leave GITHUB_TOKEN at its default permissions, or that set read-all or write-all instead of specific scopes. Static pre-pass checks The static pre-pass complements the analysis pass by catching patterns the analysis may miss. It reports one finding per occurrence, so a report can list the same ID many times, each with its own location. static-inline-injection is the most common finding in the catalog for that reason.
Finding ID Severity What it catches static-inline-injection High A single expression interpolated directly into a run: block. The finding names the expression and the step it appears in, and the fix moves the value into an env: map. static-unsanitized-env-write Medium An unsanitized write to a GitHub environment file. invalid-yaml High An action or workflow file that could not be parsed. Both stages read the action definition (action.yml or action.yaml) and every workflow under .github/workflows/ in the action&rsquo;s own repository. Neither inspects built or vendored output: dist/, vendor/, and node_modules/ are out of scope, as are the action&rsquo;s test fixtures.
Findings are fixed in place and the pipeline re-evaluates its own work, so a single hardening run can take several iterations before an action passes. Both the findings and the per-iteration notes are recorded in HARDENING.md.
Transitive dependencies Hardening the top-level action is only part of the problem. Actions pull in other actions and container images behind the scenes, and those transitive dependencies are a common path into a pipeline. Chainguard hardens the references it can see in the action definition:
Container images are pinned to digests. When an action runs a registry image (runs.using: docker with a docker:// image), the pipeline resolves the tag to its digest and rewrites the reference as image:tag@sha256:&lt;digest&gt;. A retagged image can&rsquo;t change what the action pulls. Nested actions are pinned, and swapped for hardened copies where they exist. Every nested uses: reference is pinned to an immutable commit SHA, with the original tag kept as a comment. In composite actions, when Chainguard has hardened the nested action, the reference points at the hardened copy instead of upstream. For example, the hardened actions/upload-pages-artifact calls chainguard-actions/actions-upload-artifact, not actions/upload-artifact. The action&rsquo;s own workflows are reviewed too. Both stages of the review cover the workflows under .github/workflows/ in the action&rsquo;s repository, not only the action definition. Missing dependencies can be onboarded. When a hardened action depends on an action that isn&rsquo;t in the catalog yet, request that action and Chainguard hardens and publishes it. How nested actions are swapped for hardened copies When Chainguard hardens and publishes an action, every hardened action that depends on it goes back through hardening so its uses: reference can point at the hardened copy. Three limits apply:
It covers composite actions only. Node and Docker actions have no uses: steps to rewrite. It applies only when a hardened copy exists for that exact upstream commit. It never rewrites a reference when the correct copy is ambiguous. Coverage grows as more of the catalog is hardened, so read the action.yml on the version branch you plan to use to see what it references today.
What hardening doesn&rsquo;t change Hardening doesn&rsquo;t change the packages an action bundles or installs. A JavaScript action ships the same dist/ bundle as its upstream release, and an action built from a Dockerfile uses the same base image as upstream. Packages and binaries an action downloads while it runs are outside what the review inspects.
To see the full dependency graph for your own repository, including actions reached through other actions, use the --recursive flag described in View the actions you are currently using.
Prerequisites To follow this guide, you need:
chainctl v0.2.261 or later, installed and authenticated. Refer to How to install chainctl if you don&rsquo;t have it yet. An active Chainguard organization. Owner access on the organization. Set up Chainguard Actions Setting up has two parts: entitle your organization, then choose how you migrate your workflows.
Step 1: Create the Actions entitlement Authenticate using chainctl:
chainctl auth loginCreate the Chainguard Actions entitlement for your organization:
chainctl actions entitlements createThe output confirms the entitlement:
Enabled Actions product for org chainguard.edu ($ENTITLEMENT_ID) [entitlement id: $ENTITLEMENT_ID]Confirm your entitlement:
chainctl actions entitlements list ID | CREATED ------------------------------------------|------------------------- $ENTITLEMENT_ID | 2026-06-18 17:33:24 UTC Step 2: Install the Chainguard App The Chainguard App is the recommended way to adopt Chainguard Actions across more than a repository or two. Once you install it and link it to your Chainguard organization, Guardener:
Inventories the actions your workflows use across every repository it can access Comments on pull requests that introduce unhardened actions, so your workflows don&rsquo;t drift back Opens and maintains a pull request that swaps in Chainguard hardened equivalents, once you enable migration To set it up:
Install the Chainguard App on your GitHub organization. Link your Chainguard organization to your GitHub organization with chainctl guardener github link. Add a .chainguard/actions.yaml file to each repository you want Guardener to work on, or once to your organization&rsquo;s .github repository to apply it to every repository. Both of the last two steps matter. Installing the app changes no repository on its own, and the Actions feature stays inert until a .chainguard/actions.yaml applies to the repository, either in the repository itself or through the org-level .github configuration. Once it does, pull request recommendations are on by default, but automated migration pull requests need migrate.enabled: true set explicitly:
enabled: true migrate: enabled: trueIf installing an app in your organization needs an administrator&rsquo;s approval, they will be asked to approve a specific set of GitHub permissions. Permissions Guardener requests lists each one and why it&rsquo;s needed, so you can take that to them before you start.
Refer to Getting started with Guardener for the installation and linking steps, and to Hardened Actions for the configuration reference, the migration options, and the on-demand migration command.
The Chainguard App is in beta. It runs in production and is supported, but its features and configuration may still change.
If you&rsquo;d rather not install a GitHub App, you can migrate with the cg-actions skill or by hand. Both approaches are covered in Configure your workflows to use Chainguard Actions.
Basic usage (quick start) To use a Chainguard hardened action, edit your workflow&rsquo;s YAML configuration file and change the uses: line to match the location in chainguard-actions:
- uses: chainguard-actions/&lt;action-name&gt;@&lt;commit-sha&gt; # &lt;version-tag&gt;Repository names are prefixed with the upstream organization, so tj-actions/changed-files becomes tj-actions-changed-files. This keeps two different sources of a changed-files action from clashing in the Chainguard Actions organization.
Search the Chainguard Actions repository, find the action you want to use, and then use the name you find there.
Note: Don&rsquo;t reference a hardened action with @main. The main branch of each repository holds only metadata (README.md, LICENSE_CHAINGUARD, and source.json). The hardened action itself lives on the version branches, so a reference to @main fails to resolve.
Pin to the commit SHA that a version tag resolves to, and keep the tag as a comment. Choose how to reference an action explains why, and Replace the uses: line in each workflow shows how to look up the SHA.
Choose how to reference an action Pin to a commit SHA, and pair the pin with Dependabot or Renovate:
- uses: chainguard-actions/actions-checkout@&lt;sha&gt; # v4A commit SHA is the only immutable reference in the catalog. Tags are mutable by design, and not just the floating major version. Chainguard re-hardens published versions in place and moves the tag when it does, including fully qualified patch tags, so a single upstream release can be re-hardened several times with the same tag pointing somewhere new each time. Both @v4 and @v4.3.1 resolve to whatever was published most recently.
Pinning on its own isn&rsquo;t enough, though. A pin with no tooling behind it is the one configuration that strands you: you stay on that build, and stop receiving re-hardening, until someone updates the SHA by hand. Dependabot and Renovate both track a pinned SHA against its tag and open a pull request when the tag moves, so you receive every re-hardening as a change your own CI validates before it reaches a live workflow. That gives you more control than a mutable tag does, and costs you nothing in freshness.
Pinning is also the standard Chainguard applies to the actions it hardens. The unpinned-uses check fails any uses: reference on a tag, so if you run an actions linter against your own repository, referencing a hardened action by tag will register a finding.
Use the canonical repository name in the reference. Some catalog repositories answer to an older name through a GitHub rename redirect — chainguard-actions/checkout reaches chainguard-actions/actions-checkout, for example — but a redirect isn&rsquo;t something to depend on in a pinned workflow.
Configure your workflows to use Chainguard Actions You can save some time by using the optional cg-actions skill, a Claude Code skill for auditing GitHub Actions usage and migrating to Chainguard hardened actions.
Following the steps in this section will achieve a similar result.
Inventory the actions you currently use. Run this from the root of your repository to get a deduplicated list of every uses: line across every workflow:
grep -rhE &#34;uses:\s*[^@]&#43;@&#34; .github/workflows/ | sort -uFor a more thorough inventory that also follows composite actions, use chainctl actions discover.
Check the Chainguard Actions catalog for each action. Browse the Chainguard Actions repository or use the GitHub search UI. Match by organization and action name — for example, if you use tj-actions/changed-files, search for org:chainguard-actions tj-actions-changed-files.
If the action isn&rsquo;t in the catalog, open an issue to request it.
Replace the uses: line in each workflow. Change the uses: line to match the location in chainguard-actions. Pin to the commit SHA and preserve the original tag as a comment so Dependabot, Renovate, and human reviewers can track upgrades:
# Before - uses: tj-actions/changed-files@v47# After - uses: chainguard-actions/tj-actions-changed-files@&lt;SHA&gt; # v47 # originally - uses: tj-actions/changed-files@v47To find the SHA digest for a specific release, use the gh CLI:
gh api repos/chainguard-actions/tj-actions-changed-files/commits/v47 --jq &#39;.sha&#39;4b4bd2ed96c7629e1c911f97f2390b91e1362735For the short SHA digest:
gh api repos/chainguard-actions/tj-actions-changed-files/commits/v47 --jq &#39;.sha[:7]&#39;4b4bd2eThe resulting uses: line with the full SHA digest:
- uses: chainguard-actions/tj-actions-changed-files@4b4bd2ed96c7629e1c911f97f2390b91e1362735 # v47Run the command rather than copying the digest shown here. Because Chainguard re-hardens a published version in place and moves its tag, the SHA a version tag resolves to changes each time that version is re-hardened.
Update your allowed-actions list. If your GitHub organization or repository restricts which actions can run (Settings &gt; Actions &gt; General &gt; Allow select actions), add chainguard-actions/* to the allowed patterns. Without this, workflows fail with a policy error on first run.
Note: It&rsquo;s a good idea to remove allowed actions that are no longer being used.
Commit, open a PR, and verify that CI passes. The action&rsquo;s inputs, outputs, and behavior are almost always identical to the upstream version, so no other workflow changes are typically needed.
However, read the HARDENING.md file for each hardened action before migrating. In rare cases, the hardening process requires a change to inputs, outputs, or behavior — those changes are documented in this file.
If something breaks, file an issue with a reproducer.
View the actions you are currently using in a repository Use chainctl to scan every workflow and composite action in a repository and list the actions and container images they reference:
chainctl actions discover $GIT_ORGANIZATION/$REPO scanning $GIT_ORGANIZATION/$REPO for workflows and actions ACTION | REQUESTED | USED BY -------------------------------------|-----------|--------- actions/checkout | v4 | 1 chainguard-actions/actions-checkout | v6.0.2 | 1 2 actions, 0 container imagesThe command needs a GitHub token, which it reads from $GITHUB_TOKEN or from gh auth token. The target can be a local directory (the current directory by default), an owner/repo pair, or a single action reference such as actions/checkout@v4.
By default, discover lists only the actions your workflows reference directly. Add --recursive to follow each referenced action into its own definition and resolve the full transitive dependency graph:
chainctl actions discover $GIT_ORGANIZATION/$REPO --recursiveA recursive scan makes many GitHub API calls, so it caches responses and stops after --timeout (five minutes by default). Refer to chainctl actions discover for the full set of flags.
View the actions currently available While you can search the Chainguard Actions repository directly in GitHub, you can also use chainctl to find an action.
chainctl actions catalog list --upstream-owner=$OWNERFor example, to list all the actions from the tj-actions source:
chainctl actions catalog list --upstream-owner=tj-actionsThis example returns a list of all actions in the Chainguard Actions repository that originate from the tj-actions upstream source.
To list the catalog entries available to a specific organization rather than the whole public catalog, use chainctl actions list:
chainctl actions list --parent $ORGANIZATION What ships in each hardened action Each hardened action&rsquo;s repository has a main branch and one branch per hardened version. The hardened action lives on the version branches; the main branch holds only metadata.
The main branch of each repository contains:
README.md — a pointer to the action and its upstream source LICENSE_CHAINGUARD — the Chainguard license for the hardened variant source.json — a manifest naming the upstream owner, repository, version, and commit, along with the policy SHAs applied Each version branch contains:
HARDENING.md — the authoritative, per-action record of what was checked, what was fixed, and how, including the policy SHA that pins the exact ruleset applied action.yml or action.yaml — the hardened action definition, preserving upstream inputs and outputs with fixes applied attestations/provenance.intoto.jsonl — a signed SLSA provenance attestation naming the upstream repository and commit, the ruleset version, the build times, and a SHA-256 digest for every file in the hardened action LICENSE_CHAINGUARD — the Chainguard license for the hardened variant The upstream action&rsquo;s own files, including its license and any documentation you can adapt for the hardened version Because HARDENING.md and the attestation are per-version, read them on the version branch you plan to use rather than on the main branch.
Nearly every version branch in the catalog carries an attestation. A small number of older releases were published before Chainguard began signing them, so if a version branch has no attestations/ directory, treat that release as unverifiable rather than as verified.
Chainguard doesn&rsquo;t publish a customer-facing verification procedure yet. Verification requires the signing key&rsquo;s fingerprint, which isn&rsquo;t published, so there is no complete recipe to follow today. Tooling for this is planned. In the meantime the attestation is still useful as a record: it names the upstream commit the release was built from and the ruleset version that was applied.
The continuous re-hardening process Chainguard Actions are continuously re-hardened:
When upstream publishes a new version, the pipeline re-runs and publishes a new hardened version When the hardening ruleset is updated, every action in the catalog is re-evaluated against the new ruleset and re-hardened as needed The HARDENING.md report is regenerated on every hardening run, with its own policy SHA pinning the exact set of rules that were applied Because the policy SHA is computed over the ruleset itself, any change to a rule produces a new SHA, which is what triggers the catalog-wide re-evaluation.
Request a new action or report an issue To request an action that isn&rsquo;t in the catalog, open a new action issue.
If an action isn&rsquo;t working as expected, open an action issue with the action reference, a description of the problem, and steps to reproduce.
Learn more Chainguard Actions telemetry and privacy Hardened Actions with Guardener Chainguard Actions product page For other questions, contact Chainguard. 
