Skip to content

chore: Harden workflows: least-privilege permissions + zizmor integration#1039

Open
brunoborges wants to merge 4 commits into
actions:mainfrom
brunoborges:harden-workflows-zizmor
Open

chore: Harden workflows: least-privilege permissions + zizmor integration#1039
brunoborges wants to merge 4 commits into
actions:mainfrom
brunoborges:harden-workflows-zizmor

Conversation

@brunoborges

Copy link
Copy Markdown
Contributor

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 of setup-java is unchanged.

What changed

1. Least-privilege permissions: on every workflow

Previously most workflows had no permissions: block and therefore inherited the repository/organization default token scopes. Now each workflow declares exactly what it needs:

Workflow Permissions
basic-validation, check-dist, licensed, all e2e-* contents: read
codeql-analysis top-level {}, job keeps actions: read / contents: read / security-events: write
publish-immutable-actions top-level {}, job keeps contents: read / id-token: write / packages: write
update-config-files top-level {}, job gets contents: write + pull-requests: write (needed to push the branch and open the PR)
release-new-action-version already scoped to contents: write (unchanged)

2. Drop credential persistence

Added persist-credentials: false to all actions/checkout steps that don't subsequently use the GITHUB_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: blocks

Moved ${{ matrix.version }} and ${{ steps.setup-java.outputs.path }} expansions out of inline run: scripts into env: variables referenced as "$VAR", the pattern recommended to avoid shell injection via expression interpolation.

4. Pin the container image

alpine:latestalpine:3.21 in e2e-versions.yml (mutable latest tag → 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-party actions/* and github/* 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:

$ zizmor .github/workflows/
No findings to report. Good job!

All workflow YAML was validated for syntax.

Related

Addresses the hardening/security-posture aspect mentioned alongside #1023 (immutable releases). The publish-immutable-actions.yml workflow that satisfies #1023 is also hardened here (persist-credentials: false, explicit top-level {}).

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>
@brunoborges brunoborges requested a review from a team as a code owner June 22, 2026 18:58
Copilot AI review requested due to automatic review settings June 22, 2026 18:58
@github-advanced-security

Copy link
Copy Markdown

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:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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: false on actions/checkout where the token isn’t needed, and move ${{ }} expansions out of inline shell scripts into env: variables.
  • Introduce zizmor configuration + a new workflow that runs zizmor and uploads SARIF to Code Scanning; pin alpine from latest to 3.21 for 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

Comment thread .github/workflows/zizmor.yml Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@brunoborges brunoborges requested a review from gdams June 22, 2026 19:13
brunoborges and others added 2 commits June 22, 2026 15:16
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>
@brunoborges brunoborges added github_actions Pull requests that update GitHub Actions code ci CI/workflow related changes and requests security Security fixes or vulnerability-related changes labels Jun 22, 2026
@brunoborges brunoborges changed the title Harden workflows: least-privilege permissions + zizmor integration chore: Harden workflows: least-privilege permissions + zizmor integration Jun 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci CI/workflow related changes and requests github_actions Pull requests that update GitHub Actions code security Security fixes or vulnerability-related changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants