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 configUnder Settings → Actions → General → Access, set Accessible from repositories in the organization so other repos can call these workflows.
Step 2 — Add the audit workflow
Section titled “Step 2 — Add the audit workflow”.github/workflows/zizmor-audit.yml:
name: zizmor auditon: 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 ScanningStep 3 — Add a shared config
Section titled “Step 3 — Add a shared config”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: []Step 4 (optional) — Auto-fix workflow
Section titled “Step 4 (optional) — Auto-fix workflow”A weekly workflow that runs zizmor --fix=all and opens a PR:
name: zizmor auto-fixon: 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.Run zizmor locally
Section titled “Run zizmor locally”Before pushing, audit and auto-fix from your machine:
brew install zizmor # or: cargo install zizmorzizmor .github/workflows/zizmor --fix=all .github/workflows/Once this workflow runs on every PR, require it as a merge gate in Layer 3.