Skip to content

Security: pin third-party release actions to commit SHAs - #7

Open
pimfeltkamp wants to merge 1 commit into
mainfrom
ai-security/code-dbcddead
Open

pimfeltkamp wants to merge 1 commit into
mainfrom
ai-security/code-dbcddead

Conversation

@pimfeltkamp

Copy link
Copy Markdown
Contributor

What was vulnerable

.github/workflows/release.yml grants id-token: write (PyPI trusted publishing) and contents: write to the publish job, and that job ran two third-party actions on mutable refs: pypa/gh-action-pypi-publish@release/v1 and softprops/action-gh-release@v3. If either upstream branch/tag were hijacked, the attacker's code would run inside the job that can mint the trusted-publisher OIDC token for the cryptohopper PyPI project and modify GitHub releases.

What the fix does

Pins both actions to full commit SHAs with a version comment:

  • pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # v1.14.3
  • softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3

Verified with gh api repos/<owner>/<repo>/commits/<ref> that release/v1 and v1.14.3 both resolve to 7245906…, and v3 and v3.0.3 both resolve to efb3536…. The pinned code is therefore exactly what the workflow runs today. The existing Dependabot github-actions entry keeps SHA-pinned actions updated using the # vX.Y.Z comment.

The fix touches a CI workflow because the vulnerable code is the workflow itself. First-party actions/checkout@v6 and actions/setup-python@v6 were left on tags, as the finding only covered third-party actions.

Breaking?

No. Same action commits as before. For pypa/gh-action-pypi-publish the reviewer confirmed the action resolves its container image from the action ref, and the image tagged with this SHA shares the same digest as the release-v1 image, so publishing behaviour is unchanged.

Tests

  • Repo test suite does not cover workflow files; no test added.
  • python -I stdlib regex check over release.yml: every non-actions/* uses: ref is a 40-hex SHA. Result: unpinned third-party: [].
  • YAML parse with PyYAML failed with ModuleNotFoundError: No module named 'yaml' (not installed); yamllint and ruby -ryaml required an approval nobody could grant in the unattended run. The change only replaces two scalar values with sha # comment, which the independent reviewer confirmed is valid YAML.

Reviewer notes (not fixed here, outside this finding)

  • The suggested job split (GitHub release in a separate job with only contents: write, publishing alone with id-token: write, tests with no write permissions) was not done to keep the diff minimal. Recommended follow-up.
  • Unpinned pip packages (build, .[dev], and hatchling fetched by python -m build) run in the same privileged job. A compromised PyPI package could reach the OIDC token. Recommended follow-up: hashed requirements and/or moving tests out of the privileged job.
  • actions/checkout defaults to persist-credentials: true, leaving the write-capable token in .git/config for later steps. Consider persist-credentials: false.
  • pypa/gh-action-pypi-publish pulls its container image by an overwritable registry tag rather than a digest. Upstream design, same trust boundary as the action code; cannot be fixed in this repo.

🤖 Automated security fix by claude-code-security. It is reviewed by the automated PR reviewer and auto-merged only if the review passes, CI is green, and the change is small and non-breaking.

🤖 Generated with Claude Code

pypa/gh-action-pypi-publish and softprops/action-gh-release ran on mutable refs (release/v1, v3) inside the job holding id-token: write for PyPI trusted publishing. Pin both to the commit SHAs those refs currently resolve to (v1.14.3, v3.0.3) so a hijacked upstream tag or branch cannot run in the publishing job. Dependabot keeps the pins updated via the version comments.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@pimfeltkamp pimfeltkamp left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Automated review passed — no issues found. (Would approve, but the reviewer account authored this PR and GitHub forbids self-approval.)

✅ Safe to merge

@pimfeltkamp pimfeltkamp added the ai-security-needs-human Security agent: this PR needs a human decision label Nov 2, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-security-needs-human Security agent: this PR needs a human decision

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant