From 735e88f0d65bcf1a8a429b9c20c28b0d75cb3100 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 29 Aug 2026 10:42:21 +0000 Subject: [PATCH] Harden CI: stop the build and publish jobs persisting the job credential actions/checkout writes the job's GITHUB_TOKEN into .git/config as an http.extraheader and leaves it there for the rest of the job. Everything that runs afterwards -- a build script, an installer signer, an installed dependency -- can read it, and it is trivially captured into an uploaded artifact. zizmor reports this as `artipacked`. Six sites across four workflows, all of them jobs that never touch the remote after checkout: desktop-artifacts.yml macos / windows / linux -- the file contains no git invocation at all; the `release` job that does hold contents: write has no checkout and attaches assets through the Releases API. publish.yml builds the wheel and uploads with twine using PYPI_API_TOKEN. No git, no Contents write. moat-keystone-... spends its `issues: write` scope through the gh CLI via GH_TOKEN, which this does not affect. auto-deploy-cloud.yml the OSS checkout only. Every git command in that job runs after `cd clawmetry-cloud`, against the second checkout and its CLOUD_REPO_PAT; that one is deliberately left alone and now says so. This changes no `permissions:` block. persist-credentials governs only whether the credential is left in .git/config; a scope spent through the API, the gh CLI, an action's `token:` input or an explicitly built remote URL is untouched. The release pipeline's write paths are all of that kind, which is why these six had no consumer to lose. Verified: 35 workflow + composite-action files parse; artipacked drops from 23 sites to 17 with no new site introduced (the PAT checkout still reports, correctly); the SHA-pinning ratchet still holds at 17 references; the repo's workflow guards report 192 passed, 2 skipped. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_018oDZctSrHcpCUP8onVhJKR --- .github/workflows/auto-deploy-cloud.yml | 8 ++++++++ .github/workflows/desktop-artifacts.yml | 6 ++++++ .github/workflows/moat-keystone-drive-nightly.yml | 2 ++ .github/workflows/publish.yml | 2 ++ 4 files changed, 18 insertions(+) diff --git a/.github/workflows/auto-deploy-cloud.yml b/.github/workflows/auto-deploy-cloud.yml index e19825364e..b263788002 100644 --- a/.github/workflows/auto-deploy-cloud.yml +++ b/.github/workflows/auto-deploy-cloud.yml @@ -36,6 +36,14 @@ jobs: steps: - name: Checkout OSS repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + # Nothing in this job pushes from the OSS working copy -- every git + # command below runs after `cd clawmetry-cloud`, against the second + # checkout and its CLOUD_REPO_PAT. So this credential has no consumer + # and only stands in .git/config for the rest of the job. Do NOT copy + # this to the checkout below: that one's credential is the one the + # force-push uses. + persist-credentials: false - name: Get OSS version id: oss_version diff --git a/.github/workflows/desktop-artifacts.yml b/.github/workflows/desktop-artifacts.yml index 4782634dba..cfcd99e9ea 100644 --- a/.github/workflows/desktop-artifacts.yml +++ b/.github/workflows/desktop-artifacts.yml @@ -41,6 +41,8 @@ jobs: HAS_SIGN: ${{ secrets.MACOS_SIGN_IDENTITY != '' }} steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false # Same rule as the Windows job: signing is optional everywhere except # a release tag. An unsigned .dmg is refused by Gatekeeper outright, @@ -265,6 +267,8 @@ jobs: HAS_WIN_CERT: ${{ secrets.WINDOWS_CERT_PFX_BASE64 != '' && !(secrets.AZURE_CLIENT_ID != '' && secrets.AZ_SIGN_PROFILE != '') }} steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false # A tag build that quietly produces an UNSIGNED artifact is worse # than a failed build: it reaches users as "unknown publisher", and # on Windows an unsigned uninstaller stub is blocked by Smart App @@ -516,6 +520,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.11" diff --git a/.github/workflows/moat-keystone-drive-nightly.yml b/.github/workflows/moat-keystone-drive-nightly.yml index 43a969528d..e299e937e2 100644 --- a/.github/workflows/moat-keystone-drive-nightly.yml +++ b/.github/workflows/moat-keystone-drive-nightly.yml @@ -46,6 +46,8 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.11" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3312ba3757..2549eb3317 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -32,6 +32,8 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: