Skip to content
Latchkey

zizmor: Audit GitHub Actions for Security Issues

zizmor statically audits GitHub Actions workflows and reusable actions for security problems like template injection, unpinned actions, and dangerous pull_request_target usage.

actionlint checks that a workflow is valid; zizmor checks that it is safe. It catches classes of supply-chain and injection bugs that a schema linter cannot see.

What it does

zizmor runs a set of audits over workflow YAML and composite actions, reporting findings by severity and confidence. It detects ${{ }} template injection into run: blocks, actions pinned by tag instead of SHA, pull_request_target with checkout of untrusted code, and excessive permissions.

Common usage

Terminal
zizmor .github/workflows/ci.yml   # audit one workflow
zizmor .github/workflows/          # audit a directory
zizmor --format sarif . > zizmor.sarif
zizmor --min-severity high .       # only high-severity findings

Options

FlagWhat it does
--format <fmt>Output format: plain, json, or sarif
--min-severity <level>Filter findings below this severity (low/medium/high)
--min-confidence <level>Filter findings below this confidence
--persona <persona>auditor, pedantic, or regular sensitivity
--offlineSkip audits that need network (e.g. online SHA checks)
--gh-token <token>GitHub token for online audits

In CI

Run zizmor in a PR check with --format sarif and upload the SARIF to GitHub code scanning so findings show inline. Combine with pinning tools (pinact, ratchet) so the "unpinned action" findings get fixed automatically.

Common errors in CI

Findings read like template-injection and unpinned-uses: action is not pinned to a hash with a severity and a code frame. error: failed to load ...: invalid workflow means the YAML is malformed (run actionlint first). Online audits that hit the API rate limit report github api error and are fixed with --gh-token or --offline.

Using this in CI

A runner shell is not a login shell. It does not read your dotfiles, it usually has no TTY, and by default it does not stop on the first error, so a failing command in the middle of a multi-line run block can leave the job green.

.github/workflows/ci.yml
# make the shell behave the way you assume it does
- name: Build
  shell: bash
  run: |
    set -euo pipefail    # exit on error, undefined vars, and pipeline failures
    ./do-the-thing | tee out.log

Frequently asked questions

zizmor: Audit GitHub Actions for Security Issues?
actionlint checks that a workflow is valid; zizmor checks that it is safe. It catches classes of supply-chain and injection bugs that a schema linter cannot see.
What it does?
zizmor runs a set of audits over workflow YAML and composite actions, reporting findings by severity and confidence. It detects ${{ }} template injection into run: blocks, actions pinned by tag instead of SHA, pull_request_target with checkout of untrusted code, and excessive permissions.
In CI?
Run zizmor in a PR check with --format sarif and upload the SARIF to GitHub code scanning so findings show inline. Combine with pinning tools (pinact, ratchet) so the "unpinned action" findings get fixed automatically.
Common errors in CI?
Findings read like template-injection and unpinned-uses: action is not pinned to a hash with a severity and a code frame. error: failed to load ...: invalid workflow means the YAML is malformed (run actionlint first). Online audits that hit the API rate limit report github api error and are fixed with --gh-token or --offline.

Related guides

References

Run this faster and cheaper on Latchkey managed runners - self-healing included. Start free → 30-day trial · No credit card