GitHub Actions cache-mode GA: stop CI cache poisoning
GitHub Actions cache-mode is now GA on all plans with four values and least-privilege defaults. Here is how to close the fork-PR cache poisoning path today.
GitHub Actions cache-mode shipped generally available on September 10, on every plan including free. It is four words of YAML that close one of the quieter holes in CI: a pull request from a fork writing a cache entry that your trusted workflow later restores and executes. If you run builds on public repos or accept outside contributions, this is a thirty-minute change with a real blast radius behind it.
What actually happened
Per GitHub's changelog, cache-mode applies least-privilege access to the Actions cache at the workflow or job level, and takes four values:
read— restores allowed, saves blocked. The new default for low-trust events such aspull_request_target.write— restores and saves allowed. The default for trusted events such aspush.write-only— saves allowed, restores blocked.none— no cache access at all.
Three details matter more than the list. Job-level settings override workflow-level, so you can lock a workflow down and open one job. The mode is enforced by the cache service rather than by the action, so a third-party cache action cannot route around it. And it carries through reusable workflows: a called workflow can never receive more cache access than its caller granted. If you explicitly set write or write-only on a low-trust event, GitHub emits a warning annotation, which is the platform telling you it knows exactly what you are doing.
Why CI cache poisoning matters for your business
The attack is unglamorous and that is why it works. Your repo accepts a PR from a fork. That workflow runs with limited permissions, but it saves a build cache. Later, a trusted workflow on push restores the cache by key and executes what is inside — a poisoned compiler artifact, a patched node_modules binary, a build script that ships one extra network call. Nothing in the diff shows it. The malicious content never appeared in the repository at all; it appeared in a tarball keyed by a hash.
For a small shop this is the same shape as the npm and GitHub Actions incidents that have been landing all year: your build environment trusts something your code review never looked at. The difference is that this one now has a switch.
Do three things this week. Set cache-mode: none as your workflow-level default and grant write only on the jobs that genuinely need to populate a cache. Audit every workflow triggered by pull_request_target — that trigger runs with repository secrets and is where this gets expensive. Then confirm your reusable workflows still build once the ceiling applies, because a called workflow that silently depended on a cache it can no longer restore will fail loudly the first time, and you want that failure on a Tuesday and not during a release.
Key takeaways
- cache-mode is GA on github.com for all plans as of September 10, 2026
- Four values: read, write, write-only, none - set at workflow or job level, job wins
- Low-trust events like pull_request_target now default to read; trusted events like push default to write
- Enforcement happens in the cache service, so third-party cache actions cannot bypass it
- A reusable workflow can never get more cache access than its caller granted
- Default the workflow to none, grant write per job, and audit every pull_request_target trigger
We harden the pipeline, not just the app. Least-privilege CI, pinned actions, scoped tokens, caches that cannot be written by anyone who opened a pull request. See how we build and secure delivery pipelines, or send us a workflow file and we will tell you what it trusts.
Sources: GitHub Changelog: Control GitHub Actions cache access with cache-mode.
- #github-actions
- #ci-security
- #supply-chain
- #cache-poisoning
- #devops
Tommy Rush — Founder, Rush Commerce
Operator turned builder. 15+ years running operations — now shipping the systems businesses run on. More
Get The Rush Report weekly — one email, zero fluff.
Keep reading
SGLang CVE-2026-86793: unauthenticated RCE on your GPU box
SGLang's SafeUnpickler can be bypassed through an unauthenticated endpoint for full RCE. No patch at disclosure. How to close it on a self-hosted inference server.
Read itAn AI agent swarm took 440 PaperCut servers
GreyNoise traced hundreds of AI agents compromising 440 PaperCut servers at 395 organizations in 48 countries. First RCE in under four hours. What it changes.
Read it