ci: harden GitHub Actions workflows with zizmor#1623
Conversation
|
@claude review |
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
- Add `permissions: {}` defaults to ci, dependabot-rebase-stale,
package-availability-check workflows
- Add `persist-credentials: false` to all checkout steps
- Move `${{ }}` interpolations in run blocks to env vars (release.yml)
- Replace `softprops/action-gh-release` with `gh release create`
- Switch claude-review from `pull_request_target` to `pull_request`
with fork check
- Replace spoofable `github.actor` check with `user.id` for dependabot
- Add zizmor CI workflow for ongoing monitoring
- Add `lookup-only: true` to mypy cache (type-checking job)
- Disable uv cache in release workflow (publishes to PyPI)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
abc8ea5 to
a6b9fec
Compare
There was a problem hiding this comment.
The lookup-only: true issue I flagged previously is no longer present in the current diff — LGTM on the security hardening.
Extended reasoning...
Overview
This PR hardens all eight GitHub Actions workflow files against common CI/CD security vulnerabilities flagged by zizmor: adds permissions: {} defaults, persist-credentials: false on all checkouts, moves ${{ }} template interpolations to env: vars, replaces softprops/action-gh-release with gh release create, switches claude-review-maintainer-prs from pull_request_target to pull_request with fork exclusion, and replaces the spoofable github.actor check with a user.id comparison in dependabot-merge.yml. A new zizmor.yml workflow provides ongoing monitoring.
Security risks
The changes reduce risk rather than introduce it. The only remaining finding (noted as a nit inline) is a version comment inconsistency in the new zizmor.yml (# v6.0.2 vs # v6 used by every other workflow for the same SHA) — cosmetic only, no security impact.
Level of scrutiny
This is CI/CD configuration hardening with no changes to application code. The patterns used (persist-credentials: false, env: var injection, user.id checks) are well-established hardening practices. The release workflow changes are the most consequential (touching push/publish logic), but the approach — disabling uv cache, moving template interpolations to env:, and replacing the third-party release action with the GitHub CLI — is sound.
Other factors
My prior review flagged lookup-only: true on the mypy cache step as breaking incremental caching. The current diff no longer contains that setting, so that issue has been resolved. The remaining nit (version comment in zizmor.yml) is covered by the inline comment and does not warrant blocking the PR.
- zizmor.yml: align checkout comment to `# v6` matching other workflows - ci.yml: fix actions/cache comment from `# v5` to `# v5.0.4` (actual tag) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
permissions: {}defaults to workflows missing explicit permissionspersist-credentials: falseto allactions/checkoutsteps${{ }}interpolations inrun:blocks toenv:vars (template injection prevention)softprops/action-gh-releasewithgh release createclaude-review-maintainer-prsfrompull_request_targettopull_requestwith fork exclusiongithub.actorcheck withuser.idfor dependabot auto-mergelookup-only: trueto mypy cacheTest plan
Fixes https://linear.app/langfuse/issue/LFE-9208/zizmor-python-sdk
🤖 Generated with Claude Code
Disclaimer: Experimental PR review
Greptile Summary
This PR hardens all GitHub Actions workflows against common CI/CD security vulnerabilities identified by zizmor: template injection in
run:blocks, over-privileged workflows, spoofable actor checks, and thepull_request_targetprivilege escalation pattern. The changes are well-scoped and follow established hardening best practices.ci.ymlnow haslookup-only: truewhich disables both restore and save, making the cache step a no-op. The# zizmor: ignore[cache-poisoning]comment on the same line already suppresses the linter warning and is sufficient on its own.Confidence Score: 5/5
ci.yml— thelookup-only: trueon the mypy cache step.Important Files Changed
permissions: {},persist-credentials: falseon all checkouts, and uv cache-poisoning ignore comments. Thelookup-only: trueon the mypy cache step effectively disables cache restore and save, making it a no-op.${{ }}interpolations inrun:blocks moved toenv:vars. Replacedsoftprops/action-gh-releasewithgh release create. Addedpersist-credentials: falsealongside the existinggh auth login+gh auth setup-gitpattern — correct approach for push credentials.pull_request_targettopull_requestand added fork-exclusion guard — eliminates the privileged-context risk for untrusted fork PRs.github.actor == 'dependabot[bot]'withgithub.event.pull_request.user.id == 49699333— a hardened check that can't be bypassed by renaming a user account.Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[release.yml triggered] --> B[Verify branch via GITHUB_REF env] B --> C[Confirm major via INPUTS env] C --> D[checkout with persist-credentials false] D --> E[gh auth login + setup-git\nrestore push credentials] E --> F[Calculate version\nall inputs via env vars] F --> G[uv build] G --> H[git commit + push\nvia gh credential helper] H --> I[uv publish to PyPI\nOIDC trusted publishing] I --> J[gh release create\nreplaces softprops action] K[claude-review workflow] --> L{pull_request trigger\nnot pull_request_target} L --> M{head.repo == repository?} M -->|same repo| N[Post claude review comment] M -->|fork| O[Skip - untrusted context] P[dependabot-merge] --> Q{user.id check\nnot actor name} Q -->|match| R[Auto-merge patch updates] Q -->|no match| S[Skip]Reviews (1): Last reviewed commit: "ci: harden GitHub Actions workflows with..." | Re-trigger Greptile