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, 2026•Reviewed 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.

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.

Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #6197

Related Articles

high

Unbounded Map in createLoginRateLimiter Exhausts API Memory

The console API's `createLoginRateLimiter` and `createMutationRateLimiter` stored one `Map` entry per client key with no upper bound and no expiry sweep, so an attacker rotating source addresses or identifiers could grow those maps until the Node process hit an out-of-memory crash. The fix introduces a `maxTrackedKeys` option (default 5000), a `trackedEntryLimit` sanitizer, and an `evictOldestIfFull` helper that drops the oldest tracked key while protecting the shared global counter. The rate li

high

js-yaml 4.3.1 Denial of Service: Malformed Input Hangs YAML Parser

A denial of service vulnerability in js-yaml versions 4.3.1 and earlier allows attackers to hang the YAML parser indefinitely by providing specially crafted malformed input. The fix, released in versions 4.3.2 and 3.15.2, patches the parsing logic to prevent unbounded processing. Upgrading is recommended for all applications parsing untrusted YAML data.

high

Express `app.get('*')` Wildcard Handler Path Traversal in watch.js

A first-party Express server's wildcard route handler used `req.url.indexOf('font.woff2')` to gate access to a font file, allowing attackers to bypass the substring check with crafted paths. The fix replaces the catch-all handler with explicit route registration.

critical

Updater.parseUpdate() CWE-494: Unsigned Metadata Download

The parseUpdate function in the Updater component extracted download URLs from remote server responses without cryptographic verification, enabling supply chain attacks via compromised or spoofed update servers. The fix adds strict URL validation requiring HTTPS and a trusted hostname before accepting any update metadata.

high

brace-expansion DoS: Exponential Backtracking in Nested Brace Patterns

A critical vulnerability in brace-expansion allows attackers to cause denial of service by submitting specially crafted patterns with nested braces. The exponential-time complexity in pattern expansion creates a computationally expensive path that can freeze applications processing user-controlled input.

high

CVE-2026-67213: nanoid customAlphabet Infinite Loop Fix

nanoid, a widely-used ID generator pulled in transitively through postcss and vitepress, had an infinite-loop bug in its `customAlphabet` code path before version 5.1.6. This PR pins the entire dependency tree to nanoid 5.1.16 via a pnpm override so no transitive consumer can resolve back to the vulnerable 3.3.16 release.