GitHub Actions Supply Chain Security
GitHub Actions workflows fetch third-party actions from GitHub repositories. Each uses: reference is a dependency โ and like npm packages, actions can be compromised, typosquatted, or modified after the version tag you referenced was originally pinned. This page covers the risks and how to address them.
Why Actions Are a Supply Chain Riskโ
A GitHub Actions workflow is code that runs with access to your repository contents, secrets, and tokens. A compromised action can:
- Exfiltrate
GITHUB_TOKENor custom secrets - Modify repository files or create branches
- Inject malicious artifacts into your build outputs
- Use your runner as a pivot point for further attacks
Unlike npm packages, actions don't have a globally audited registry. There is no equivalent of npm audit. The action code that runs on your runner is fetched live from GitHub โ unless you pin it.
Notable Incidentsโ
| Year | Action / Vector | What happened |
|---|---|---|
| 2020 | Codecov uploader | Bash uploader script was tampered with; CI environment variables (including tokens) were exfiltrated to an attacker-controlled endpoint. Not a GitHub Action, but identical threat model: fetching and executing untrusted remote code in CI. |
| 2023 | tj-actions/changed-files | Action was compromised; malicious code printed repository secrets into workflow logs. Affected thousands of repositories. |
| 2025 | reviewdog/action-* | Compromise of the reviewdog action family; credential exfiltration from CI pipelines. |
The threat is real and recurring. Actions that have thousands of users are attractive targets.
Attack Vectorsโ
Mutable Tagsโ
By default, uses: actions/checkout@v4 resolves to whatever commit the v4 tag currently points to. Tags are mutable โ the action author (or an attacker who gains access) can move the tag to a different commit. Your workflow pins the tag string, not the underlying code.
Typosquattingโ
A malicious actor registers actlons/checkout (with a lowercase L instead of i) and waits for a copy-paste error. The action executes with full access to your CI environment.
Compromised Maintainer Accountโ
The action author's GitHub account is hijacked. The attacker pushes a new commit, moves the version tag to point to it, and every workflow using that action now runs the malicious code.
Preventionโ
Pin Actions to a Full Commit SHAโ
The most reliable mitigation is pinning every action reference to an immutable full commit SHA rather than a mutable tag or branch name:
steps:
# Instead of:
- uses: actions/checkout@v4
# Pin to the exact commit SHA:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
A commit SHA cannot be moved โ it is immutable by definition. If the action is later compromised and the tag is updated, your workflow is unaffected.
Include the version as a comment so the pinned SHA remains human-readable.
Maintain an Actions Lock Fileโ
Track the SHA-to-version mapping in a lock file committed to your repository. This provides a single source of truth for which action versions are in use:
{
"entries": {
"actions/checkout@v4.2.2": {
"repo": "actions/checkout",
"version": "v4.2.2",
"sha": "11bd71901bbe5b1630ceea73d27597364c9af683"
}
}
}
Keep this file in version control. When updating an action version, update both the workflow file and the lock file in the same commit so the change is reviewable.
Restrict Token Permissionsโ
Set the minimum permissions your workflow needs at the job level. The default GITHUB_TOKEN has broad write access โ narrowing it limits what a compromised action can do:
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
Use contents: read as the default for read-only workflows. Only escalate to write for the specific jobs that need it.
Audit New Actions Before Adoptingโ
Before adding a new action:
- Review the action's source repository. Read
action.ymland the scripts it calls. - Check when the repository was last actively maintained.
- Check how many other repositories use it โ obscure actions with few users are higher risk.
- Prefer actions published by GitHub itself (
actions/*) or established organizations over third-party actions by individuals.
Use Dependabot for Automated Updatesโ
Configure Dependabot to keep action versions current. Regular updates mean you get security patches and reduce the drift between your pinned SHA and the latest stable version:
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
When Dependabot opens a PR to update an action, it updates the SHA reference. Review the diff in the action's source repository before merging.