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.

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

critical

deleteNestedProperty Prototype Pollution via Dot-Notation Path

The `deleteNestedProperty` function in propertyUtils.ts allowed attackers to manipulate JavaScript object prototypes by passing specially crafted dot-notation paths like `__proto__.polluted`. A fix now blocks dangerous keys before processing, preventing prototype pollution attacks that could affect all objects in the application.

high

How Denial of Service via Infinite Loop Happens in JavaScript Dependencies and How to Fix It

CVE-2026-67213 is a high-severity denial of service vulnerability in nanoid before version 5.1.6 that triggers an infinite loop during random ID generation when processing specially crafted input. We upgraded nanoid across the entire dependency tree to patch this flaw and prevent attackers from freezing application threads. This fix ensures that ID generation remains resilient even when handling adversarial input patterns.

high

How Sensitive Data Exposure happens in Zotero plugins and how to fix it

A high-severity data exposure vulnerability in `Zotero.ts` automatically transmitted complete document metadata—including private notes, attachment paths, and tags—to external LLM services without user consent. The fix replaces broad `item.toJSON()` serialization with explicit field selection, sending only essential bibliographic data.

high

How missing dependency update cooldowns happen in GitHub Dependabot configurations and how to fix it

A semgrep scan flagged `.github/dependabot.yml` for lacking a cooldown period, meaning Dependabot would immediately propose updates to brand-new package versions across npm, Bundler, and Docker ecosystems. The fix adds a `cooldown: default-days: 7` block to every `package-ecosystem` entry, forcing a one-week waiting period before newly published releases are considered — reducing exposure to malicious or unstable package drops.

high

How Path Traversal Happens in TensorFlow's Data Service and How to Fix It

TensorFlow's data service dispatcher validated dataset IDs against forward-slash traversal attacks but overlooked backslash characters on non-Windows platforms, allowing attackers to escape the root directory. A targeted fix adds explicit backslash validation across all platforms, closing a high-severity path traversal vulnerability in the snapshot management system.

critical

How Unbounded WebSocket Message Handling Causes Resource Exhaustion in Node.js and How to Fix It

The WebSocketCrossServerAdapter class in a popular Node.js WebSocket library lacked any rate limiting on inbound messages, allowing attackers to flood Redis nodes and WebSocket servers with high-volume traffic. The fix introduces a configurable `rateLimit` option that caps messages per connection per second, preventing resource exhaustion while preserving legitimate functionality.