Add PR check to enforce SHA-pinned GitHub Actions - #3028
Open
P0NDER0SA wants to merge 5 commits into
Open
Conversation
Runs zizmor against .github/workflows (and .github/actions, if present) on every PR that touches them, and fails the check if any action is referenced by a mutable tag/branch instead of a full commit SHA. Follows up on the one-time SHA-pinning remediation to prevent regressions. Exceptions can be added inline with a '# zizmor: ignore[unpinned-uses]' comment, demonstrated on the notification-pr-bot@main reference which intentionally tracks main.
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a GitHub Actions workflow that enforces SHA-pinning for uses: references in workflows and composite actions, closing the gap where new unpinned actions could be introduced via pull requests.
Changes:
- Adds
.github/workflows/check_pinned_actions.yamlto runzizmorand fail PRs/pushes whenunpinned-usesfindings exist in.github/workflows/**or.github/actions/**. - Adds an inline zizmor ignore annotation for the intentionally unpinned
cds-snc/notification-pr-bot@mainreference in the Docker workflow.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| .github/workflows/docker.yaml | Adds a zizmor ignore annotation for an intentionally unpinned first-party action ref. |
| .github/workflows/check_pinned_actions.yaml | Introduces a CI workflow that scans for unpinned uses: references and fails with actionable output. |
Suppressed comments (1)
.github/workflows/check_pinned_actions.yaml:27
- If switching to
pull_request_target,actions/checkoutwill otherwise default to checking out the base branch. Add an explicitrefso the scan analyzes the PR's head commit forpull_request_targetevents while still working onpushevents.
- name: "Checkout code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Keeps the exception self-documenting per zizmor's own convention (# zizmor: ignore[rule] <reason>), rather than relying on a separate comment line above that could get edited or moved away independently.
…ants Falls back to the concrete feature (or a placeholder) instead of printing null if the location isn't a Local symbolic key.
Passing a nonexistent local path caused zizmor to misinterpret it as a remote owner/repo spec and fail with a GitHub API 404, since most of these repos don't have composite actions under .github/actions.
Copilot's suggested fix used a nonexistent 'given_path' field and lost the finding-level '.desc' by restructuring through .locations[] without capturing it first - verified it actually printed 'unknown location:N: null' against real zizmor output. This version keeps .desc, selects the Primary location, and adds a line number.
ben851
approved these changes
Aug 26, 2026
18 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Follow-up to the GH Actions SHA-pinning remediation: adds a mandatory PR check that automatically fails if a workflow or composite action references a third-party action by a mutable tag/branch instead of a full 40-character commit SHA. Same implementation as cds-snc/notification-manifests#5133.
Why not Renovate or the existing OSSF Scorecard job?
Pinned-Dependenciescheck only runs weekly/on push tomainas a scoring job — it never runs onpull_requestand doesn't fail the build.Neither currently blocks a PR, which is the actual gap this closes.
What this does
.github/workflows/check_pinned_actions.yaml, triggered onpull_request/push:mainwhen.github/workflows/**or.github/actions/**change.zizmorand filters its results down to just theunpinned-usesfinding, so it stays scoped to SHA-pinning only (zizmor's other audits, e.g.template-injection, are a separate concern, out of scope here).# zizmor: ignore[unpinned-uses] <reason>comment skips the check for that line. Demonstrated oncds-snc/notification-pr-bot@mainindocker.yaml, which intentionally tracksmain(already excluded from Renovate via this repo'srenovate.jsonignoreDeps) and can't be hash-pinned without losing that behavior.To make this a real merge gate
Branch protection on
mainneeds "Scan for unpinned actions" added as a required status check (not done in this PR — repo settings, not code).Testing
Ran
zizmorlocally against this repo's.github/workflows: found and fixed the one pre-existingunpinned-usesfinding, confirmed 0 findings remain, and confirmed the CI script correctly fails against a deliberately unpinned test workflow.