Skip to main content

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_TOKEN or 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โ€‹

YearAction / VectorWhat happened
2020Codecov uploaderBash 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.
2023tj-actions/changed-filesAction was compromised; malicious code printed repository secrets into workflow logs. Affected thousands of repositories.
2025reviewdog/action-*Compromise of the reviewdog action family; credential exfiltration from CI pipelines.
note

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:

.github/workflows/ci.yml
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:

.github/aw/actions-lock.json
{
"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:

.github/workflows/ci.yml
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.yml and 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:

.github/dependabot.yml
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.

Quick Referenceโ€‹

PracticePriority
Pin all uses: references to full commit SHAsHigh
Keep a lock file mapping versions to SHAsHigh
Set minimum permissions on every jobHigh
Review action source before adoptingMedium
Enable Dependabot for github-actionsMedium

Further Readingโ€‹