Skip to content

zizmor: ignore[<audit>] comments emitted by generators are placed on the wrong line — suppression silently fails (regression from #37586) #38859

@microsasa

Description

@microsasa

Summary

The # zizmor: ignore[github-env] annotation that PR #37586 added to the generator in pkg/workflow/ghes_host_step.go does not actually suppress the github-env finding. Per zizmor's own documentation, an ignore comment must be placed inside the span identified by the finding (i.e. on the run: line itself or inside the script body), not on a sibling YAML line above it. The generator emits the comment one line above run:, which zizmor treats as detached.

Live evidence: the daily static-analysis-report issue at HEAD still surfaces the same github-env High on dev-hawk.lock.yml, persisting roughly 19 days after #37586 merged on 2026-06-07. The persistence is repeatedly framed as "confirm value is not attacker-controlled," but the actual root cause is that the suppression never took effect.

This pattern (PR #37586: "adds a scoped zizmor: ignore[github-env] annotation as a standalone comment line immediately above run:, matching the # zizmor: ignore[...] - <reason> pattern used elsewhere in the codebase") implies the same placement may be used by other generators too. Every compiled .lock.yml that embeds the GHES host step is currently emitting an un-suppressed github-env High.

Where the bug lives

pkg/workflow/ghes_host_step.go, the string emitted by generateGHESHostConfigurationStep():

      - name: Configure GH_HOST for enterprise compatibility
        id: ghes-host-config
        shell: bash
        # zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input.
        run: |
          GH_HOST="${GITHUB_SERVER_URL#https://}"
          GH_HOST="${GH_HOST#http://}"
          echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV"

The # zizmor: ignore[github-env] line sits as a top-level YAML comment on the step object, adjacent to but outside the run: value. zizmor parses the finding span as the multi-line block scalar belonging to run:; the comment is not within that span and therefore does not match.

What zizmor's docs say

From docs/usage.md of zizmorcore/zizmor (the "With comments" section):

An ignore comment can be placed anywhere in any span identified by a finding, so long as it can be identified as a YAML comment. In particular, this means that you can''t place an ignore comment in the middle of a string or a block literal.

The documented working pattern places the comment on the same line as the YAML key that owns the finding, e.g.:

run: | # zizmor: ignore[template-injection]
  echo "${{ github.event.issue.title }}"

The current ghes_host_step.go output does not follow that pattern -- the comment is on the prior YAML key, not on the run: line.

Root cause

  1. # zizmor: ignore[<audit>] - <reason> is the correct syntax.
  2. Placement is wrong: the comment must be on the run: line itself (or inside the script body as a shell # comment).
  3. The PR that introduced this (Resolve dev-hawk github-env highs by tightening env-file usage in generated workflow steps #37586) added a test that asserts the string is present in the generated YAML; the test does not actually run zizmor against the output to verify suppression. So the bug shipped green.

The same template-string convention is referenced as "matching the pattern used elsewhere in the codebase" in #37586''s description, suggesting other generators may have the same defect.

Minimal repro

Compile any agentic workflow that includes the GHES host step, then run zizmor against the compiled .lock.yml:

gh aw compile <workflow-name>
docker run --rm -v "$(pwd):/workdir:ro" -w /workdir \
  ghcr.io/zizmorcore/zizmor:1.25.2 \
  --min-severity high --color=never \
  .github/workflows/<workflow-name>.lock.yml

Expected (with #37586''s fix working): no github-env finding.

Actual: github-env High is reported on the echo "GH_HOST=..." >> "$GITHUB_ENV" line, despite the immediately preceding # zizmor: ignore[github-env] comment.

The same High shows up in this project''s own daily report -- see issue #38795 ("Top Priority #2: dangerous $GITHUB_ENV use ... @ dev-hawk.lock.yml:1718").

Proposed fix

Move the suppression onto the run: | line itself in pkg/workflow/ghes_host_step.go:

return `      - name: Configure GH_HOST for enterprise compatibility
        id: ghes-host-config
        shell: bash
        run: | # zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input.
          GH_HOST="${GITHUB_SERVER_URL#https://}"
          GH_HOST="${GH_HOST#http://}"
          echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV"
`

That places the comment inside the span zizmor considers part of the finding.

Suggested follow-ups

  1. Strengthen the test. The corresponding test in pkg/workflow/ghes_host_step_test.go should actually invoke zizmor against the generated YAML (or at minimum assert that the comment appears on the run: line, not on its own line), so a future regression of this exact pattern is caught before merge.

  2. Sweep other generators. Grep pkg/workflow/ for occurrences of # zizmor: ignore[ to find other generators following the same pattern:

    grep -rn ''# zizmor: ignore\['' pkg/workflow/

    Any hit whose template emits the comment on a YAML line that is a sibling of run: (or uses:) rather than on the run:/uses: line itself has the same bug.

  3. Add an integration test in static-analysis-report. When the daily run sees a finding whose source file is generator-emitted and whose immediately preceding line contains a # zizmor: ignore[<same-audit>] comment, file a higher-priority alert -- this is exactly the misplaced-suppression pattern.

References

Environment

  • zizmor: v1.25.2
  • gh-aw: v0.79.6 (also reproducible at HEAD)
  • Runner: ubuntu-latest

Why this matters

Every gh-aw-managed repo that compiles workflows including the GHES host step ships a .lock.yml with an un-suppressed github-env High finding. Downstream consumers running zizmor as a pre-merge gate (e.g. with --min-severity high) will see this finding indefinitely until the placement is corrected upstream.

Metadata

Metadata

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions