Files
Triple-C/scripts/windows-verify-signatures.ps1
T
shadowdaoandClaude Opus 5.5 cc274f39a8
Secret Scan / scan (push) Successful in 8s
Build App (Preview) / compute-version (pull_request) Successful in 12s
Secret Scan / scan (pull_request) Successful in 5s
Build App (Preview) / create-release (pull_request) Successful in 1s
Build App (Preview) / build-macos (pull_request) Successful in 2m48s
Build App (Preview) / test (pull_request) Successful in 3m41s
Build App (Preview) / build-linux (pull_request) Successful in 7m8s
Build App (Preview) / build-windows (pull_request) Failing after 8m53s
Build App (Preview) / prune-previews (pull_request) Skipped
fix(ci): pass the sign command with --config and prove the uninstaller was signed
The first signing run built nothing signed, and "Verify signatures" failed
it as intended. The Tauri 2 CLI never reads TAURI_CONFIG. It only sets that
variable for tauri-build, so the sign command was dropped silently, just as
the inline beforeBuildCommand override had been for as long as the Windows
jobs have set it. The setup now writes a config file, and the build passes
it with `cargo tauri build --config`.

Review follow-ups:
- The NSIS uninstaller is written to %TEMP% and signed from 32-bit makensis.
  SYSTEM's %TEMP% sits under System32, which WOW64 redirects for makensis but
  not for the x64 signtool, so they would disagree about where the file is.
  %TEMP% and %TMP% now point into the workspace. makensis ignores the sign
  command's exit code for the uninstaller, so windows-sign.ps1 logs every
  file it signs, and the verify step requires a logged signature under that
  temp directory.
- The signing client is pinned by SHA-512, so the pin can be checked against
  nuget.org's published packageHash.
- tauri-cli on Windows is pinned to =2.11.0 --locked, the @tauri-apps/cli
  version the Linux and macOS jobs run from the lockfile, instead of "^2".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 18:05:03 -07:00

67 lines
3.0 KiB
PowerShell

# windows-verify-signatures.ps1 <path-or-wildcard>... - fail unless every file
# carries a valid, timestamped Authenticode signature.
#
# The check that makes signing load-bearing rather than hopeful: Tauri skips
# signing silently in some configurations (no sign command, --no-sign), and an
# unsigned installer looks exactly like a signed one until SmartScreen blocks
# it on a user's machine. Every pattern must match at least one file, so a
# bundle that was never produced cannot pass either.
param([Parameter(Mandatory = $true, ValueFromRemainingArguments = $true)][string[]]$Patterns)
$ErrorActionPreference = 'Stop'
if (-not $env:TRIPLE_C_SIGNTOOL) { throw 'TRIPLE_C_SIGNTOOL is not set - run windows-signing-setup.ps1 first' }
$files = foreach ($pattern in $Patterns) {
$found = @(Get-ChildItem -Path $pattern -File -ErrorAction SilentlyContinue)
if ($found.Count -eq 0) { throw "Nothing to verify matches $pattern" }
$found
}
$failed = @()
foreach ($file in $files) {
# signtool's own check: chain to a trusted root under the default
# Authenticode policy.
# Stop relaxed for the native call, as in windows-sign.ps1.
$ErrorActionPreference = 'Continue'
$verifyOutput = & $env:TRIPLE_C_SIGNTOOL verify /pa $file.FullName 2>&1 | ForEach-Object { "$_" }
$signtoolOk = ($LASTEXITCODE -eq 0)
$ErrorActionPreference = 'Stop'
if (-not $signtoolOk) { $verifyOutput | Write-Host }
# And the timestamp, which signtool verify does not require.
$sig = Get-AuthenticodeSignature -FilePath $file.FullName
$timestamped = $null -ne $sig.TimeStamperCertificate
if ($signtoolOk -and $sig.Status -eq 'Valid' -and $timestamped) {
Write-Host "OK $($file.Name) - $($sig.SignerCertificate.Subject)"
} else {
Write-Host "FAIL $($file.Name) - status $($sig.Status), signtool $(if ($signtoolOk) {'ok'} else {'failed'}), timestamped $timestamped"
$failed += $file.Name
}
}
# The NSIS uninstaller is signed from inside makensis, which ignores the sign
# command's exit code, and it ends up embedded in the installer where the
# checks above cannot reach it. windows-sign.ps1 logs every file it signs; the
# uninstaller is the one makensis wrote under the job's temp directory (see
# windows-signing-setup.ps1), so require at least one logged path there.
$nsisBuilt = @($files | Where-Object { $_.FullName -match '\\bundle\\nsis\\' }).Count -gt 0
if ($nsisBuilt) {
$tmp = $env:TRIPLE_C_SIGN_TMP
$log = $env:TRIPLE_C_SIGN_LOG
$signedInTmp = @()
if ($tmp -and $log -and (Test-Path $log)) {
$signedInTmp = @(Get-Content $log | Where-Object { $_.StartsWith($tmp, [StringComparison]::OrdinalIgnoreCase) })
}
if ($signedInTmp.Count -eq 0) {
Write-Host 'FAIL NSIS uninstaller - no successful signature was logged for it'
$failed += 'NSIS uninstaller'
} else {
Write-Host "OK NSIS uninstaller - signed as $($signedInTmp[-1])"
}
}
if ($failed.Count -gt 0) { throw "Not validly signed: $($failed -join ', ')" }
Write-Host "All $(@($files).Count) files are signed and timestamped."