From fc9812b4af3b464d0c506393a0c0c14339cedc43 Mon Sep 17 00:00:00 2001 From: "Paul Campbell (AgOS)" Date: Fri, 11 Sep 2026 15:32:43 -0700 Subject: [PATCH] fix: fail loudly when test MSIX signing has no inputs The signing script previously skipped both architecture directories when the artifact root was not CI-shaped, then reported success after creating and discarding a certificate. Validate the expected architecture directories before creating signing material so a no-op cannot look like a signed result. Add a regression test that runs the script against an empty artifact root and verifies the failure message, non-zero exit, absence of output, and absence of a newly created publisher certificate. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 97c31f3f-0c7b-43e8-b979-6418dcb7fedc --- scripts/Sign-TestMSIX.ps1 | 20 ++++++++- scripts/Test-Sign-TestMSIX.Tests.ps1 | 63 ++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 scripts/Test-Sign-TestMSIX.Tests.ps1 diff --git a/scripts/Sign-TestMSIX.ps1 b/scripts/Sign-TestMSIX.ps1 index 6e0d1162..fa1d19f9 100644 --- a/scripts/Sign-TestMSIX.ps1 +++ b/scripts/Sign-TestMSIX.ps1 @@ -29,6 +29,24 @@ if ([string]::IsNullOrWhiteSpace($publisher)) { throw 'The Gateway MSIX release policy publisher is missing.' } +$architectures = @('x64', 'arm64') +$architectureDirectories = @( + $architectures | + ForEach-Object { + $sourceDirectory = Join-Path $resolvedArtifactsDirectory $_ + if (Test-Path -LiteralPath $sourceDirectory -PathType Container) { + $sourceDirectory + } + } +) +if ($architectureDirectories.Count -eq 0) { + throw ( + "No architecture directories were found under " + + "'$resolvedArtifactsDirectory'. Expected at least one of: " + + ($architectures -join ', ') + '.' + ) +} + $signtool = Get-ChildItem ` -LiteralPath "${env:ProgramFiles(x86)}\Windows Kits\10\bin" ` -Filter 'signtool.exe' ` @@ -75,7 +93,7 @@ try { -Password $password | Out-Null - foreach ($architecture in @('x64', 'arm64')) { + foreach ($architecture in $architectures) { $sourceDirectory = Join-Path $resolvedArtifactsDirectory $architecture if (-not (Test-Path -LiteralPath $sourceDirectory -PathType Container)) { continue diff --git a/scripts/Test-Sign-TestMSIX.Tests.ps1 b/scripts/Test-Sign-TestMSIX.Tests.ps1 new file mode 100644 index 00000000..88338e1c --- /dev/null +++ b/scripts/Test-Sign-TestMSIX.Tests.ps1 @@ -0,0 +1,63 @@ +[CmdletBinding()] +param() + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +if (-not $IsWindows) { + throw 'Test MSIX signing tests require Windows.' +} + +$scriptPath = Join-Path $PSScriptRoot 'Sign-TestMSIX.ps1' +$testRoot = Join-Path $env:TEMP ( + "openclaw-sign-testmsix-$([guid]::NewGuid().ToString('N'))" +) +$artifactsDirectory = Join-Path $testRoot 'artifacts' +$outputDirectory = Join-Path $testRoot 'signed' +$publisher = 'CN=OpenClaw Foundation, O=OpenClaw Foundation, L=Mill Valley, S=California, C=US' +$thumbprintsBefore = @( + Get-ChildItem -Path 'Cert:\CurrentUser\My' | + Where-Object Subject -eq $publisher | + Select-Object -ExpandProperty Thumbprint +) + +try { + New-Item -Path $artifactsDirectory -ItemType Directory -Force | Out-Null + + $output = @( + & pwsh -NoProfile -File $scriptPath ` + -ArtifactsDirectory $artifactsDirectory ` + -OutputDirectory $outputDirectory 2>&1 + ) + $exitCode = $LASTEXITCODE + $message = $output -join [Environment]::NewLine + + if ($exitCode -eq 0) { + throw 'Signing unexpectedly succeeded without an architecture directory.' + } + if ($message -notmatch 'No architecture directories were found') { + throw "Signing failure did not identify the missing architecture directories. Output: $message" + } + if (Test-Path -LiteralPath $outputDirectory) { + throw 'Signing created an output directory despite finding no architecture directory.' + } + + $thumbprintsAfter = @( + Get-ChildItem -Path 'Cert:\CurrentUser\My' | + Where-Object Subject -eq $publisher | + Select-Object -ExpandProperty Thumbprint + ) + $newThumbprints = Compare-Object ` + -ReferenceObject $thumbprintsBefore ` + -DifferenceObject $thumbprintsAfter ` + -PassThru | + Where-Object SideIndicator -eq '=>' + if ($null -ne $newThumbprints) { + throw 'Signing created a publisher certificate on the no-op failure path.' + } + + Write-Host 'Test MSIX no-op signing regression test passed.' +} +finally { + Remove-Item -LiteralPath $testRoot -Recurse -Force -ErrorAction SilentlyContinue +}