Skip to content

SEC015 — release-cache-poisoning

Severity: warning · Category: Security

What it checks

Flags a job that restores a GitHub Actions cache (via actions/cache or a setup-* action's built-in cache: input) inside a workflow triggered by release.

Why it matters

Anyone with a valid GITHUB_TOKEN — for example, from an unrelated, low-privilege PR run — can write entries into a repo's GitHub Actions cache. If a release-triggered workflow later restores a cache keyed in a way an attacker could predict or collide with, that poisoned entry gets restored into a privileged release job, potentially executing attacker-controlled code at build time or tampering with the artifact that ends up published.

Examples

Flagged — a release build reuses whatever cache entry matches, without regard for who might have written it:

on:
  release:
    types: [published]

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f
        with:
          path: node_modules
          key: npm-${{ hashFiles('package-lock.json') }}
      - run: npm publish

Fixed — skip caching entirely for the release path, keeping the published artifact built from a clean, unrestored state:

on:
  release:
    types: [published]

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - run: npm ci # fresh install, no cache restore
      - run: npm publish

Some caching actions support disabling restoration conditionally instead of removing the step entirely — check the specific action's docs for an input like enable-cache: false or lookup-only: true that can be tied to the release trigger.

Suppressing

The finding is reported on the job's declaration line:

  publish: # vlotpipe: ignore[SEC015]
    runs-on: ubuntu-latest

Reasonable if the cache key is content-addressed in a way that makes poisoning it require also compromising the content it's keyed on (making the cache no more attacker-controllable than the source itself).