diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f84205b..5268612 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,7 +37,17 @@ jobs: if: github.ref == 'refs/heads/main' && github.event_name == 'push' steps: - uses: actions/checkout@v4 - with: { fetch-depth: 0 } + with: + fetch-depth: 0 + # The release job declares `contents: write`, which overrides this repo's + # read-only default workflow permission -- so the checkout's default + # persisted credential is WRITE-scoped and stays live in .git/config + # through `npm ci` below. A compromised dependency lifecycle script + # could read it off disk and push. semantic-release authenticates its + # own pushes from GITHUB_TOKEN, so it does not need the persisted + # credential; `persist-credentials: false` is semantic-release's own + # documented GitHub Actions recipe. (CWE-250) + persist-credentials: false - uses: actions/setup-node@v4 with: node-version: 22 diff --git a/CHANGELOG.md b/CHANGELOG.md index 50d8d1e..111634a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,3 +20,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] + +### Changed + +- **Release workflow no longer persists a write-scoped git credential across `npm install`.** The release job declares `contents: write`, which overrides this repo's read-only default workflow permission, so `actions/checkout`'s default persisted credential was write-scoped and lived in `.git/config` through dependency install and build — readable by any compromised dependency lifecycle script. `persist-credentials: false` is semantic-release's own documented GitHub Actions recipe; it authenticates its pushes from `GITHUB_TOKEN` directly and never needed the persisted credential. (CWE-250)