From 26a1487b67d64cc6b609833e331f8d2b33ccfe8a Mon Sep 17 00:00:00 2001 From: Aaron Sachs Date: Thu, 10 Sep 2026 15:10:43 -0400 Subject: [PATCH] fix(ci): stop persisting a write-scoped git credential through npm ci The release job declares contents: write, which overrides this repo's read-only default workflow permission. So actions/checkout's default persisted credential is WRITE-scoped and stays in .git/config through npm ci, build and test -- readable off disk by any compromised dependency lifecycle script, which could use it to push. semantic-release authenticates its own pushes from GITHUB_TOKEN and never needed the persisted credential; persist-credentials: false is its own documented GitHub Actions recipe. Verified: this repo uses @semantic-release/git, so the push path is semantic-release's, not the checkout credential's. Matches the pattern already in node-syncro, node-atera and node-connectwise-automate. CWE-250, flagged by CodeRabbit. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_0154CagrzaLihv19SKENWpKQ --- .github/workflows/release.yml | 9 +++++++++ CHANGELOG.md | 3 +++ 2 files changed, 12 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6c50808..147947e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,6 +20,15 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 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 - name: Setup Node.js uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 65e06ff..74e1197 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **Release workflow no longer persists a write-scoped git credential across `npm ci`.** 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, build and test — 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, flagged by CodeRabbit.) + + - Standardized on Node 22: bumped `@types/node` to `^22` and set `tsup` build target to `node22`. ### Added