Back to Blog
medium SEVERITY5 min read

How GitHub Actions Mutable Action Tags Enable Supply-Chain Attacks and How to Fix Them

A GitHub Actions workflow was using `actions/checkout@v1`, a mutable tag reference that could be silently repointed by the action owner to inject malicious code. This supply-chain vulnerability was fixed by pinning the action to a specific commit SHA (`11bd71901bbe5b1630ceea73d27597364c9af683`), ensuring the workflow always executes verified, immutable code.

O
By Orbis AppSec
Published August 14, 2026Reviewed August 14, 2026

Answer Summary

GitHub Actions mutable action tag vulnerability (CWE-829) occurs when workflows reference actions using tags like `@v1` instead of immutable commit SHAs. In this YAML workflow file, `actions/checkout@v1` was vulnerable to supply-chain attacks where the tag could be silently repointed to malicious code. The fix pins the action to a full 40-character commit SHA: `actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683`.

Vulnerability at a Glance

cweCWE-829 (Inclusion of Functionality from Untrusted Control Sphere)
fixPin action to full 40-character commit SHA
riskSupply-chain attack via silently modified action code
languageYAML (GitHub Actions)
root causeUsing mutable tag reference `@v1` instead of immutable commit SHA
vulnerabilityGitHub Actions Mutable Action Tag

Introduction

The file src/negative_test/github-workflow/bad_pull_request_event_declaration.yaml contained a subtle but dangerous security flaw at line 11. While the workflow appeared to simply check out repository code using the popular actions/checkout action, the reference @v1 created a supply-chain attack vector that could allow malicious code to execute in your CI/CD pipeline without any changes to your repository.

This isn't theoretical—real-world compromises of GitHub Actions like trivy-action and kics-github-action have demonstrated how attackers exploit mutable tags to inject malicious code into thousands of downstream repositories. When you reference actions/checkout@v1, you're trusting that whoever controls that tag will never—intentionally or through compromise—point it at malicious code.

The Vulnerability Explained

When you write a GitHub Actions workflow step like this:

steps:
  - uses: actions/checkout@v1

You're telling GitHub "go fetch whatever code is currently tagged as v1 from the actions/checkout repository and run it." The critical word here is currently—that tag can change at any time.

How Tags Work in Git

Git tags are just pointers to commits. While they're often treated as immutable version markers, they can be deleted and recreated to point at different commits. This means:

  1. Today, actions/checkout@v1 might point to commit abc123
  2. Tomorrow, the repository owner (or an attacker who compromises their account) could repoint it to commit xyz789
  3. Your workflow would silently start running completely different code

Attack Scenario for This Workflow

Consider this specific workflow file handling pull request events:

name: bad_pull_request_event_declaration
on:
  pull_request:
jobs:
  with:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1

An attacker who gains access to the actions/checkout repository could:

  1. Modify the code that runs during checkout to exfiltrate secrets
  2. Repoint the v1 tag to their malicious commit
  3. Wait for any pull request to trigger this workflow
  4. Harvest GITHUB_TOKEN, repository secrets, or inject backdoors into builds

The workflow would continue to show uses: actions/checkout@v1—no visible change—while executing completely different code.

Real-World Precedent

This isn't hypothetical. In 2023, the tj-actions/changed-files action was compromised when an attacker gained access and modified the action to steal secrets from CI runs. Organizations using mutable tags were affected; those using pinned SHAs were protected.

The Fix

The fix replaces the mutable tag reference with an immutable commit SHA:

Before (Vulnerable)

steps:
  - uses: actions/checkout@v1

After (Secure)

steps:
  - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

Why This Works

The 40-character SHA 11bd71901bbe5b1630ceea73d27597364c9af683 is a cryptographic hash of a specific commit. It cannot be repointed—it's mathematically tied to that exact code. If an attacker modifies the action code, the resulting commit would have a completely different SHA.

The trailing comment # v4.2.2 serves as documentation, making it easy for developers to understand which version is pinned without the SHA being human-readable.

Additional Improvements

Note that the fix also upgrades from v1 to v4.2.2. This brings security improvements, bug fixes, and better performance from three major versions of development—while ensuring the exact code is locked to a verified commit.

Prevention & Best Practices

1. Always Pin to Full Commit SHAs

For every GitHub Action in your workflows:

# ❌ Vulnerable - mutable tag
- uses: actions/setup-node@v4

# ❌ Still vulnerable - branches are mutable
- uses: actions/setup-node@main

# ✅ Secure - immutable SHA
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2

2. Use Dependabot or Renovate

Configure automated dependency updates to keep your pinned SHAs current while maintaining security:

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"

3. Audit Existing Workflows

Run Semgrep or similar tools to find mutable references:

semgrep --config "p/github-actions" .github/workflows/

4. Verify SHAs Before Pinning

When pinning a new action:
1. Visit the action's repository
2. Navigate to the release/tag you want
3. Copy the full commit SHA from the release
4. Verify the commit is signed if possible

Key Takeaways

  • The actions/checkout@v1 pattern in bad_pull_request_event_declaration.yaml was a supply-chain attack vector that could execute arbitrary code in CI without any visible workflow changes
  • Commit SHA 11bd71901bbe5b1630ceea73d27597364c9af683 is cryptographically immutable—unlike tags, it cannot be silently repointed to malicious code
  • Pull request event workflows are high-value targets because they often have access to repository secrets and can modify code during builds
  • Adding version comments like # v4.2.2 maintains readability while preserving the security benefits of SHA pinning
  • This defensive hardening removes an exploit primitive that automated attack tools could chain with other weaknesses

How Orbis AppSec Detected This

  • Source: External GitHub Action repository referenced in workflow
  • Sink: uses: actions/checkout@v1 in src/negative_test/github-workflow/bad_pull_request_event_declaration.yaml:11
  • Missing control: No immutable reference (commit SHA) to verify action integrity
  • CWE: CWE-829 (Inclusion of Functionality from Untrusted Control Sphere)
  • Fix: Replaced mutable tag @v1 with immutable commit SHA @11bd71901bbe5b1630ceea73d27597364c9af683

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

Supply-chain security in CI/CD pipelines is increasingly critical as attackers target the software build process. The simple change from actions/checkout@v1 to actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 transforms a workflow from "trusting the internet" to "trusting cryptographically verified code."

This pattern applies to every GitHub Action in your workflows. Take time to audit your existing workflow files, pin all actions to commit SHAs, and set up automated tooling to keep those pins updated. Your future self—and your security team—will thank you.

References

Frequently Asked Questions

What is a GitHub Actions mutable action tag vulnerability?

It occurs when workflows reference actions using tags or branches that can be silently repointed by the action owner, allowing malicious code injection without any visible change to your workflow file.

How do you prevent mutable action tag vulnerabilities in GitHub Actions?

Always pin actions to their full 40-character commit SHA instead of using version tags like `@v1` or `@main`. Add a comment with the version number for readability.

What CWE is GitHub Actions mutable action tag?

CWE-829: Inclusion of Functionality from Untrusted Control Sphere, as the workflow includes external code that could be modified by an untrusted party.

Is using major version tags like @v4 enough to prevent supply-chain attacks?

No. Even major version tags can be repointed. Only full commit SHAs are truly immutable and prevent supply-chain attacks.

Can static analysis detect mutable action tag vulnerabilities?

Yes. Tools like Semgrep can detect when GitHub Actions workflows use mutable references instead of pinned commit SHAs.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #6197

Related Articles

high

How Shell Injection in GitHub Actions happens in YAML workflows and how to fix it

A high-severity shell injection vulnerability was discovered in `.forgejo/workflows/docker.yml` where `${{github.ref}}` and `${{github.ref_name}}` were directly interpolated into a bash `run:` step. An attacker could craft malicious git reference names to inject arbitrary commands into the CI runner, potentially stealing secrets and source code. The fix moves these values into intermediate environment variables, preventing command injection.

high

How GitHub Actions Shell Injection happens in YAML workflows and how to fix it

A GitHub Actions workflow in `reusable-workflow-input-must-declare-type.yaml` was directly interpolating `${{ inputs.constraints }}` inside a `run:` shell step, creating a shell injection vulnerability. An attacker who controls the workflow input could inject arbitrary shell commands into the runner, potentially stealing secrets and source code. The fix moves the untrusted value into an intermediate environment variable, breaking the injection path entirely.

critical

How Credential Leakage in GitHub Actions Happens in Node.js and How to Fix It

A GitHub Actions workflow in Node.js was storing authentication tokens in plain variables without masking them in logs, creating a critical security risk. When debug mode was enabled or errors occurred, tokens could be exposed in console output and GitHub Actions logs. The fix uses the `setSecret()` API to automatically mask sensitive credentials throughout the execution.

high

How Dependabot Missing Cooldown Periods Happen in GitHub Actions and How to Fix It

A missing cooldown period in Dependabot configuration creates a supply chain vulnerability by allowing automatic updates to newly published packages that could be malicious or unstable. This fix adds a 7-day cooldown to the `.github/dependabot.yml` file, ensuring newly published package versions are vetted before being proposed for update. This is critical for Node.js libraries where vulnerabilities affect all downstream consumers.

high

How missing cooldown periods in Dependabot configuration happen in GitHub Actions and how to fix it

A high-severity vulnerability was discovered in a Node.js library's `.github/dependabot.yml` configuration file where no cooldown period was set for package updates. This exposed the project to potentially malicious or unstable newly-published packages, as Dependabot would immediately propose updates without any waiting period. The fix adds a 7-day cooldown to the npm package-ecosystem configuration, ensuring a safety window before adopting new package versions.

medium

How Uninitialized Memory Vulnerabilities Happen in Rust and How to Fix Them

The fuser crate (versions prior to 0.16.0) contained a critical vulnerability that allowed uninitialized memory to be read and leaked through FUSE operations. This security issue was fixed by upgrading fuser from 0.15.1 to 0.16.0, which tightens memory handling and prevents potential information disclosure in applications that interact with the filesystem via FUSE.