chore: add zizmor security audit; harden existing workflow permissions#5
Merged
Conversation
This was referenced Apr 30, 2026
77b0784 to
c1f68c5
Compare
Adds a ``zizmor`` GitHub Actions workflow that audits ``.github/workflows`` on every PR that touches workflow / action definitions, with results surfaced as inline annotations (no GHAS / SARIF). Per-rule overrides live in ``.github/zizmor.yml``; ``unpinned-uses`` is disabled there pending a dedicated SHA-pin sweep. To make the new audit pass on the existing workflow set, the six existing workflows gain: * a top-level ``permissions: contents: read`` block (closes ``excessive-permissions`` finding — default token permissions were much broader than these read-only jobs need) * ``with: persist-credentials: false`` on every ``actions/checkout`` step (closes ``artipacked`` — prevents the runner from caching the GitHub App token in ``.git/config``, which would persist across PR runs on a self-hosted runner) Local zizmor run after the changes: ``No findings to report`` (23 suppressed are the unpinned-uses ones disabled in ``.github/zizmor.yml``). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Zhengyuan Su <su.zhengyuan@u.nus.edu>
The previous commit ran zizmor through ``zizmorcore/zizmor-action``,
which spawns ``docker run`` — the self-hosted runner container has no
Docker daemon access (no Docker-in-Docker), so the action exited 125
("Cannot connect to the Docker daemon"). Switch the workflow to
``uvx zizmor --persona regular --format github`` so the audit runs
in the same Python environment that ``setup-uv`` already provisions.
While replacing the action, also pin every remaining action to its
content SHA:
* ``actions/checkout@v6`` →
``actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6``
* ``astral-sh/setup-uv@v7`` →
``astral-sh/setup-uv@94527f2e458b27549849d47d273a16bec83a01e9 # v7``
This closes the ``unpinned-uses`` audit zizmor was suppressing via the
``.github/zizmor.yml`` config — that suppression file is now removed,
since there are no rules left to disable. The version comment after
each SHA stays so a human can read what was pinned.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Zhengyuan Su <su.zhengyuan@u.nus.edu>
Renames ``zizmor.yml`` to ``security.yml`` and updates the workflow's ``name:`` to ``security``. The file now reads as the home for every security-CI job, so adding gitleaks / bandit / pip-audit later means a new ``jobs.<name>:`` block, not a new file. The ``Run zizmor`` step now pins ``zizmor==1.24.1`` via ``uvx --from zizmor==1.24.1 zizmor``. Without the pin, the CI's uvx was resolving to a pre-1.x zizmor whose ``--format`` argument only accepted ``[plain, json, sarif]`` (no ``github``), so the step exited 2 with ``invalid value 'github' for '--format'``. Pinning to 1.24.1 (the version verified locally) locks the CLI surface. The ``paths:`` filter that scoped the workflow to ``.github/workflows`` changes is dropped — the renamed file runs every PR, which is the right trigger now that the security audit has a broader scope. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Zhengyuan Su <su.zhengyuan@u.nus.edu>
Bumps the zizmor invocation in ``security.yml`` from
``--persona regular`` to ``--persona pedantic`` (the strictest level
whose findings are actually addressable for this project) and closes
every finding pedantic surfaces:
* ``anonymous-definition`` (7×, info) — every existing job lacks a
human-readable ``name:`` field. Adds one to each.
* ``template-injection`` (2×, low) — ``check-signoff`` interpolated
``${{ github.event.pull_request.{base,head}.sha }}`` directly into
a shell ``run:`` block. Moved to an ``env:`` mapping so the values
pass through as ordinary shell env vars and never reach
template-expansion in the script body.
Why pedantic, not auditor: ``auditor`` adds 7 ``self-hosted-runner``
warnings — one per job — flagging the use of self-hosted infra at all.
That's an architectural choice for this project, not a bug. Pedantic
catches every actionable issue without that noise.
After this commit, ``zizmor --persona pedantic .github/workflows``
reports ``No findings to report``. The 7 still-suppressed entries are
auditor-only audits, not project-side suppressions; ``.github/zizmor.yml``
remains absent.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Zhengyuan Su <su.zhengyuan@u.nus.edu>
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.
Purpose
Add
zizmorto CI — a static auditor for GitHub Actions workflows. Lives in.github/workflows/security.yml, so future security tools (gitleaks, bandit, pip-audit) land as additional jobs in the same file.Changes
.github/workflows/security.yml— new workflow with azizmorjob. Runsuvx --from zizmor==1.24.1 zizmor --persona regular --format github .github/workflowson every PR and on push tomain. Pinned to1.24.1because unpinneduvx zizmorresolved to a pre-1.x version in CI that didn't accept--format github.permissions: contents: read,with: persist-credentials: falseon every checkout, everyuses:pinned to its content SHA with a# vNcomment.Pinned actions:
actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6astral-sh/setup-uv@94527f2e458b27549849d47d273a16bec83a01e9 # v7No suppressions: no
.github/zizmor.yml, no per-rule disables.Design
SHA pinning was the actual recommendation behind the
unpinned-usesaudit; pin rather than suppress. The# vNcomment after each SHA preserves readability for reviewers. Thezizmorcore/zizmor-actionwas rejected — it requires Docker-in-Docker, which the self-hosted runner container doesn't provide.Test Plan
Test Result
The 22 suppressed entries are zizmor's own built-in default-disabled audits at the
regularpersona; no project-side suppression.Pre-submission Checklist
CONTRIBUTING.md(orAGENTS.mdif noCONTRIBUTING.md).uv run pre-commit run --all-filesand fixed any issues.[BREAKING]and described migration steps above.