Skip to content

Harden GitHub Actions workflows per zizmor audit#187

Merged
sdairs merged 3 commits into
mainfrom
security/zizmor-workflow-hardening
May 18, 2026
Merged

Harden GitHub Actions workflows per zizmor audit#187
sdairs merged 3 commits into
mainfrom
security/zizmor-workflow-hardening

Conversation

@sdairs
Copy link
Copy Markdown
Collaborator

@sdairs sdairs commented May 18, 2026

Summary

Runs zizmor over .github/workflows/ and addresses every finding. No functional CI changes — purely supply-chain hardening.

  • SHA-pin every third-party action to the latest tagged release. The tag is preserved as a trailing comment so future bumps stay reviewable:
    • actions/checkoutde0fac2e4500dabe0009e67214ff5f5447ce83dd (v6.0.2)
    • actions/upload-artifact043fb46d1a93c77aae656e7c1c64a875d1fc6a0a (v7.0.1)
    • actions/download-artifact3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c (v8.0.1)
    • Swatinem/rust-cachee18b497796c12c097a38f9edb9d0641fb99eee32 (v2.9.1)
    • dtolnay/rust-toolchain29eef336d9b2848a0b548edc03f92a220660cdb8 (stable branch @ 2026-03-27)
  • Drop softprops/action-gh-release in favour of a gh release create script step. gh is pre-installed on every GitHub-hosted runner, so removing the third-party action shrinks the release path's external dependency surface. Tag and token are passed via env vars rather than direct ${{ }} expression expansion to keep us out of the template-injection class of issues.
  • persist-credentials: false on every actions/checkout invocation. None of these jobs push via the default GITHUB_TOKEN, so leaving it in the worktree only widens the blast radius if a later step is compromised.
  • release.yml: drop workflow-level contents: write to contents: read; grant contents: write only on the release job that publishes.
  • test-install.yml: add an explicit workflow-level permissions: contents: read block — previously the workflow inherited overly broad default token permissions.

The checkout v6 / upload-artifact v7 / download-artifact v7+ majors all require Actions Runner ≥ 2.327.1 (Node.js 24); GitHub-hosted runners satisfy this. download-artifact v8 also now defaults to erroring on artifact hash mismatch, which is the secure default we want.

zizmor result after this PR

No findings to report. Good job! (34 suppressed)

Merge cleanliness with open PRs

Only three open PRs touch any workflow file, and all touch only cloud-integration.yml:

The other 12 open PRs don't touch .github/workflows/ at all.

Test plan

  • zizmor .github/workflows reports zero findings on the branch
  • All non-release CI workflows still pass on this PR (they exercise the pinned versions of every touched action)
  • release.yml is verified by the next tag push; behavioural changes are (a) the permissions split (release job retains contents: write) and (b) gh release create replacing softprops/action-gh-release — same inputs (auto-generated notes + release/* files)

🤖 Generated with Claude Code

@sdairs sdairs requested a review from iskakaushik as a code owner May 18, 2026 11:34
@sdairs sdairs temporarily deployed to cloud-integration May 18, 2026 11:34 — with GitHub Actions Inactive
@CLAassistant
Copy link
Copy Markdown

CLAassistant commented May 18, 2026

CLA assistant check
All committers have signed the CLA.

sdairs and others added 2 commits May 18, 2026 12:47
- Pin every third-party action to a commit SHA (latest tagged release
  within its current major). Tag noted in trailing comment so future
  bumps stay reviewable.
- Set persist-credentials: false on every actions/checkout invocation so
  the default GITHUB_TOKEN isn't left in the worktree for later steps
  to exfiltrate via artifacts or environment files.
- Drop release.yml workflow-level contents: write to contents: read and
  re-grant contents: write only on the release job that publishes.
- Add an explicit permissions: contents: read block to test-install.yml,
  which previously inherited overly broad default token permissions.

Resolves all zizmor errors and warnings; only an info-level suggestion
to replace softprops/action-gh-release with a `gh release` script step
remains, which is out of scope for this hardening pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- actions/checkout v4.3.1 → v6.0.2 (de0fac2e)
- actions/upload-artifact v4.6.2 → v7.0.1 (043fb46d)
- actions/download-artifact v4.3.0 → v8.0.1 (3e5f45b2)

v6 of checkout, v7 of upload-artifact, and v7+ of download-artifact run
on Node.js 24 and require Actions Runner ≥ 2.327.1, which the GitHub-
hosted runners satisfy. download-artifact v8 also enforces hash checks
by default — we want that.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sdairs sdairs force-pushed the security/zizmor-workflow-hardening branch from ac00cce to 36f8fe2 Compare May 18, 2026 11:48
`gh` is pre-installed on every GitHub-hosted runner, so dropping the
third-party action removes one external dependency from the release
path. Tag and token come in via env vars (not direct expression
expansion) to avoid the template-injection class of finding.

Clears the last remaining zizmor info-level finding; the audit now
reports zero findings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sdairs sdairs merged commit 6bb80e8 into main May 18, 2026
12 checks passed
@sdairs sdairs deleted the security/zizmor-workflow-hardening branch May 18, 2026 17:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants