Skip to content

ci: Add static analysis of GitHub Actions with zizmor#2693

Open
matthewfeickert wants to merge 16 commits into
scikit-hep:mainfrom
matthewfeickert:feat/add-zizmor
Open

ci: Add static analysis of GitHub Actions with zizmor#2693
matthewfeickert wants to merge 16 commits into
scikit-hep:mainfrom
matthewfeickert:feat/add-zizmor

Conversation

@matthewfeickert
Copy link
Copy Markdown
Member

@matthewfeickert matthewfeickert commented Apr 9, 2026

Description

Resolves #2685

Checklist Before Requesting Reviewer

  • Tests are passing
  • "WIP" removed from the title of the pull request
  • Selected an Assignee for the PR to be responsible for the log summary

Before Merging

For the PR Assignees:

  • Summarize commit messages into a comprehensive review of the PR
* Add zizmor as a pre-commit hook.
   - Set persona to 'regular' instead of 'pedantic' for the time being.
     c.f. https://docs.zizmor.sh/usage/#using-personas
* Apply zizmor fixes to workflows.
   - https://docs.zizmor.sh/audits/#dependabot-cooldown
   - https://docs.zizmor.sh/audits/#artipacked
   - https://docs.zizmor.sh/audits/#secrets-outside-env
   - https://docs.zizmor.sh/audits/#template-injection

Summary by CodeRabbit

  • Chores
    • Enhanced CI/CD pipeline security by implementing improved credential handling across all workflows.
    • Configured a 7-day cooldown period for automated dependency updates to reduce PR frequency.
    • Added GitHub Actions security scanning and validation to maintain workflow best practices.

Comment thread .github/workflows/bump-version.yml Outdated
permissions:
contents: write # for Git to git push
runs-on: ubuntu-latest
environment: ci
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@henryiii is https://docs.zizmor.sh/audits/#secrets-outside-env going to mean that every GitHub action run triggers a huge number of "deployments" now because this exists in an environment?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not sure, but I think so. This check is only triggered with the "auditor" level or something like that, right?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

That can be avoided à la data-apis/array-api-extra#699

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@lucascolley we have

    environment:
      name: ci
      deployment: false

already, but now the CI isn't running.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

but now the CI isn't running.

what do you mean by this? The required status checks are showing as pending because the job names have been changed

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The required status checks are showing as pending because the job names have been changed

@lucascolley you're (in retrospect, obviously) very correct

test (ubuntu-latest, 3.13)

vs. now

CI/CD / CI (ubuntu-latest, 3.13) (pull_request)

Thanks for pointing this out. Apologies for not having taken the time to think before posting. I didn't realize that in

jobs:
  test:

    name: CI
    runs-on: ${{ matrix.os }}
    environment:
      name: ci
      deployment: false
...

that name: CI would override everything else — I thought it was internal naming.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 9, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.28%. Comparing base (4a068b7) to head (dc92f02).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2693   +/-   ##
=======================================
  Coverage   98.28%   98.28%           
=======================================
  Files          65       65           
  Lines        4305     4305           
  Branches      465      465           
=======================================
  Hits         4231     4231           
  Misses         46       46           
  Partials       28       28           
Flag Coverage Δ
contrib 98.16% <ø> (ø)
doctest 98.28% <ø> (ø)
unittests-3.10 96.46% <ø> (ø)
unittests-3.11 96.46% <ø> (ø)
unittests-3.12 96.46% <ø> (ø)
unittests-3.13 96.46% <ø> (ø)
unittests-3.9 96.53% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 14, 2026

Warning

Rate limit exceeded

@matthewfeickert has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 21 minutes and 27 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 633c6e0d-a501-40f7-b78f-108f98d7b202

📥 Commits

Reviewing files that changed from the base of the PR and between a5c3f78 and dc92f02.

📒 Files selected for processing (4)
  • .github/workflows/ci-windows.yml
  • .github/workflows/semantic-pr-check.yml
  • .github/zizmor.yml
  • .pre-commit-config.yaml
📝 Walkthrough

Walkthrough

The pull request introduces security hardening and static analysis tooling across GitHub Actions workflows. Changes include disabling credential persistence in checkout steps, adding GitHub Actions environment configurations, introducing Zizmor security scanning via pre-commit hooks, implementing a Dependabot cooldown policy, and refactoring the version bump workflow to use environment variables instead of direct context references.

Changes

Cohort / File(s) Summary
Checkout Credential Persistence Hardening
.github/workflows/codeql-analysis.yml, .github/workflows/docs.yml, .github/workflows/lint.yml, .github/workflows/lower-bound-requirements.yml, .github/workflows/merged.yml, .github/workflows/notebooks.yml, .github/workflows/publish-package.yml, .github/workflows/release_tests.yml
Adds persist-credentials: false to actions/checkout steps across multiple workflows to prevent Git credentials from persisting in the runner environment.
Environment Configuration and Job Documentation
.github/workflows/ci.yml, .github/workflows/docker.yml, .github/workflows/ci-windows.yml, .github/workflows/dependencies-head.yml
Adds explicit GitHub Actions environment configurations (ci environment with deployment: false), adds human-readable job names, disables credential persistence in checkout, and refactors digest output handling in Docker workflow.
Version Bump Workflow Refactoring
.github/workflows/bump-version.yml
Migrates workflow input references from ${{ github.event.inputs.* }} to shell environment variables, adds job environment configuration, disables credential persistence, parameterizes version bump and tagging logic, and updates RC/stable branching and validation error messages.
Security and Dependency Tooling
.github/dependabot.yml, .github/zizmor.yml, .pre-commit-config.yaml, .github/workflows/semantic-pr-check.yml
Adds Dependabot cooldown policy (7-day minimum between PRs), introduces Zizmor static analysis configuration, adds Zizmor pre-commit hook with path filtering, and suppresses Zizmor dangerous-triggers warning in semantic PR check workflow.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and specifically identifies the main change: adding zizmor as static analysis for GitHub Actions, which aligns with the primary purpose and scope of the changeset.
Linked Issues check ✅ Passed The PR successfully addresses issue #2685 by integrating zizmor for GitHub Actions static analysis through pre-commit configuration, workflow updates, and applying recommended fixes from zizmor audits.
Out of Scope Changes check ✅ Passed All changes are in-scope and directly support the zizmor integration objective, including workflow updates for credential persistence, environment configuration, and template injection fixes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 21 minutes and 27 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@matthewfeickert
Copy link
Copy Markdown
Member Author

matthewfeickert commented Apr 30, 2026

On the Scientific Python Discord, Seth Larson pointed out

If that review is blocked on using an environment, maybe that can be backed out and the rest merged? There's a way to use Zizmor in a way that sends the "findings" to the GitHub security scanning tab instead of failing CI.

and @tupi helpfully mentioned that in addition to

permissions:
      security-events: write

under https://docs.zizmor.sh/integrations/#manual-integration you can write out a sarif file.

@matthewfeickert matthewfeickert added the pre-commit Related to pre-commit hooks label May 1, 2026
@matthewfeickert matthewfeickert marked this pull request as ready for review May 1, 2026 19:10

on:
pull_request_target:
pull_request_target: # zizmor: ignore[dangerous-triggers]
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'm not sure if there is anything that can be done about this given https://github.com/amannn/action-semantic-pull-request/tree/45b9ed7cf24087a2f7785bf55be97394ba87e1c2#installation still shows uing pull_request_target.


- name: Update the Git tag annotation
if: ${{ github.event.inputs.dry_run }} == 'false'
if: ${{ github.event.inputs.dry_run }} == 'false' # zizmor: ignore[unsound-condition]
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is one of two ignores, but in this particular case I think this is acceptable?

Without this I get https://docs.zizmor.sh/audits/#unsound-condition

error[unsound-condition]: unsound conditional expression
   --> .github/workflows/bump-version.yml:238:7
    |
238 |       if: ${{ github.event.inputs.dry_run }} == 'false'
    |       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ condition always evaluates to true
    |
    = note: audit confidence → High

22 findings (1 ignored, 20 suppressed): 0 informational, 0 low, 0 medium, 1 high

but dry_run can be set by the user.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
.pre-commit-config.yaml (1)

82-89: 🧹 Nitpick | 🔵 Trivial

Add zizmor to CI with SARIF output for centralized GitHub Security tab visibility.

Currently zizmor runs only in pre-commit locally. Pair it with a CI workflow step using zizmor's --format=sarif output and github/codeql-action/upload-sarif to expose findings centrally in the Security tab. This complements the existing SARIF infrastructure (scorecard.yml, codeql-analysis.yml) and provides auditable, persistent records of workflow security checks.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.pre-commit-config.yaml around lines 82 - 89, Add a CI workflow step that
runs the existing zizmor pre-commit hook in CI and emits SARIF so findings
appear in the GitHub Security tab: run the zizmor hook (the hook id "zizmor"
from the .pre-commit-config entry) in the workflow instead of relying only on
local pre-commit, invoke it with the --format=sarif argument (replace or extend
the current args array that contains --persona=regular), save the generated
SARIF artifact, and add the github/codeql-action/upload-sarif step to upload
that SARIF file; ensure the workflow step names and paths reference the same
hook id "zizmor" and produce a deterministic SARIF filename for upload.
.github/workflows/bump-version.yml (1)

52-57: ⚠️ Potential issue | 🟠 Major

Re-authenticate before the final git push.

With persist-credentials: false at line 57, the PAT token is not persisted to Git config, so the git push command at line 284 will fail with an authentication error. The workflow needs explicit credential setup before the push step.

Suggested fix
    - name: Push new tag back to GitHub
      shell: bash
      run: |
-        if [ ${GITHUB_EVENT_INPUTS_DRY_RUN} == 'true' ]; then
+        if [ "${GITHUB_EVENT_INPUTS_DRY_RUN}" == 'true' ]; then
             echo "# DRY RUN"
         else
-            git push origin ${GITHUB_EVENT_INPUTS_TARGET_BRANCH} --tags
+            git remote set-url origin "https://x-access-token:${ACCESS_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
+            git push origin "${GITHUB_EVENT_INPUTS_TARGET_BRANCH}" --tags
         fi
      env:
+        ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
         GITHUB_EVENT_INPUTS_DRY_RUN: ${{ github.event.inputs.dry_run }}
         GITHUB_EVENT_INPUTS_TARGET_BRANCH: ${{ github.event.inputs.target_branch }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/bump-version.yml around lines 52 - 57, The workflow
currently sets actions/checkout@v6.0.0 with persist-credentials: false which
prevents the PAT being available for the later git push; add an explicit
authentication step before the final git push (or change persist-credentials to
true) so the push will succeed. Concretely, either set persist-credentials: true
on the actions/checkout invocation or add a step prior to the git push that
configures credentials (for example, set the remote URL to include the token or
run git config/credential helper with the token from secrets.ACCESS_TOKEN) so
the git push command can authenticate; update the job that performs the push
(the step invoking git push) to run after this authentication step.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/semantic-pr-check.yml:
- Line 4: The workflow suppresses the dangerous-triggers audit for
pull_request_target but uses a mutable action tag; update the action reference
amannn/action-semantic-pull-request@v6 to a specific commit SHA (full 40-char
hash) so the workflow is pinned before keeping the suppression for
pull_request_target; locate the action usage string
"amannn/action-semantic-pull-request@v6" and replace the tag with the exact
commit SHA for that release.

In @.github/zizmor.yml:
- Around line 1-3: Replace the global suppression rules.unpinned-uses.disable:
true with a scoped ignore list so the unpinned-uses check remains active
everywhere except trusted workflows; modify the YAML key
rules.unpinned-uses.disable to rules.unpinned-uses.ignore and set it to an array
of specific workflows/locations (e.g., ["ci.yml:100", "tests.yml"]) to suppress
only those findings, leaving the audit enabled for all other workflows.

---

Outside diff comments:
In @.github/workflows/bump-version.yml:
- Around line 52-57: The workflow currently sets actions/checkout@v6.0.0 with
persist-credentials: false which prevents the PAT being available for the later
git push; add an explicit authentication step before the final git push (or
change persist-credentials to true) so the push will succeed. Concretely, either
set persist-credentials: true on the actions/checkout invocation or add a step
prior to the git push that configures credentials (for example, set the remote
URL to include the token or run git config/credential helper with the token from
secrets.ACCESS_TOKEN) so the git push command can authenticate; update the job
that performs the push (the step invoking git push) to run after this
authentication step.

In @.pre-commit-config.yaml:
- Around line 82-89: Add a CI workflow step that runs the existing zizmor
pre-commit hook in CI and emits SARIF so findings appear in the GitHub Security
tab: run the zizmor hook (the hook id "zizmor" from the .pre-commit-config
entry) in the workflow instead of relying only on local pre-commit, invoke it
with the --format=sarif argument (replace or extend the current args array that
contains --persona=regular), save the generated SARIF artifact, and add the
github/codeql-action/upload-sarif step to upload that SARIF file; ensure the
workflow step names and paths reference the same hook id "zizmor" and produce a
deterministic SARIF filename for upload.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 44cb0253-1189-4b27-a811-61998cc7f356

📥 Commits

Reviewing files that changed from the base of the PR and between 4a068b7 and a5c3f78.

📒 Files selected for processing (17)
  • .github/dependabot.yml
  • .github/workflows/bump-version.yml
  • .github/workflows/ci-windows.yml
  • .github/workflows/ci.yml
  • .github/workflows/codeql-analysis.yml
  • .github/workflows/dependencies-head.yml
  • .github/workflows/docker.yml
  • .github/workflows/docs.yml
  • .github/workflows/lint.yml
  • .github/workflows/lower-bound-requirements.yml
  • .github/workflows/merged.yml
  • .github/workflows/notebooks.yml
  • .github/workflows/publish-package.yml
  • .github/workflows/release_tests.yml
  • .github/workflows/semantic-pr-check.yml
  • .github/zizmor.yml
  • .pre-commit-config.yaml


on:
pull_request_target:
pull_request_target: # zizmor: ignore[dangerous-triggers]
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 1, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Finding pull_request_target workflows and non-SHA-pinned actions..."
for wf in $(fd -e yml -e yaml .github/workflows); do
  if rg -n '^\s*pull_request_target\s*:' "$wf" >/dev/null; then
    echo "== $wf =="
    rg -n '^\s*uses:\s+\S+@' "$wf" || true
    echo "-- non SHA-pinned uses --"
    rg -nP '^\s*uses:\s+\S+@(?![0-9a-f]{40}\b)\S+' "$wf" || true
  fi
done

Repository: scikit-hep/pyhf

Length of output: 519


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Correctly search .github/workflows directory
echo "=== Checking .github/workflows directory ==="
fd . '.github/workflows' -e yml -e yaml

echo -e "\n=== Reading semantic-pr-check.yml ==="
cat -n '.github/workflows/semantic-pr-check.yml' 2>/dev/null || echo "File not found"

echo -e "\n=== Checking for actions with non-SHA pins in pull_request_target workflows ==="
for wf in $(fd . '.github/workflows' -e yml -e yaml); do
  if rg -q '^\s*pull_request_target\s*:' "$wf"; then
    echo "== Workflow: $wf =="
    rg -A 50 '^\s*pull_request_target\s*:' "$wf" | head -60
    echo ""
  fi
done

Repository: scikit-hep/pyhf

Length of output: 2494


Pin action to commit SHA before suppressing dangerous-triggers warning.

Line 4 suppresses the dangerous-triggers audit on pull_request_target, but the workflow runs with an unpinned action tag (amannn/action-semantic-pull-request@v6 on line 28). Replace the mutable tag with a full commit SHA to mitigate supply-chain risk, then the suppression is justified.

🔧 Suggested changes
-  pull_request_target:  # zizmor: ignore[dangerous-triggers]
+  pull_request_target:
-        uses: amannn/action-semantic-pull-request@v6
+        uses: amannn/action-semantic-pull-request@<FULL_40_CHAR_COMMIT_SHA>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/semantic-pr-check.yml at line 4, The workflow suppresses
the dangerous-triggers audit for pull_request_target but uses a mutable action
tag; update the action reference amannn/action-semantic-pull-request@v6 to a
specific commit SHA (full 40-char hash) so the workflow is pinned before keeping
the suppression for pull_request_target; locate the action usage string
"amannn/action-semantic-pull-request@v6" and replace the tag with the exact
commit SHA for that release.

✅ Addressed in commits 76e83ff to dc92f02

Comment thread .github/zizmor.yml Outdated
@matthewfeickert
Copy link
Copy Markdown
Member Author

cc @jarrodmillman @drammock given our recent discussions, for examples of what applying zizmor looks like.

Comment thread .github/zizmor.yml Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI CI systems, GitHub Actions pre-commit Related to pre-commit hooks security Improving repository security measures

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

Add zizmor for static analysis of GitHub Actions

3 participants