Researchers demonstrate a cache poisoning attack on GitHub Actions

A team at Ruhr University Bochum showed that cache keys in GitHub Actions can be poisoned from a fork's pull request workflow, injecting artifacts into a main branch build.

Younes Bekrar11 min read
ShareXLinkedInFacebook
Researchers demonstrate a cache poisoning attack on GitHub Actions

A team at Ruhr University Bochum published a paper on Friday describing a practical cache poisoning attack against GitHub Actions, and demonstrated it against 41 open source repositories with the maintainers' permission. The attack abuses the scoping rules for the actions cache: a workflow running on a pull request from a fork writes to a cache scoped to the base branch under specific and common configurations, and a later workflow on that base branch restores the poisoned entry. Since caches routinely contain compiled dependencies and build tool binaries, the result is arbitrary code execution in a privileged build. GitHub has shipped mitigations and rated the underlying behavior as working as designed.

How the cache scoping works

GitHub Actions caches are scoped by branch with a fallback chain. A workflow on a feature branch can read caches from its own branch and from the default branch, which is what makes caching useful, since a new branch would otherwise start cold. Writes go to the current branch's scope. For pull requests from forks, the workflow runs in the context of a merge reference, and the researchers found that under certain trigger configurations that reference is treated as belonging to the base repository for cache write purposes.

The dangerous configuration is pull_request_target, a trigger designed to give fork pull requests access to secrets for use cases like labeling and commenting. It runs with base repository permissions, and if the workflow checks out the pull request head, which many do, it executes attacker-controlled code with those permissions. That risk has been documented since 2020. What the researchers added is that even workflows that do not check out untrusted code can poison the cache through a dependency installation step, because the attacker controls the manifest that determines what gets installed and cached.

What they proved

The team scanned 210,000 public repositories with workflows and found 8,400 with a configuration they assessed as exploitable. They contacted maintainers of a sample and received permission from 41 to demonstrate. In all 41 cases they successfully placed a marker file into a cache entry that was subsequently restored during a build on the default branch. In 9 cases the cache contained compiled artifacts that were linked into a release, meaning the marker would have reached published software.

None of the affected repositories are named individually in the paper, which lists categories instead: three are in the top 1,000 npm packages by download, two are widely deployed Go tools, and one is a Linux distribution packaging repository. The researchers followed a 90 day disclosure timeline with GitHub and coordinated fixes with each maintainer. All 41 have deployed changes.

The story is rarely the launch. It is what breaks, what ships, and who owns the mess at 2 a.m.
Younes Bekrar

GitHub's response and why it is contested

GitHub shipped three changes. Cache entries now record the workflow and reference that created them, and restore operations log that provenance. Cross-scope restore from a fork-created entry now requires an explicit opt-in. And the actions/cache action version 5 refuses to restore an entry created by a workflow with a different permission level than the current one, which is the change that actually stops the attack in the common case.

The company declined to treat the original behavior as a vulnerability, arguing that pull_request_target is documented as dangerous and that the cache scoping followed its specification. Security researchers pushed back hard on that framing, noting that a feature whose documented safe usage almost nobody follows is a design problem rather than a user error. The counterargument, which GitHub makes, is that removing the capability would break legitimate automation for a large number of projects.

What maintainers should check

The immediate audit is any workflow using pull_request_target, and for each one, whether it checks out or executes anything from the pull request head. If it does, that workflow gives fork contributors your repository's secrets and has done so all along. The safe pattern is to split: a pull_request workflow with no secrets that builds and tests untrusted code, and a separate privileged workflow triggered by workflow_run that consumes only artifacts, never source.

Beyond that, upgrade actions/cache to version 5, and consider whether caching is worth the risk for security-sensitive builds at all. Release builds in particular should probably start cold. The time saved by caching a dependency download is small against the cost of an artifact you cannot fully account for. Several large projects, including two Linux distributions, have already announced they are disabling caching on release workflows entirely.

The broader CI security picture

Continuous integration systems have become the highest-value target in software supply chains, because they hold credentials for every downstream system and they execute code from anyone who can open a pull request. The 2024 xz incident, the 2023 CircleCI breach, and a steady drip of token leaks all point the same direction. Defenses have improved: OIDC-based short-lived credentials, provenance attestation, and better permission defaults are all real progress.

The structural problem is that CI convenience and CI security are directly opposed. Every feature that makes a build faster, caching, shared runners, reusable workflows, artifact passing, creates a channel between contexts with different trust levels. The Bochum paper is a useful reminder that these channels need the same scrutiny as network boundaries, and that most organizations have never mapped them.


Skarvonix will keep following this beat with reporting grounded in how systems behave outside the launch keynote.

  • Open Source
  • Zero Trust

Keep reading