From c198de795f32724e506bac3997f29a3fd30f601c Mon Sep 17 00:00:00 2001 From: shujaat hasan Date: Mon, 31 Aug 2026 14:02:48 +0200 Subject: [PATCH] fix(install): a VERIFIED signature no longer kills the Windows install cosign writes "Verified OK" to STDERR on the SUCCESS path. Windows PowerShell 5.1 turns native-command stderr into a NativeCommandError record, and this installer runs under `$ErrorActionPreference = 'Stop'` (line 27), so that record is TERMINATING -- the install dies at the exact moment verification passes. `2>$null` does not save it: 5.1 raises the record before the redirection applies. Measured on a real Windows 11 box against the published v0.10.17 installer: Verifying cosign signature... cosign.exe : Verified OK + & $cosign verify-blob ` + FullyQualifiedErrorId : NativeCommandError ...and nothing was installed. So `tracebloc` cannot currently be installed on Windows at all. It also takes tracebloc/client's installer with it: Step 4 fails, which drops Step 5 to manual sign-in and Step 6 to the hand-typed credential prompt -- observed on the same machine. INVISIBLE ON POWERSHELL 7, which is why it survived: pwsh 7 does not make error records out of native stderr, so this passes wherever it is tested and fails on the Windows default. Verified both ways here. THIS FILE ALREADY HELD THE FIX, 230 LINES UP. `Test-CosignRuns` opens exactly this `$ErrorActionPreference = 'Continue'` window for `cosign version` and explains why. The idiom was simply never applied to the call that matters. Not piped to Out-Null: `$LASTEXITCODE` is the entire verdict here and piping a native command through a cmdlet is a documented way to lose it. The window makes the record non-terminating; `2>$null` still discards the text, so a real verification failure is unchanged -- it still exits 1. Fixes the CLASS, not the instance: install-ps1-verify.sh now asserts that every `& $...` invocation sits inside an ErrorActionPreference window (2 calls, 2 windows), with a vacuity floor so it cannot pass by finding none. Confirmed it reddens when the window is removed. scripts/tests/install-ps1-verify.sh: 11 passed. shellcheck clean. (The one behavioural-tier failure is pre-existing and environmental -- it finds a real cosign on this machine's PATH; identical on an unmodified tree.) Co-Authored-By: Claude Opus 5 --- scripts/install.ps1 | 51 +++++++++++++++++++++++++---- scripts/tests/install-ps1-verify.sh | 27 +++++++++++++++ 2 files changed, 72 insertions(+), 6 deletions(-) diff --git a/scripts/install.ps1 b/scripts/install.ps1 index 18042dc..85d3e1c 100644 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -464,12 +464,51 @@ try { # pass. Only cosign actually completing can bring it back to 0. That is # the whole guarantee behind RFC-0001 R8, and it was one line away. $global:LASTEXITCODE = 255 - & $cosign verify-blob ` - --certificate-identity-regexp "https://github.com/$GitHubRepo/.github/workflows/release.yml@refs/tags/v.*" ` - --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' ` - --certificate (Join-Path $tmpDir "$binaryFile.cert") ` - --signature (Join-Path $tmpDir "$binaryFile.sig") ` - (Join-Path $tmpDir $binaryFile) 2>$null + # AND THE STDERR WINDOW, WITHOUT WHICH SUCCESS ITSELF IS FATAL. + # + # cosign writes "Verified OK" to STDERR on the SUCCESS path. Windows + # PowerShell 5.1 turns any native-command stderr into a + # NativeCommandError record, and this script runs under + # `$ErrorActionPreference = 'Stop'` (line 27) -- so the record is + # TERMINATING and the installer dies at the moment verification + # passes. `2>$null` does not save it: 5.1 raises the record before + # the redirection applies. Measured on a real Windows 11 box against + # the published v0.10.17 installer: + # + # Verifying cosign signature... + # cosign.exe : Verified OK + # + & $cosign verify-blob ` + # + FullyQualifiedErrorId : NativeCommandError + # + # ...and nothing was installed. So `tracebloc client` cannot be + # installed on Windows at all, and tracebloc/client's installer + # Step 4 fails with it, which drops Step 5 to manual sign-in and + # Step 6 to the hand-typed credential prompt. + # + # INVISIBLE ON POWERSHELL 7, which is why it survived: pwsh 7 does + # not make error records out of native stderr, so it passes there + # and fails on the Windows default. + # + # THIS FILE ALREADY HAD THE FIX, 230 LINES UP. `Test-CosignRuns` + # opens exactly this window for `cosign version` and says why. The + # idiom was simply never applied to the call that matters. + # + # NOT piped to Out-Null: `$LASTEXITCODE` is the entire verdict here + # and piping a native command through a cmdlet is a documented way + # to lose it. The window makes the record non-terminating; `2>$null` + # still discards the text. + $prevEap = $ErrorActionPreference + try { + $ErrorActionPreference = 'Continue' + & $cosign verify-blob ` + --certificate-identity-regexp "https://github.com/$GitHubRepo/.github/workflows/release.yml@refs/tags/v.*" ` + --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' ` + --certificate (Join-Path $tmpDir "$binaryFile.cert") ` + --signature (Join-Path $tmpDir "$binaryFile.sig") ` + (Join-Path $tmpDir $binaryFile) 2>$null + } finally { + $ErrorActionPreference = $prevEap + } if ($LASTEXITCODE -ne 0) { # No TRACEBLOC_ALLOW_UNVERIFIED branch here, deliberately. # Verification RAN and said no. diff --git a/scripts/tests/install-ps1-verify.sh b/scripts/tests/install-ps1-verify.sh index 671c99c..893610d 100755 --- a/scripts/tests/install-ps1-verify.sh +++ b/scripts/tests/install-ps1-verify.sh @@ -134,6 +134,33 @@ else bad 'a post-cosign artifact fetch is missing its size cap' fi + +# ── 5d. every native call sits in a stderr window (cli, Windows PowerShell 5.1) +# THE SUCCESS PATH WAS FATAL. cosign writes "Verified OK" to STDERR when a +# signature VERIFIES. Windows PowerShell 5.1 turns native-command stderr into a +# NativeCommandError record, and this installer runs under +# `$ErrorActionPreference = 'Stop'`, so that record TERMINATES the run at the +# moment verification succeeds. `2>$null` does not prevent it -- 5.1 raises the +# record before the redirection applies. Measured on a real Windows 11 box +# against the published v0.10.17 installer: "cosign.exe : Verified OK" followed +# by NativeCommandError, and nothing installed. +# +# Invisible on PowerShell 7, which does not make error records out of native +# stderr -- so it passes wherever it is tested and fails on the Windows default. +# +# THE INVARIANT, and it catches the next native call someone adds: every `& $...` +# invocation must sit inside an `$ErrorActionPreference = 'Continue'` window. +# There are exactly two (cosign version, cosign verify-blob) and there must be at +# least as many windows as calls. +native_calls=$(grep -cE '^[[:space:]]*&[[:space:]]*\$' "$INSTALLER" || true) +eap_windows=$(grep -cF "ErrorActionPreference = 'Continue'" "$INSTALLER" || true) +if [ "$native_calls" -eq 0 ]; then + bad 'no native invocation found at all -- this guard would pass vacuously' +elif [ "$eap_windows" -ge "$native_calls" ]; then + ok "every native invocation ($native_calls) sits in a stderr window ($eap_windows)" +else + bad "a native invocation runs outside an ErrorActionPreference window ($native_calls calls, $eap_windows windows) -- cosign's 'Verified OK' on stderr will terminate the install under Windows PowerShell 5.1" +fi # ── 6. behavioural tier ───────────────────────────────────────────────────── # pwsh is preinstalled on GitHub-hosted ubuntu runners. If it is missing we # cannot tell whether the helpers behave, and "cannot tell" is a finding, not a