Background
The publish job in .github/workflows/ci.yml has elevated permissions:
permissions:
id-token: write # OIDC token for trusted PyPI publishing
contents: write # GitHub Release creation
All actions in the workflow are pinned by mutable tag (e.g. actions/checkout@v4) rather than immutable commit SHA. If a tag is force-pushed by a compromised or malicious maintainer, the publish job would run arbitrary code with PyPI publishing and GitHub release creation privileges. This is a supply-chain risk.
GitHub's own security hardening guide recommends SHA-pinning for workflows with sensitive permissions.
Fix
Pin the actions in the publish job (and optionally the whole workflow) to their commit SHAs. Use a tool like pinact or Ratchet to automate this:
# Using pinact
pinact run .github/workflows/ci.yml
Or manually look up each SHA:
gh api repos/actions/checkout/git/ref/refs/tags/v4 --jq '.object.sha'
gh api repos/pypa/gh-action-pypi-publish/git/ref/refs/heads/release/v1 --jq '.object.sha'
# etc.
Example pinned form:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: pypa/gh-action-pypi-publish@76f52bc884231d6614e0d5b2c4b57374e5f41060 # release/v1
Scope
At minimum pin the actions inside the publish job. Pinning the rest of the workflow (lint-and-test, build_wheels, etc.) is lower priority since those jobs have no write permissions.
Background
The
publishjob in.github/workflows/ci.ymlhas elevated permissions:All actions in the workflow are pinned by mutable tag (e.g.
actions/checkout@v4) rather than immutable commit SHA. If a tag is force-pushed by a compromised or malicious maintainer, thepublishjob would run arbitrary code with PyPI publishing and GitHub release creation privileges. This is a supply-chain risk.GitHub's own security hardening guide recommends SHA-pinning for workflows with sensitive permissions.
Fix
Pin the actions in the
publishjob (and optionally the whole workflow) to their commit SHAs. Use a tool like pinact or Ratchet to automate this:# Using pinact pinact run .github/workflows/ci.ymlOr manually look up each SHA:
Example pinned form:
Scope
At minimum pin the actions inside the
publishjob. Pinning the rest of the workflow (lint-and-test,build_wheels, etc.) is lower priority since those jobs have no write permissions.