From 48acf4f13b18d29273082d9f12781614ce615c35 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Sat, 30 May 2026 01:52:51 +0800 Subject: [PATCH] ci(windows): tolerate untrusted root in signature verification for test cert signtool verify /pa exits non-zero when the certificate chain terminates in an untrusted root, which is always the case for the SignPath self-signed test certificate. Add an -AllowUntrustedRoot switch to verify-authenticode.ps1 that accepts a signed-but-untrusted-root result (while still failing on unsigned or otherwise invalid files), and pass it from the workflow during test-signing. Remove the switch once signing-policy-slug moves to release-signing with a production CA-issued certificate, so release builds enforce a fully trusted chain. --- .github/workflows/build_orca.yml | 5 +++- scripts/verify-authenticode.ps1 | 39 ++++++++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build_orca.yml b/.github/workflows/build_orca.yml index 92d7eb1c32..7f7d6d72a4 100644 --- a/.github/workflows/build_orca.yml +++ b/.github/workflows/build_orca.yml @@ -323,7 +323,10 @@ jobs: - name: Verify SignPath Windows portable signatures if: github.repository == 'OrcaSlicer/OrcaSlicer' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && runner.os == 'Windows' && !vars.SELF_HOSTED shell: pwsh - run: ./scripts/verify-authenticode.ps1 -ArtifactDirectory '${{ github.workspace }}/build/signpath/windows-portable' + # -AllowUntrustedRoot is required while signing with the SignPath test + # certificate (self-signed). Remove it once signing-policy-slug switches + # to release-signing with a production CA-issued certificate. + run: ./scripts/verify-authenticode.ps1 -ArtifactDirectory '${{ github.workspace }}/build/signpath/windows-portable' -AllowUntrustedRoot - name: Replace Windows portable bundle with signed output if: github.repository == 'OrcaSlicer/OrcaSlicer' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && runner.os == 'Windows' && !vars.SELF_HOSTED diff --git a/scripts/verify-authenticode.ps1 b/scripts/verify-authenticode.ps1 index 4176c99437..676ef5d579 100644 --- a/scripts/verify-authenticode.ps1 +++ b/scripts/verify-authenticode.ps1 @@ -9,7 +9,13 @@ param( "OrcaSlicer.dll" ), - [string]$SignToolPath + [string]$SignToolPath, + + # Accept signatures whose certificate chain terminates in an untrusted root. + # Required for the SignPath test certificate (self-signed). Do NOT pass this + # once a production CA-issued certificate is in use, so release builds enforce + # a fully trusted chain. + [switch]$AllowUntrustedRoot ) $ErrorActionPreference = "Stop" @@ -64,10 +70,35 @@ foreach ($relativePath in $Files) { } Write-Host "Verifying $relativePath" - & $signtool verify /pa /all /tw /v $filePath - if ($LASTEXITCODE -ne 0) { - throw "SignTool verification failed for '$relativePath' with exit code $LASTEXITCODE." + + # Capture signtool output without letting native stderr (redirected via 2>&1) + # raise a terminating NativeCommandError under $ErrorActionPreference = 'Stop'. + $previousErrorActionPreference = $ErrorActionPreference + $ErrorActionPreference = "Continue" + try { + $output = & $signtool verify /pa /all /tw /v $filePath 2>&1 + $exitCode = $LASTEXITCODE } + finally { + $ErrorActionPreference = $previousErrorActionPreference + } + + $output | ForEach-Object { Write-Host $_ } + + if ($exitCode -eq 0) { + continue + } + + # signtool wraps the message across lines, so normalize whitespace before matching. + $normalizedOutput = (($output | Out-String) -replace "\s+", " ") + $isUntrustedRoot = $normalizedOutput -match "terminated in a root certificate which is not trusted by the trust provider" + + if ($AllowUntrustedRoot -and $isUntrustedRoot) { + Write-Host " Accepted: '$relativePath' is signed but its certificate chains to an untrusted root (expected for the SignPath test certificate)." + continue + } + + throw "SignTool verification failed for '$relativePath' with exit code $exitCode." } Write-Host "Authenticode verification passed."