Action fetches and runs remote code from a mutable ref
Control: Actions must not execute mutable remote code· Config key: actionsMustNotExecuteMutableRemoteCode
📋 What is this?
A third-party action's own source fetches a script from a moving ref of another repository: a branch such as main/master, not a tag or commit SHA and executes it at runtime without verifying the download against a checksum. Pinning the action by commit SHA does not reach this: the fetched code lives upstream, where nothing is committed at the point your pin can see. The canonical example is anchore/scan-action, which downloads and runs grype's install.sh from that repo's main branch.
⚠️ Impact
If that upstream branch is retagged or compromised, the changed script runs in your job with its GITHUB_TOKEN and secrets: no pull request, no new SHA, nothing your pin or review can catch.
🔧 How to fix
Prefer an action that pins its downloads to an immutable ref and verifies a checksum. Otherwise install the tool yourself from a pinned release, verify it against a checksum committed to your repo, and run it directly; or vendor the action and pin its internal fetch. A fetch that already verifies a pinned checksum is content-pinned and is not flagged.
# .github/workflows/scan.yml: ❌ SHA-pinned, but still runs remote codejobs: scan: runs-on: ubuntu-latest steps: - uses: anchore/scan-action@d43cc1dfea6a99ed123bf8f3133f1797c9b44492 # v3 # ^ the pin is fine; the action's OWN source fetches and runs # grype's install.sh from the mutable 'main' branch at runtime# .github/workflows/scan.yml: ✅ install the tool yourself, pinned + verifiedjobs: scan: runs-on: ubuntu-latest steps: - run: | curl -sSL -o grype.tar.gz \ https://github.com/anchore/grype/releases/download/v0.74.7/grype_0.74.7_linux_amd64.tar.gz echo "EXPECTED_SHA256 grype.tar.gz" | sha256sum -c - tar -xzf grype.tar.gz grype ./grype dir:.💡 Tips
- A fetch that already verifies a pinned checksum is content-pinned and is not flagged.
- This is a static, text-level match on the action's source: a clean result means "no known pattern seen", not "safe". An author who deliberately hides the fetch is ISSUE-715.
- Plumber reads the action's source over the GitHub API; running offline, against GHES, or against a private action reports could-not-verify (ISSUE-716) instead.
- The scanned repo can itself be the action (producer side): the same pattern in your own
action.ymlfires too.
⚙️ Configuration
This control is configured in .plumber.yaml under the github section:
github:
controls:
actionsMustNotExecuteMutableRemoteCode:
enabled: trueSee the CLI documentation for the full configuration reference.