Skip to content

Add cache-write input for read-only cache mode#710

Open
salmanmkc wants to merge 1 commit intoactions:mainfrom
salmanmkc:feature/cache-read-only
Open

Add cache-write input for read-only cache mode#710
salmanmkc wants to merge 1 commit intoactions:mainfrom
salmanmkc:feature/cache-read-only

Conversation

@salmanmkc
Copy link
Contributor

When cache: true is 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-write input (defaults to true, backward compatible). When false, the post-step save is skipped.

Usage:

- uses: actions/setup-dotnet@v5
  with:
    dotnet-version: 8.x
    cache: true
    cache-write: ${{ github.event_name != 'pull_request' }}

What changed:

  • action.yml — new cache-write input
  • src/cache-save.ts — early return when cache-write is false
  • dist/ — rebuilt

Same change going into setup-node, setup-python, setup-go, setup-java.

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.
@salmanmkc salmanmkc requested a review from a team as a code owner March 9, 2026 12:51
Copilot AI review requested due to automatic review settings March 9, 2026 12:51
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-write input in action.yml (default true for backward compatibility).
  • Skips the post-step cache save when cache-write is 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.

Comment on lines +17 to +18
const cacheWriteEnabled = core.getInput('cache-write');
if (cacheWriteEnabled === 'false') {
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
const cacheWriteEnabled = core.getInput('cache-write');
if (cacheWriteEnabled === 'false') {
const cacheWriteEnabled = core.getBooleanInput('cache-write');
if (!cacheWriteEnabled) {

Copilot uses AI. Check for mistakes.
Comment on lines +17 to 24
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();
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
Comment on lines +17 to 25
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();
}
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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 uses AI. Check for mistakes.
@Frulfump
Copy link

Frulfump commented Mar 9, 2026

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants