Add cache-write input for read-only cache mode#710
Add cache-write input for read-only cache mode#710salmanmkc wants to merge 1 commit intoactions:mainfrom
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 cache-write input to support a “read-only cache” mode, preventing untrusted workflows (e.g., PRs) from saving back to the NuGet cache while still allowing cache restore.
Changes:
- Introduces
cache-writeinput inaction.yml(defaulttruefor backward compatibility). - Skips the post-step cache save when
cache-writeis disabled. - Rebuilds generated
dist/output to include the new behavior.
Reviewed changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
action.yml |
Adds the cache-write input to configure whether the post-step saves the cache. |
src/cache-save.ts |
Adds an early return to skip cache saving when cache-write is disabled. |
dist/cache-save/index.js |
Compiled output reflecting the cache-write gating logic. |
💡 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 defined as a boolean input (action.yml default: true), but the code reads it with core.getInput() and compares to the exact string 'false'. This is brittle (e.g., False/FALSE/whitespace won’t be treated as false) and could unintentionally enable cache writes. Prefer core.getBooleanInput('cache-write') and negate it for the early-return check.
| const cacheWriteEnabled = core.getInput('cache-write'); | |
| if (cacheWriteEnabled === 'false') { | |
| const cacheWriteEnabled = 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; | ||
| } | ||
|
|
||
| if (core.getBooleanInput('cache')) { | ||
| await cachePackages(); |
There was a problem hiding this comment.
New behavior (skipping the post-step cache save when cache-write is false) isn’t covered by tests. There are existing unit tests for cache-save; please add a case that sets cache: true and cache-write: false and asserts no save attempt is made (e.g., no getState/saveCache calls).
| const cacheWriteEnabled = core.getInput('cache-write'); | ||
| if (cacheWriteEnabled === 'false') { | ||
| core.info('Cache write is disabled (read-only mode). Skipping cache save.'); | ||
| return; | ||
| } | ||
|
|
||
| if (core.getBooleanInput('cache')) { | ||
| await cachePackages(); | ||
| } |
There was a problem hiding this comment.
The cache-write early-return runs before checking whether cache is enabled. If a workflow sets cache-write: false but cache: false, this will still emit a “Skipping cache save” message even though no save would happen anyway. Consider checking cache first (or only logging when caching is enabled) to avoid confusing logs.
| const cacheWriteEnabled = core.getInput('cache-write'); | |
| if (cacheWriteEnabled === 'false') { | |
| core.info('Cache write is disabled (read-only mode). Skipping cache save.'); | |
| return; | |
| } | |
| if (core.getBooleanInput('cache')) { | |
| await cachePackages(); | |
| } | |
| const cacheEnabled = core.getBooleanInput('cache'); | |
| if (!cacheEnabled) { | |
| return; | |
| } | |
| const cacheWriteEnabled = core.getInput('cache-write'); | |
| if (cacheWriteEnabled === 'false') { | |
| core.info('Cache write is disabled (read-only mode). Skipping cache save.'); | |
| return; | |
| } | |
| await cachePackages(); |
|
Copilot left some potentially good comments. (Some required checks are also failing, unclear if it's due to this PR or just flaky CI.) This seems like something where it would be good to make it a required setting in an upcoming major version change due to the security concern. But taking the change with the same behavior as before means it can bake for a while and give people a chance to test it out first. |
When
cache: trueis set, this action restores and saves the NuGet global-packages cache. For PR workflows there's no way to do restore-only — a malicious PR can write to the cache and have it affect later trusted runs.This adds a
cache-writeinput (defaults totrue, backward compatible). Whenfalse, the post-step save is skipped.Usage:
What changed:
action.yml— newcache-writeinputsrc/cache-save.ts— early return whencache-writeisfalsedist/— rebuiltSame change going into setup-node, setup-python, setup-go, setup-java.