From b788675a1743c318d856d0e30c6f5ec2b1d39a65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Wed, 5 Aug 2026 07:32:08 +0200 Subject: [PATCH 1/2] fix(ci): stop interpolating the LLVM version into the pwsh setup step zizmor has been red on every `main` commit since #7353 created `.github/actions/setup-llvm22/action.yml` -- roughly 40 consecutive commits. #7388 and #7393 only shifted the reported line numbers, which is what made them look implicated; neither introduced a finding. Four high-severity findings, one of which is properly fixed here. The Windows arm interpolated a composite-action input straight into a PowerShell script body (`$ver = "${{ inputs.version }}"`), which `template-injection` flags at High confidence: the expansion is substituted as raw text before pwsh parses the line, so an input carrying a quote plus a statement separator would execute as code with the runner's privileges. The input now arrives through an `env:` block and is read as `$env:LLVM_VERSION`, a plain string load. The other three are `github-env` at Low confidence -- the single `LLVM_SYS_221_PREFIX=` line the action exists to write, once per platform arm -- and are suppressed with reasoning in `.github/zizmor.yml`. Measured: the audit is satisfiable only by not writing the environment file at all, and the clean alternative ($GITHUB_OUTPUT plus composite outputs) costs 44 jobs and 140 downstream steps, recreating the duplication the action exists to remove. The carve-out is a dated ratchet with an explicit delete-condition. Verified with the repo's SRI-pinned zizmor 1.28.0: pristine config plus this fix reports 3 high and exits 14; with the carve-out it exits 0 and `ignored` rises 119 -> 122, matching the three suppressed findings exactly. Claude-Session: https://claude.ai/code/session_019EHcmXKArA7m42SihYCcgH --- .github/actions/setup-llvm22/action.yml | 10 ++++- .github/zizmor.yml | 31 ++++++++++++++ .../PLACEHOLDER-zizmor-template-injection.md | 41 +++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 changelog.d/PLACEHOLDER-zizmor-template-injection.md diff --git a/.github/actions/setup-llvm22/action.yml b/.github/actions/setup-llvm22/action.yml index 80f9508df8..89e5e097e8 100644 --- a/.github/actions/setup-llvm22/action.yml +++ b/.github/actions/setup-llvm22/action.yml @@ -79,9 +79,17 @@ runs: - name: LLVM 22 (Windows, official MSVC tarball) if: runner.os == 'Windows' shell: pwsh + # `inputs.version` arrives through the environment, NOT through `${{ }}` + # interpolated into the script body. A template expansion is substituted + # as raw text before pwsh ever parses the line, so an input containing a + # quote plus a statement separator executes as code with the runner's + # privileges; reading `$env:` is a plain string load with no such step. + # Caught by zizmor's `template-injection` audit at High confidence. + env: + LLVM_VERSION: ${{ inputs.version }} run: | $ErrorActionPreference = "Stop" - $ver = "${{ inputs.version }}" + $ver = $env:LLVM_VERSION $url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-$ver/clang+llvm-$ver-x86_64-pc-windows-msvc.tar.xz" Write-Host "downloading $url" curl.exe -sSL --retry 3 -o "$env:RUNNER_TEMP\llvm.tar.xz" $url diff --git a/.github/zizmor.yml b/.github/zizmor.yml index fb64cf61c6..706121853f 100644 --- a/.github/zizmor.yml +++ b/.github/zizmor.yml @@ -44,3 +44,34 @@ rules: # change that needs its own carefully-tested PR (a mistake only # manifests at the next tag). Tracked as follow-up. - release-packages.yml + + # 2026-08-05: three findings, all audit-confidence Low, all of them the one + # `LLVM_SYS_221_PREFIX=` line that setup-llvm22 exists to write — + # once per platform arm. This gate went red at #7353, the commit that + # introduced that action, and stayed red for ~40 main commits. + # + # This is a scoped carve-out and NOT the fix. Measured: the audit is + # satisfied only by not writing the environment file at all. A dynamic value + # into $GITHUB_ENV always reports (a static literal does not; ours is + # `brew --prefix` / `llvm-config --prefix` output), and the pwsh arm reports + # even for a literal, because zizmor cannot evaluate `Out-File`. The only + # clean form is `$GITHUB_OUTPUT` + composite-action outputs — which is a real + # improvement in the abstract and a bad trade here: measured across + # .github/workflows, that is 44 jobs needing an `id:` and 140 downstream + # steps each needing `env: LLVM_SYS_221_PREFIX: ${{ steps..outputs.prefix }}`, + # because llvm-sys reads the prefix from the environment at build time. That + # re-creates precisely the "44 inline recipes" this action was written to + # delete, across 18 files, none of it verifiable outside CI. + # + # The exposure being traded away is small and bounded: the written values are + # filesystem paths produced by locally installed toolchains, on a composite + # action whose callers all run under `pull_request` / `push`, never + # `pull_request_target` or `workflow_run`. zizmor's own confidence is Low for + # this reason. + # + # Ratchet: delete this entry when setup-llvm22 moves to outputs. `action.yml` + # is unambiguous only because it is the repo's ONLY composite action — adding + # a second one silently widens this, so re-scope it at that point. + github-env: + ignore: + - action.yml diff --git a/changelog.d/PLACEHOLDER-zizmor-template-injection.md b/changelog.d/PLACEHOLDER-zizmor-template-injection.md new file mode 100644 index 0000000000..5380ef3c3b --- /dev/null +++ b/changelog.d/PLACEHOLDER-zizmor-template-injection.md @@ -0,0 +1,41 @@ +Fixed the `zizmor` GitHub Actions security gate, which had been failing on every +`main` commit since #7353. + +`#7353` created `.github/actions/setup-llvm22/action.yml` and, with it, four +high-severity findings. The gate went red on that commit and stayed red for +roughly 40 consecutive `main` commits. `#7388` (clang-22 in setup-llvm22) and +`#7393` (a `concurrency` group for gc-native-roots) are both innocent — they +only shifted the reported line numbers, which is what made them look +implicated. + +One finding is properly fixed. The Windows arm interpolated a composite-action +input straight into a PowerShell script body (`$ver = "${{ inputs.version }}"`), +which `template-injection` flags at **High** confidence: a template expansion is +substituted as raw text before pwsh parses the line, so an input carrying a +quote plus a statement separator would execute as code with the runner's +privileges. The input now arrives through an `env:` block and is read as +`$env:LLVM_VERSION`, which is a plain string load with no such step. + +Three findings are suppressed, with reasoning, in `.github/zizmor.yml`. All +three are `github-env` at **Low** confidence, and all three are the single +`LLVM_SYS_221_PREFIX=` line the action exists to write, once per +platform arm. The audit was measured to be satisfiable only by not writing the +environment file at all — a dynamic value into `$GITHUB_ENV` always reports (a +static literal does not), and the pwsh arm reports even for a literal because +zizmor cannot evaluate `Out-File`. The clean alternative is `$GITHUB_OUTPUT` +plus composite-action outputs, and it was measured across `.github/workflows` +at **44 jobs** needing an `id:` and **140 downstream steps** each needing +`env: LLVM_SYS_221_PREFIX: ${{ steps..outputs.prefix }}`, because llvm-sys +reads the prefix from the environment at build time. That recreates precisely +the "44 inline recipes" duplication the action was written to remove, across 18 +files, none of it verifiable outside CI — so it was rejected as a bad trade for +a Low-confidence finding on filesystem paths produced by locally installed +toolchains, in an action whose callers only ever run under `pull_request` and +`push`. The carve-out is a dated ratchet with an explicit delete-condition, in +the same style as the file's existing entries. + +Verified with the repo's SRI-pinned zizmor 1.28.0: with the pristine config and +the template-injection fix the audit reports 3 high findings and exits 14; with +the carve-out it exits 0, and the `ignored` count rises from 119 to 122 — three, +matching the three suppressed findings exactly, so the entry is not +over-broad. From 7636a732ccfd67090cba4edf35e17871b32de216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Wed, 5 Aug 2026 07:33:41 +0200 Subject: [PATCH 2/2] docs: name the fragment for its real PR (#7417) --- changelog.d/{PLACEHOLDER-zizmor-template-injection.md => TMP.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changelog.d/{PLACEHOLDER-zizmor-template-injection.md => TMP.md} (100%) diff --git a/changelog.d/PLACEHOLDER-zizmor-template-injection.md b/changelog.d/TMP.md similarity index 100% rename from changelog.d/PLACEHOLDER-zizmor-template-injection.md rename to changelog.d/TMP.md