Help us improve
Share bugs, ideas, or general feedback.
From alexop-skills
Whenever you create or substantially edit a GitHub Actions workflow (.github/workflows/*.yml), run `uvx zizmor --persona pedantic` on it and fix every finding before considering the file done. Use this any time you add a new workflow, paste a template workflow (e.g. the Claude GitHub App, a marketplace action, a generated CI/CD file), or change `uses:`, `permissions:`, or trigger blocks — even if the user didn't mention security or zizmor. A workflow that hasn't been run through the pedantic audit is not finished.
npx claudepluginhub alexanderop/skillsHow this skill is triggered — by the user, by Claude, or both
Slash command
/alexop-skills:harden-github-actionsThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
`zizmor` is a static analyzer for GitHub Actions workflows. New workflow files —
Creates p5.js generative art with seeded randomness, noise fields, and interactive parameter exploration. Use for algorithmic art, flow fields, or particle systems.
Share bugs, ideas, or general feedback.
zizmor is a static analyzer for GitHub Actions workflows. New workflow files —
especially ones pasted from templates or generated by an app — almost always ship
with insecure defaults (unpinned actions, no permissions boundary, credential
persistence). If the repo runs zizmor in CI, those defaults fail the pipeline; even
if it doesn't, they're real supply-chain and token-leak risks. So treat the audit as
part of writing the workflow, not a separate review step that happens later.
Run the audit whenever you have:
.github/workflows/.uses:, permissions:, on:, or concurrency:.You don't need the user to ask for "security" or mention "zizmor" — hardening is the default expectation for any workflow you touch.
Match the repo's existing conventions first. Before inventing hardening, read a
workflow in the same repo that already passes (often ci.yml or codeql.yml). Copy
its action pins, its permission-comment style, its concurrency group naming. A
consistent repo is easier to review than a "correct" but idiosyncratic file. Use
grep -nE "uses:|permissions:|persist-credentials|concurrency:" .github/workflows/*.yml
to see what's already established.
Run the audit on the file(s) you created or changed:
uvx zizmor --persona pedantic .github/workflows/<file>.yml
--persona pedantic is the strictest mode and is what hardened repos tend to enforce
in CI, so matching it locally avoids a surprise pipeline failure. Pass every file you
touched. Exit code 0 with "No findings to report" means done.
Fix every finding, not just the error-level ones. The pedantic persona reports
informational findings too, and a CI gate with no min-severity set fails on any
finding. The common ones and their fixes are below.
Re-run until clean (exit 0). Then you're done.
These are the recurring ones. Apply the fix that matches how the rest of the repo does it.
| Finding | Why it matters | Fix |
|---|---|---|
| unpinned action reference (error) | A mutable tag like @v4 can be repointed at malicious code; pinning to a commit SHA is immutable. | Pin to the full commit SHA with the tag in a trailing comment: uses: actions/checkout@34e1148… # v4. Resolve the SHA for an action (see below). |
| overly broad permissions | Default GITHUB_TOKEN permissions are write-all; an exploited step can push code or open releases. | Add top-level permissions: {} and grant each job only what it needs. |
| permissions without explanatory comments | Reviewers can't tell why a scope was granted. | Add an inline comment per permission line: contents: read # checkout. |
| credential persistence | actions/checkout leaves the git token on disk by default, readable by later steps. | Add persist-credentials: false # don't leave the git token on disk to the checkout with: block. |
| insufficient job-level concurrency limits | Stacked runs on the same ref waste minutes and can race. | Add a top-level concurrency: block with a group: keyed on the ref/PR and cancel-in-progress: true. |
| anonymous-definition (informational) | An unnamed job shows up with an opaque check name. | Give each job a name:. |
Lightweight tags resolve directly; annotated tags (common for @v1-style major tags)
need one extra dereference step. Use the GitHub API:
# Step 1: resolve the tag ref
gh api repos/<owner>/<repo>/git/refs/tags/<tag>
# If "object.type" is "commit", that SHA is your pin.
# If it's "tag" (annotated), dereference it:
gh api repos/<owner>/<repo>/git/tags/<tag-object-sha>
# The resulting "object.sha" (type "commit") is your pin.
Pin to that commit SHA and keep the human-readable tag in a # v1 comment so the file
stays reviewable.
uvx runs zizmor in an ephemeral environment, so nothing needs to be installed
globally. If uvx isn't available, fall back to pipx run zizmor, docker run --rm -v "$PWD":/work -w /work ghcr.io/zizmorcore/zizmor:latest, or a local zizmor.# zizmor: ignore[<rule>] comment on the offending line (with a reason) over
silencing the whole audit — that keeps the rest of the file covered and documents the
decision.