Safeguard
Security Guides•

Who can write to my GitHub Actions cache?

Pushes and scheduled runs can write the default branch cache; low-trust triggers are read-only by default. See the scope rules and the cache-mode setting.

Safeguard Security Team
4 min read

Trusted triggers such as push, schedule and workflow_dispatch can create or overwrite caches in the default branch scope. Per GitHub's documentation, runs from low-trust triggers like pull_request_target, issue_comment and workflow_run get read-only access to that scope by default, which blocks the classic cache poisoning route.

Why the cache is a security topic

A cache is a bundle of files that a later job unpacks and trusts. If an attacker can write a bad bundle under a key a privileged job will restore, that job runs attacker content with its own secrets. That is cache poisoning.

GitHub's docs describe the risk in these terms: events that outsiders can start, but that run in the default branch context, could write a malicious cache that a later, more privileged workflow restores.

The scope rules

GitHub isolates caches by branch and tag.

  • A run can restore caches from its own branch or the default branch.
  • A pull request run can also restore from the base branch.
  • Runs cannot restore caches from child or sibling branches, or from other tag names.
  • A cache created by a pull request run is stored under the merge ref, so only re-runs of that pull request can restore it. The base branch and other pull requests cannot.

So an ordinary pull request cannot plant a cache that your main branch build will pick up. The remaining risk is workflows that run in the default branch scope but are influenced by outsiders.

Who writes by default

TriggerDefault cache access in the default branch scope
push, schedule, workflow_dispatch, repository_dispatchRead and write
pull_requestNot affected, scoped to the merge ref
pull_request_target, issue_comment, workflow_runRead only

When a read-only run tries to save a cache, the save fails with a warning but the job continues. GitHub suggests keeping the cache warm from a trusted workflow, such as a build triggered by a push to the default branch, and using the restore-only action in low-trust workflows.

Tighten it further with cache-mode

GitHub's docs describe a cache-mode key you can set at workflow or job level. Its values are read, write, write-only and none. Give each job the least it needs. A lint job that only consumes a cache can be read. A job that never needs one can be none.

The docs also include a warning. Explicitly setting write or write-only on a workflow with a low-trust trigger brings back the poisoning risk the default exists to prevent. Prefer a narrower fix, such as keeping the job on read and letting a trusted push workflow maintain the cache.

Practical steps for a small team

  1. List your workflows that use caching and note their triggers.
  2. Mark any that run on pull_request_target, issue_comment or workflow_run. Confirm none declares a write capable cache-mode.
  3. Make deploy and release jobs restore as little as possible. A cold install is slower but removes a trust dependency.
  4. Key caches on your lockfile hash so a changed dependency set cannot reuse stale content.
  5. Know the housekeeping rules: unaccessed entries are removed after 7 days, and the default repository limit is 10 GB, with eviction by last access when it is reached.

Where Safeguard fits

Cache scope is a platform setting. Safeguard's SBOM and dependency scanning cover what ends up installed from lockfiles, which helps you notice when a dependency set changes. See also our post on cache poisoning attacks for background.

Quick check

Can a pull request from a fork poison my main branch cache?

Not through a plain pull_request run, because its cache is scoped to the merge ref. The risk is privileged triggers that check out untrusted code.

Should release builds use caches at all?

It is a judgement call. A clean install removes one trust dependency from the most sensitive job, at the cost of build time.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.