How to Configure GitHub Actions Cache-Mode: 2026 Enterprise Mac CI Poisoning Guide

How to Configure GitHub Actions Cache-Mode: 2026 Enterprise Mac CI Poisoning Guide

Untrusted pull requests can run on a Mac Runner, while later builds restore or save shared cache entries.

Fastest fix: give untrusted jobs read access only when they need cache restores; use none otherwise. Let a separate trusted workflow write caches, and isolate and clean the Mac Runner independently.

For enterprise IT and platform engineering leads, this runbook maps cache permissions to workflow trust and approval boundaries.
For iOS and macOS CI owners, it shows where cached files can carry risk into real Mac builds.
For security leads, it provides checks for untrusted code, persistent Runner state, and signing credentials.

Last updated September 24, 2026; verified against GitHub’s cache-mode announcement, workflow syntax, cache security, event, and self-hosted Runner documentation.

GitHub Actions cache-mode enterprise configuration: control cache access, not execution

Use cache mode to decide whether a workflow may restore cached data, save it, do both, or do neither. It is an authorization control for the Actions cache. It does not sandbox a job, stop untrusted code from running, or clean a Mac after the job finishes. GitHub describes the new control in its cache-mode announcement; check the current workflow syntax reference before setting it in production.

Mode Restore an existing cache? Save a cache? Enterprise use
read Yes No Use for untrusted jobs that need acceleration but must not write entries.
write Yes Yes Reserve for trusted workflows that need both restore and save access.
write-only No Yes Use when a trusted job should publish a cache without consuming an existing one.
none No No Use when the job does not need cache access or the input is too risky.

These are four distinct permission choices, not four levels of protection for the host. Confirm each mode’s supported syntax and exact behavior in GitHub’s current announcement and workflow syntax. Don’t infer access from a job’s name or branch label: verify the effective workflow configuration and logs.

Decision rule: if the workflow executes code you do not trust, do not grant cache writes. If it does not require restores, disable cache access. If trusted work needs to create cache entries, keep that permission in a workflow with a controlled input path.

Trigger trust changes who should restore or save

A trigger tells you how a workflow started; it does not prove that all code or artifacts it handles are safe. Assess the event, the checked-out revision, the actions it invokes, and any data passed between workflows. GitHub’s event reference and dependency caching guide explain the event and cache boundaries to verify.

Workflow input or trigger Cache policy to start with Risk to check
Fork pull request executing contributor code read only if a restore is needed; otherwise none The job executes code you do not control. Avoid giving it a path to write trusted cache entries.
Internal pull request with unreviewed changes Treat as untrusted until reviewed; use read or none Repository membership alone does not establish that a change is safe to execute with write access.
pull_request_target Do not check out or run untrusted pull-request code in its privileged context The event can have access to the base repository’s privileges. Follow GitHub’s secure-use guidance for pull_request_target.
workflow_run receiving output from another workflow Treat upstream artifacts and data as untrusted until validated A privileged downstream workflow must not blindly execute or cache content produced by an untrusted run.
Protected, reviewed push or controlled release workflow write only when cache publication is required Confirm the revision, approval path, permissions, and secrets available to the job.

A fork pull request may be able to use eligible base-branch cache entries under GitHub’s cache scope rules, even when it must not write to the trusted branch’s cache. Check the current dependency-caching security reference for the exact access boundary that applies to your repository and workflow. The operational distinction matters: read access can still introduce cached files into a build, so it is not equivalent to “no untrusted input.”

For pull_request_target, use the trigger for trusted metadata actions only when the workflow is designed for that privilege. Do not fetch and execute a contributor’s code in that context. If a trusted cache must be updated after a pull request is accepted, create a separate trusted run from the reviewed revision. Pass only validated information between workflows, and do not use a privileged workflow as a shortcut for letting an untrusted job save cache entries.

Cache scope and contents determine the blast radius

Permissions answer whether a workflow can access the cache. Keys, restore behavior, and cached files determine what it can encounter. GitHub’s dependency-caching security guidance warns that caches can be accessible across eligible workflow contexts and that sensitive information should not be stored there. Treat a restored cache as input to your build, not as a trusted package source.

Start with cache contents. Caching dependency downloads can be useful, but a cache may also contain generated files, scripts, compiler outputs, or other executable material. If a build later runs a restored file, assess it as you would assess other code entering that job. Avoid caching signing keys, tokens, credentials, provisioning material, or files that expose secrets. A cache key is not an encryption boundary and should not be used to conceal sensitive content.

Next, inspect the key and restore keys. A broad restore key can intentionally match several cache entries; that convenience can also widen which content a job may restore. Keep keys specific to the operating system, toolchain, dependency lock state, and build purpose where those distinctions matter to your design. Review branch scope and default-branch access in the official dependency caching documentation, rather than assuming that a branch name alone creates isolation.

Operational warning: a job can be read-only with respect to cache writes and still restore content that influences a build. If the restored content is executable, validate its source and handling before using it in a trusted signing or release path.

A safer pattern is to separate cache consumers from cache publishers. Let lower-trust jobs restore only the entries they need, if any. Let a trusted workflow create or refresh cache entries from reviewed source and controlled dependencies. Avoid passing unverified artifacts from a pull request into that writer. If the cache is not necessary for a sensitive job, disabling it gives you a simpler boundary to verify.

Persistent Mac Runner risk sits outside cache-mode

A remote cache is not the same thing as the Mac’s local state. A self-hosted Mac Runner can retain files in workspaces, user directories, temporary locations, tool configuration, or process state beyond one job. It may also have credentials or access to signing resources. Cache permissions do not remove any of that state. GitHub’s secure use guidance for self-hosted Runners advises treating untrusted workflow code as a risk to the Runner environment.

Use independent trust zones for jobs with different consequences. An untrusted pull request should not share a persistent host or credential context with a production signing job merely because the cache is read-only. Where you cannot provide a clean environment for an untrusted job, move it to a separate Runner group or another disposable environment. For trusted signing work, restrict who can trigger it, which source revisions it checks out, and which credentials it can access.

The trade-off is operational as well as technical. A persistent Mac can preserve useful tool state, but residue can survive the build. A clean or rebuilt environment reduces that carry-over, but requires you to manage setup and job handoff deliberately. Choose based on the code each Runner executes and the credentials it can reach—not on whether cache writes are enabled.

If you are evaluating the underlying Mac environment, compare isolation boundaries rather than assuming that hardware form determines trust. MacDate’s guide to bare metal versus macOS virtualization can help frame that infrastructure decision. It does not replace a workflow-level review of permissions, cleanup, and credential access.

Validate the effective policy before rollout

Do not accept a YAML review as proof that the intended control is active. Confirm what the event ran, what permissions the job received, and whether the cache restore or save actually happened. The following checklist gives platform, security, and CI owners a shared set of evidence to collect.

  • [ ] Record the trigger, source revision, and trust classification for each workflow that uses a cache.
  • [ ] Set an explicit cache mode for each relevant workflow or job; do not rely on an assumed default. Check the supported syntax in GitHub’s workflow reference.
  • [ ] For untrusted code, choose read only when a restore is needed; otherwise choose none. Confirm the workflow cannot save an entry.
  • [ ] Keep cache-writing permission in a separate trusted workflow where possible. Document how it receives source or artifacts and what validation occurs before saving.
  • [ ] Review cache keys, restore keys, branch scope, and cached paths. Remove secrets and assess any cached executable files as untrusted input.
  • [ ] Inspect logs from both an untrusted run and a trusted run. Capture evidence that the first did not save a cache and that the second followed the approved write path.
  • [ ] Check job permissions, available secrets, Runner group assignment, workspace cleanup, and residual credentials separately from cache-mode.
  • [ ] Define the failure behavior. If cache access is unavailable or a cache entry fails validation, the build should fail safely or proceed without the cache—not switch to an unreviewed writer.
  • [ ] Obtain platform, security, and CI owner approval before expanding the policy to release or signing jobs.

Score each area as Pass, Needs evidence, or Fail. Pass only when logs and configuration support the claim; mark Needs evidence when a behavior is expected but not demonstrated; mark Fail when an untrusted path can write trusted cache state or reach a sensitive Runner context. This is a review rubric, not a GitHub-provided score.

For the pilot decision, require evidence for both cache behavior and host isolation. If cache permissions are correct but a persistent Mac retains untrusted files or credentials, hold rollout for jobs that share that host. If the Runner boundary is sound but cache behavior is unclear, disable cache access until you can verify the effective mode. Expand only after the workflow and host controls have both passed review.

FAQ: cache permissions and Mac CI boundaries

Can a pull request workflow use a cache?

It can use a cache only within the access GitHub permits for that workflow and repository context. For untrusted pull requests, prefer read when a restore is required and none otherwise. Do not grant write access to speed up builds. Review the trigger and cache scope in the official documentation, then verify that the run logs match your intended policy.

How should a trusted workflow update a cache after review?

Keep cache publication in a workflow that runs from a reviewed revision and receives only validated inputs. Give that workflow write permission only if it needs to save an entry. Do not blindly execute artifacts or outputs from an untrusted upstream run in a privileged context. Confirm both the workflow permissions and the resulting cache-save logs.

Is restoring a cache safe for an Xcode CI build?

A restore is not automatically safe. Cached dependencies and generated files can affect later build steps, especially if the workflow executes restored scripts or binaries. Keep sensitive signing material out of caches, narrow the key and restore behavior, and validate executable content before it reaches a release or signing job. Treat the cache as input whose provenance matters.

What evidence should an enterprise team keep for a self-hosted Mac Runner?

Keep the workflow definition and effective permissions, trigger and revision details, restore and save logs, Runner assignment, cleanup evidence, and a record of which credentials the job could access. Cache logs alone do not show whether local files or credentials remained on the Mac. Review host isolation and cache authorization as separate controls before approving production use.

Decide whether to keep the current Mac setup or add rented capacity

A team running every iOS build on its own persistent Mac has to fund the hardware, maintain the host and toolchain, and manage local-state cleanup. A hosted CI environment can reduce hardware operations, but it may not provide the same control over the Mac’s local environment or access path that your security review requires. Neither choice makes cache-mode a substitute for separating untrusted builds from signing credentials.

If you need temporary Mac capacity to test a controlled CI design before committing to hardware, review MacDate’s Mac compute node options and assess them against your Runner isolation, cleanup, and credential requirements. Treat the cache policy and the Mac environment as separate decisions; rent only when the access model and evidence meet your team’s security bar.