Skip to content

Layer 2 — Static Analysis with zizmor

SHA pinning (Layer 1) only enforces pinning. zizmor catches the rest: template injection, excessive permissions, credential persistence, dangerous triggers, ref/version mismatches, and secrets misuse. Running it centrally also gives you the audit data you need before enabling Layer 1.

Step 1 — Create the shared workflows repo

Section titled “Step 1 — Create the shared workflows repo”

Create platform-org/security-workflows with internal visibility:

platform-org/security-workflows/
├── .github/
│ └── workflows/
│ ├── zizmor-audit.yml # the reusable audit workflow
│ └── zizmor-autofix.yml # weekly auto-fix that opens PRs
└── zizmor.yml # shared zizmor config

Under Settings → Actions → General → Access, set Accessible from repositories in the organization so other repos can call these workflows.

.github/workflows/zizmor-audit.yml:

name: zizmor audit
on:
pull_request:
branches: ['**']
push:
branches: [main]
permissions: {}
jobs:
zizmor:
name: Run zizmor
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write # upload SARIF
actions: read
steps:
- name: Checkout
uses: actions/checkout@<SHA> # pin this
with:
persist-credentials: false
- name: Run zizmor
uses: zizmorcore/zizmor-action@<SHA> # pin this
with:
advanced-security: true # uploads SARIF to Code Scanning

zizmor.yml (consumed by the action automatically when present):

rules:
unpinned-uses:
config:
policies:
# Allow tag pinning for first-party and trusted internal actions
"actions/*": ref-pin
"github/*": ref-pin
"your-org/*": ref-pin
# Everything else: hash-pin
"*": hash-pin
template-injection:
ignore: []
excessive-permissions:
ignore: []

A weekly workflow that runs zizmor --fix=all and opens a PR:

name: zizmor auto-fix
on:
schedule:
- cron: '0 6 * * 1'
workflow_dispatch:
permissions: {}
jobs:
fix:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@<SHA>
with:
persist-credentials: false
- name: Install zizmor
run: |
curl -LsSf https://github.com/zizmorcore/zizmor/releases/latest/download/zizmor-installer.sh | sh
- name: Run auto-fix
run: zizmor --fix=all .
- name: Open PR
uses: peter-evans/create-pull-request@<SHA>
with:
token: ${{ secrets.ZIZMOR_PAT }} # PAT with `workflow` scope
commit-message: 'chore: apply zizmor --fix=all'
branch: chore/zizmor-autofix
title: 'chore: apply zizmor security fixes'
body: |
Automated SHA-pinning and security fixes from zizmor.
Review the diff carefully — especially shell quoting on Windows runners.

Before pushing, audit and auto-fix from your machine:

Terminal window
brew install zizmor # or: cargo install zizmor
zizmor .github/workflows/
zizmor --fix=all .github/workflows/

Once this workflow runs on every PR, require it as a merge gate in Layer 3.