Back to Blog
high SEVERITY6 min read

How mutable action tag vulnerabilities happen in GitHub Actions and how to fix it

The `bench-probe.yml` workflow referenced a GitHub Action by a mutable tag or branch name instead of a full commit SHA, leaving the pipeline exposed to supply-chain attacks if the action owner's tag were ever repointed. The fix pins the action to an immutable 40-character commit SHA, closing the same class of gap exploited in the real-world trivy-action and kics-github-action compromises.

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

Answer Summary

This is a GitHub Actions mutable action tag vulnerability (CWE-829) in `.github/workflows/bench-probe.yml`, where a workflow step referenced an action by a tag or branch name (e.g. `@v4` or `@main`) rather than a pinned commit SHA. Because tags and branches can be silently repointed by the action's maintainer or a compromised maintainer account, this creates a supply-chain attack vector. The fix is to pin the `uses:` reference to a full 40-character commit SHA, such as `actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608`.

Vulnerability at a Glance

cweCWE-829 (Inclusion of Functionality from Untrusted Control Sphere)
fixPin the `uses:` reference to a full 40-character immutable commit SHA
riskSupply-chain compromise via a repointed tag/branch executing malicious code in CI
languageYAML (GitHub Actions workflow)
root causeWorkflow step in `bench-probe.yml` used a mutable version tag/branch instead of a commit SHA for a third-party action
vulnerabilityGitHub Actions Mutable Action Tag Reference

Introduction

The .github/workflows/bench-probe.yml file drives the automated benchmarking pipeline for this repository — the same pipeline responsible for building and running performance probes like bench/wordcount/wordcount.c. Like most CI configuration, it invokes third-party GitHub Actions using the standard uses: syntax. The problem: at least one step referenced its action by a mutable tag or branch name rather than an immutable commit SHA — a pattern flagged as high severity by Semgrep's github-actions-mutable-action-tag rule.

This might look harmless in a review — after all, uses: some-action@v4 is everywhere in GitHub Actions examples online. But that convenience is exactly what makes it dangerous. Tags and branches are just pointers; the action's maintainer (or anyone who compromises their account) can silently move that pointer to a new, malicious commit at any time. The next time your CI runs, it pulls and executes that new code — no code review, no PR, no warning.

The Vulnerability Explained

Semgrep's github-actions-mutable-action-tag rule looks for uses: steps in workflow YAML that resolve to anything other than a full 40-character commit SHA. A typical vulnerable step looks like this:

steps:
  - name: Run benchmark probe
    uses: some-org/some-action@v4

Here, @v4 is a tag, not a commit. Tags in Git are mutable by design — the repository owner can git tag -f v4 <new-commit> and push, and every workflow referencing @v4 will pick up the new code on its next run. The same applies to branch references like @main or @master.

This is not a theoretical risk. It's the exact mechanism behind two real supply-chain incidents:

  • trivy-action compromise: a widely used security-scanning action was repointed, causing downstream CI pipelines to execute unexpected code.
  • kics-github-action compromise: a similar tag-repointing attack affected another popular scanning action used across thousands of repositories.

Attack scenario for bench-probe.yml

Imagine the mutable-tagged action in bench-probe.yml is used to check out code, set up a build environment, or upload benchmark artifacts. An attacker who compromises the action maintainer's account (via phished credentials, a leaked publish token, or a malicious contributor merge) could push a new commit to the tag that:

  1. Exfiltrates repository secrets (GITHUB_TOKEN, API keys, cloud credentials) available to the workflow.
  2. Injects a backdoor into the benchmark build artifacts.
  3. Modifies the checked-out source before the wordcount.c benchmark (or any other bench target) is compiled and run.

Because CI runners often have elevated permissions — access to secrets, push rights, or deployment credentials — a single repointed tag can turn a benchmarking workflow into a foothold for a much larger compromise.

The Fix

The fix is conceptually simple but security-critical: replace the mutable tag/branch reference with the full 40-character commit SHA that tag currently points to.

Before (vulnerable pattern):

- uses: some-org/some-action@v4

After (hardened):

- uses: some-org/some-action@8ade135a41bc03ea155e62e844d188df1ea18608  # v4

Note the trailing comment — it's good practice to annotate the SHA with the human-readable version it corresponds to, so future maintainers know what they're looking at without losing the immutability guarantee.

Why does this work? A commit SHA is a cryptographic hash of the commit's content and history. It cannot be silently repointed the way a tag can — if the underlying code changes, the SHA changes with it. Pinning to a SHA means the exact bytes of code that run in your CI today are the exact bytes that will run tomorrow, until you explicitly choose to bump the pin (ideally via a reviewed pull request, which is how Dependabot and Renovate handle SHA updates safely).

This change requires no behavioral modification to the workflow — the action still does exactly what it did before. It simply removes the attacker's ability to change that behavior out from under you.

Prevention & Best Practices

  • Pin every third-party action to a commit SHA, not a tag or branch. This applies to actions/checkout, actions/setup-node, and any community or third-party action.
  • Use Dependabot or Renovate with GitHub Actions support enabled — both tools understand SHA pinning and will open PRs to bump pins safely, including the version comment.
  • Enable branch protection and required reviews on workflow file changes so a modified uses: line doesn't slip through unnoticed.
  • Run actionlint or Semgrep's github-actions-mutable-action-tag rule in CI itself, so any newly added mutable reference is caught before merge.
  • Audit existing workflows periodically — this class of risk accumulates silently as new steps are added by different contributors over time.
  • Treat CI as production infrastructure. Workflow files have access to secrets and can execute arbitrary code; they deserve the same scrutiny as application source.

This maps directly to CWE-829: Inclusion of Functionality from Untrusted Control Sphere, and is closely related to general software supply-chain integrity guidance from OWASP's Software Component Verification Standard and the SLSA framework.

Key Takeaways

  • bench-probe.yml referenced an action by a mutable tag/branch instead of a commit SHA — an easy-to-miss but high-severity gap.
  • Mutable tags gave an attacker who compromised the action's repository a path to silently alter CI behavior, mirroring the real trivy-action and kics-github-action incidents.
  • The fix — pinning to a full 40-character SHA (e.g., actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608) — is a drop-in change with no functional impact.
  • CI/CD configuration is part of your attack surface; it should be linted and reviewed with the same rigor as application code.
  • Automated tooling (Dependabot/Renovate + Semgrep) can keep SHA pins current without reintroducing the mutable-reference risk.

How Orbis AppSec Detected This

  • Source: The uses: directive in .github/workflows/bench-probe.yml, resolved at workflow-run time from GitHub's action registry.
  • Sink: The GitHub Actions runner, which fetches and executes whatever commit the tag/branch currently points to.
  • Missing control: No pinning to an immutable commit SHA — the workflow trusted a mutable reference to determine what code executes in CI.
  • CWE: CWE-829 (Inclusion of Functionality from Untrusted Control Sphere).
  • Fix: The action reference in bench-probe.yml was pinned to its full 40-character commit SHA, eliminating the ability for the tag/branch to be silently repointed.

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

A single unpinned uses: line in bench-probe.yml was enough to expose the benchmarking pipeline to a supply-chain attack pattern that has already been used against real projects. Pinning GitHub Actions to full commit SHAs is a small, low-friction change that removes an entire class of risk — one that's easy to overlook because tag-based references are the default example in almost every piece of GitHub Actions documentation. Treat every uses: line in your workflows as a dependency that deserves the same scrutiny as a package in your package.json or requirements.txt.

References

Frequently Asked Questions

What is a GitHub Actions mutable action tag vulnerability?

It's a CI/CD supply-chain weakness where a workflow references a third-party action by a mutable tag (like `@v4`) or branch (like `@main`) instead of an immutable commit SHA, allowing the referenced code to change without warning.

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

Always pin `uses:` references to a full 40-character commit SHA, and use tools like Dependabot or Renovate with SHA-pinning support to keep them updated safely.

What CWE is associated with mutable action tag references?

CWE-829 (Inclusion of Functionality from Untrusted Control Sphere), since the workflow trusts code from a reference that can change outside the repository's control.

Is pinning to a major version tag like `@v4` enough to prevent this vulnerability?

No — major and minor version tags, and branches, can be repointed by the action owner or a compromised account at any time; only a commit SHA is immutable.

Can static analysis detect mutable action tag references?

Yes, tools like Semgrep, actionlint, and GitHub's own dependency review can flag `uses:` statements that don't pin to a full commit SHA.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #109

Related Articles

critical

How a vulnerable websocket-driver dependency happens in Node.js lockfiles and how to fix it

A Trivy scan flagged `websocket-driver@0.7.4` in this repository's `bun.lock` as affected by CVE-2026-54466, a critical issue in a WebSocket protocol handler that parses untrusted HTTP upgrade requests and frame data. The fix upgrades the package to `0.7.5` and adds an explicit `websocket-driver` entry to the lockfile's override block so every transitive consumer — webpack-dev-server, sockjs, faye-websocket — resolves to the patched build instead of the pinned vulnerable one.

high

How Dependabot Missing Cooldown Periods Enable Supply Chain Attacks and How to Fix It

A critical security vulnerability in `.github/dependabot.yml` was exposing a Node.js library to supply chain attacks by automatically updating to newly published packages without a safety delay. By adding a 7-day cooldown period to each package ecosystem configuration, the project now protects against malicious or unstable package versions that could affect downstream consumers.

high

How Exponential-Time Complexity Causes Denial of Service in brace-expansion and How to Fix It

A critical vulnerability in brace-expansion versions 1.1.13 and earlier allowed attackers to cause denial of service through crafted brace pattern inputs. The fix upgrades to patched versions 1.1.16, 2.1.2, and 5.0.7, eliminating the exponential-time complexity that made exploitation possible.

high

How unrestricted file upload via extension-only validation happens in Deno/JavaScript and how to fix it

The review image upload handler in this Deno-based app trusted the client-supplied filename extension to decide whether a file was a "safe" image, without ever inspecting the actual file bytes. The fix adds magic-byte signature verification for PNG, JPEG, GIF, and WEBP formats before the file is written to disk, closing the door on disguised executables and malicious payloads.

high

How Missing Dependabot Cooldown Periods Enable Supply Chain Attacks in CI/CD Pipelines and How to Fix Them

We fixed a high-severity supply chain security gap in `.github/dependabot.yml` where missing cooldown periods allowed immediate adoption of newly published packages. The fix adds `cooldown: default-days: 7` to all package ecosystems, creating a critical security buffer against typosquatting and malicious dependency attacks.

high

How Dependabot Missing Cooldown Vulnerability Happens in GitHub Actions and How to Fix It

Dependabot configurations without cooldown periods can automatically propose updates from newly published packages within hours—potentially including malicious or unstable versions. This vulnerability in `.github/dependabot.yml` was fixed by adding a `cooldown` block with `default-days: 7` to delay updates and allow time for community vetting.