Uh oh!
There was an error while loading. Please reload this page.
Add cache-write input for read-only cache mode - #1284
Conversation
Add a 'cache-write' input (default: true) that controls whether the cache is saved at the end of the workflow. When set to 'false', the action will restore cached dependencies but skip saving, providing a read-only cache mode. This is useful for preventing cache poisoning attacks from untrusted PR builds while still benefiting from cached dependencies.
There was a problem hiding this comment.
Pull request overview
Adds a new action input to support “read-only” cache usage by allowing workflows to restore caches without writing updated caches back, helping mitigate cache poisoning risks for untrusted PRs.
Changes:
- Introduces a new
cache-writeinput (defaulting to enabled) inaction.yml. - Skips the post-step cache save when
cache-writeis set tofalse. - Rebuilds the compiled
dist/output to include the new behavior.
Reviewed changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| action.yml | Adds the new cache-write input and documents its purpose. |
| src/cache-save.ts | Implements early return to skip saving cache when cache-write is disabled. |
| dist/cache-save/index.js | Compiled output reflecting the new cache-write behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| const cacheWriteEnabled = core.getInput('cache-write'); | ||
| if (cacheWriteEnabled === 'false') { |
There was a problem hiding this comment.
cache-write is treated as a raw string and only disables saving when the value is exactly 'false'. This is inconsistent with other boolean inputs in this repo (e.g., check-latest uses core.getBooleanInput) and will silently save the cache for values like False/FALSE. Prefer core.getBooleanInput('cache-write') (or normalize/lowercase) and branch on the boolean result.
| constcacheWriteEnabled=core.getInput('cache-write'); | |
| if(cacheWriteEnabled==='false'){ | |
| constcacheWriteInput=core.getInput('cache-write'); | |
| constcacheWriteEnabled= | |
| cacheWriteInput==='' | |
| ? true | |
| : core.getBooleanInput('cache-write'); | |
| if(!cacheWriteEnabled){ |
| const cacheWriteEnabled = core.getInput('cache-write'); | ||
| if (cacheWriteEnabled === 'false') { | ||
| core.info('Cache write is disabled (read-only mode). Skipping cache save.'); | ||
| return; | ||
| } | ||
| const cache = core.getInput('cache'); | ||
| if (cache) { | ||
| await saveCache(cache); |
There was a problem hiding this comment.
New cache-write behavior is not covered by tests. Add a unit test in __tests__/cache-save.test.ts that sets cache-write to false and asserts cache.saveCache is not called (and optionally that the skip message is logged) when cache is enabled.
brunoborges
commented
Jul 16, 2026
After reviewing the current GitHub Actions cache security model, I recommend closing this PR as unnecessary for the stated cache-poisoning threat.
The suggested Explicit restore-only behavior may still be useful ergonomically, but GitHub already recommends composing References:
|
Right now if you use
cache: pip(or pipenv/poetry) in a PR workflow, the action restores and saves the cache. There's no way to get read-only mode where you benefit from existing caches without writing back. This matters for cache poisoning — an untrusted PR could plant bad packages in the cache that later get picked up by pushes to main.This adds a
cache-writeinput (defaults totrue, no breaking change). Set it tofalseto skip the post-step save.Usage:
What changed:
action.yml— newcache-writeinputsrc/cache-save.ts— early return whencache-writeisfalsedist/— rebuiltSame change going into setup-node, setup-go, setup-java, setup-dotnet.