Summary
The unpinned-tools audit flags steps that are syntactically if: false — i.e., guaranteed never to execute. In repos that use a composite action as a declarative allowlist registry (where every - uses: entry is if: false purely so dependabot tracks the SHA for inclusion in an approved-actions list), this produces a false positive: the audit's premise ("when this step runs, it'll install an unpinned tool") doesn't hold for steps that don't run.
Reproducer
Minimal composite action:
runs:
using: "composite"
steps:
- uses: 1Password/load-secrets-action@<sha> # v4.0.0
if: false
zizmor v1.25.2 raises warning[unpinned-tools] here, even though the step has no runtime effect.
Real-world example: https://github.com/apache/infrastructure-actions/blob/main/.github/actions/for-dependabot-triggered-reviews/action.yml — 105 if: false entries serving as the approved-actions allowlist; one currently triggers unpinned-tools.
Suggested handling
Skip unpinned-tools (and arguably similar runtime-behavior audits — bot-conditions, cache-poisoning, etc.) on steps where if is a statically-falsey literal: if: false, if: ${{ false }}. Detecting more sophisticated never-runs conditions (e.g. if: ${{ github.actor == 'never-this-user' }}) is harder and out of scope; literal-false detection alone covers the common allowlist-registry pattern.
This wouldn't weaken coverage of any step that could actually run.
Workaround in the meantime
Inline # zizmor: ignore[unpinned-tools] per offending step — works, but each new entry that triggers the audit needs its own ignore, which scales poorly across allowlists of 100+ entries.
Happy to discuss / send a PR if there's interest.
Summary
The
unpinned-toolsaudit flags steps that are syntacticallyif: false— i.e., guaranteed never to execute. In repos that use a composite action as a declarative allowlist registry (where every- uses:entry isif: falsepurely so dependabot tracks the SHA for inclusion in an approved-actions list), this produces a false positive: the audit's premise ("when this step runs, it'll install an unpinned tool") doesn't hold for steps that don't run.Reproducer
Minimal composite action:
zizmor v1.25.2 raises
warning[unpinned-tools]here, even though the step has no runtime effect.Real-world example: https://github.com/apache/infrastructure-actions/blob/main/.github/actions/for-dependabot-triggered-reviews/action.yml — 105
if: falseentries serving as the approved-actions allowlist; one currently triggersunpinned-tools.Suggested handling
Skip
unpinned-tools(and arguably similar runtime-behavior audits —bot-conditions,cache-poisoning, etc.) on steps whereifis a statically-falsey literal:if: false,if: ${{ false }}. Detecting more sophisticated never-runs conditions (e.g.if: ${{ github.actor == 'never-this-user' }}) is harder and out of scope; literal-false detection alone covers the common allowlist-registry pattern.This wouldn't weaken coverage of any step that could actually run.
Workaround in the meantime
Inline
# zizmor: ignore[unpinned-tools]per offending step — works, but each new entry that triggers the audit needs its own ignore, which scales poorly across allowlists of 100+ entries.Happy to discuss / send a PR if there's interest.