Harden workflows: least-privilege permissions + zizmor integration#1039
Open
brunoborges wants to merge 4 commits into
Open
Harden workflows: least-privilege permissions + zizmor integration#1039brunoborges wants to merge 4 commits into
brunoborges wants to merge 4 commits into
Conversation
Apply GitHub Actions security best practices to the action's own
workflows and integrate zizmor to catch regressions.
- Add explicit least-privilege `permissions:` to every workflow
(contents: read for read-only workflows; default-deny `{}` with
job-scoped grants for codeql, publish-immutable-actions and
update-config-files).
- Set `persist-credentials: false` on all checkout steps that don't
need the GITHUB_TOKEN afterwards.
- Move `${{ ... }}` expansions out of `run:` blocks into `env:` vars
to avoid template injection.
- Pin the alpine container image (alpine:latest -> alpine:3.21).
- Add a zizmor CI workflow that uploads SARIF to code scanning, plus a
`.github/zizmor.yml` pinning policy (ref-pin for actions/* and
github/*, hash-pin for third-party actions).
zizmor now reports no findings (offline and online).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
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. |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR hardens this repository’s GitHub Actions workflows by enforcing least-privilege permissions, reducing token persistence, mitigating expression interpolation risks in run: blocks, pinning a previously-mutable container tag, and adding a zizmor workflow to continuously scan for workflow security regressions.
Changes:
- Add explicit least-privilege
permissions:across workflows (including top-level{}+ job-level overrides where needed). - Set
persist-credentials: falseonactions/checkoutwhere the token isn’t needed, and move${{ }}expansions out of inline shell scripts intoenv:variables. - Introduce zizmor configuration + a new workflow that runs zizmor and uploads SARIF to Code Scanning; pin
alpinefromlatestto3.21for the alpine e2e job.
Show a summary per file
| File | Description |
|---|---|
| .github/zizmor.yml | Defines zizmor pinning policy (ref-pin for first-party, hash-pin for third-party). |
| .github/workflows/zizmor.yml | Adds zizmor CI job to scan workflows and upload SARIF to code scanning. |
| .github/workflows/update-config-files.yml | Applies top-level {} permissions and grants minimal write permissions to the reusable workflow job. |
| .github/workflows/publish-immutable-actions.yml | Applies top-level {} permissions and disables checkout credential persistence. |
| .github/workflows/licensed.yml | Explicitly scopes workflow token to contents: read. |
| .github/workflows/e2e-versions.yml | Adds contents: read, disables credential persistence, pins alpine image, and avoids inline expression interpolation in run:. |
| .github/workflows/e2e-publishing.yml | Adds contents: read and disables checkout credential persistence across jobs. |
| .github/workflows/e2e-local-file.yml | Adds contents: read, disables checkout credential persistence, and avoids inline expression interpolation in run:. |
| .github/workflows/e2e-cache.yml | Adds contents: read and disables checkout credential persistence across jobs. |
| .github/workflows/e2e-cache-dependency-path.yml | Adds contents: read and disables checkout credential persistence across jobs. |
| .github/workflows/codeql-analysis.yml | Applies top-level {} permissions (job-level permissions already scoped). |
| .github/workflows/check-dist.yml | Explicitly scopes workflow token to contents: read. |
| .github/workflows/basic-validation.yml | Explicitly scopes workflow token to contents: read. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 13/13 changed files
- Comments generated: 1
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
The `if:` key on the "Upload SARIF results to code scanning" step had no
indentation, producing invalid YAML ("Nested mappings are not allowed in
compact mappings"). This broke `npm run format-check` (prettier) in Basic
validation.
Indent `if:` to 8 spaces so it nests under the step alongside uses/with.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.
Summary
Hardens the action's own CI/CD workflows by applying GitHub Actions security best practices and integrates zizmor so regressions are caught automatically going forward.
Motivation: after the recent supply-chain compromises in the Actions ecosystem, consumers increasingly expect the actions they depend on to follow least-privilege and hardening practices. These changes are config-only — no action source (
src//dist/) is touched, so runtime behavior ofsetup-javais unchanged.What changed
1. Least-privilege
permissions:on every workflowPreviously most workflows had no
permissions:block and therefore inherited the repository/organization default token scopes. Now each workflow declares exactly what it needs:basic-validation,check-dist,licensed, alle2e-*contents: readcodeql-analysis{}, job keepsactions: read/contents: read/security-events: writepublish-immutable-actions{}, job keepscontents: read/id-token: write/packages: writeupdate-config-files{}, job getscontents: write+pull-requests: write(needed to push the branch and open the PR)release-new-action-versioncontents: write(unchanged)2. Drop credential persistence
Added
persist-credentials: falseto allactions/checkoutsteps that don't subsequently use theGITHUB_TOKEN(every e2e/validation checkout plus the immutable-publish checkout). This prevents the token from lingering in the local git config.3. Avoid template injection in
run:blocksMoved
${{ matrix.version }}and${{ steps.setup-java.outputs.path }}expansions out of inlinerun:scripts intoenv:variables referenced as"$VAR", the pattern recommended to avoid shell injection via expression interpolation.4. Pin the container image
alpine:latest→alpine:3.21ine2e-versions.yml(mutablelatesttag → fixed version).5. Integrate zizmor
.github/workflows/zizmor.yml— runs on push/PR, fails the build on any finding (regression gate), and uploads SARIF to the Code scanning tab..github/zizmor.yml— pinning policy aligned with this repo's conventions: first-partyactions/*andgithub/*may use version tags (ref-pin), while any third-party action must be pinned to a full commit SHA (hash-pin).Validation
zizmor goes from 39 high + 39 medium + 31 low/info findings to 0, both offline and online:
All workflow YAML was validated for syntax.
Related
Addresses the hardening/security-posture aspect mentioned alongside #1023 (immutable releases). The
publish-immutable-actions.ymlworkflow that satisfies #1023 is also hardened here (persist-credentials: false, explicit top-level{}).