fix(packaging/windows): a release must not sign itself with a throwaway cert
Both pack scripts fell back to a fresh `New-SelfSignedCertificate` whenever MSIX_CERT_PFX_B64 was absent, and said nothing about it beyond one Write-Host. That fallback is right for canary and dev builds. On a tag it is not: an ephemeral cert is regenerated per build, so nobody can pin it, and a release signed with one is indistinguishable from a release signed by whoever else got to the artifact. A secret that got renamed, rotated away, or simply wasn't inherited by a new workflow would have downgraded a real release silently and shipped it. pack-msix.ps1 and pack-host-installer.ps1 now resolve -RequireSignedCert (default 'auto' = true iff GITHUB_REF is refs/tags/v*) and throw instead of falling back; pack-host-installer.ps1 also refuses -NoSign on the same condition. Reading GITHUB_REF inside the scripts rather than taking a workflow flag means a future packaging workflow inherits the guard instead of having to remember it. No effect on today's releases: MSIX_CERT_PFX_B64 / MSIX_CERT_PASSWORD are both set as repo secrets, so the supplied-cert branch is the one tag builds take. This only closes the trapdoor underneath it. Verified on the windows-amd64 runner: both files parse, and the guard resolves true for refs/tags/v0.21.0, false for refs/heads/main and for an unset ref, with 'true'/'false' overriding as intended. Not touched: the pf-vdisplay / gamepad driver builds use the same pattern with DRIVER_CERT_PFX_B64, which has no secret behind it — those need attestation signing (a Partner Center action), and a guard there would just fail every tag. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,9 @@
|
||||
.DESCRIPTION
|
||||
From a release `cargo build -p punktfunk-host --features nvenc` output (the exe), this:
|
||||
1. resolves a code-signing cert (supplied stable .pfx from CI secrets OR an ephemeral self-signed
|
||||
CN=unom - same scheme as the client's pack-msix.ps1) and exports the public .cer,
|
||||
CN=unom - same scheme as the client's pack-msix.ps1) and exports the public .cer. The
|
||||
ephemeral fallback is for canary/CI/dev ONLY: on a v* tag build a missing cert (or -NoSign)
|
||||
is a hard failure, never a silent downgrade to a throwaway cert - see -RequireSignedCert,
|
||||
2. signs the inner punktfunk-host.exe,
|
||||
3. stages the pf-vdisplay virtual-display driver bundle (unless -NoDriver),
|
||||
4. runs ISCC to build punktfunk-host-setup-<ver>.exe,
|
||||
@@ -31,7 +33,9 @@ param(
|
||||
[string]$BunExe = $env:BUN_EXE, # portable bun.exe runtime for the console + runner
|
||||
[string]$VbCableDir = $env:VBCABLE_DIR, # official base VB-CABLE package -> bundle the virtual mic
|
||||
[switch]$NoDriver, # build without the bundled pf-vdisplay driver
|
||||
[switch]$NoSign # skip signing (local debug)
|
||||
[switch]$NoSign, # skip signing (local debug)
|
||||
# 'auto' (default) = required iff this is a v* tag build; 'true'/'false' to force. See below.
|
||||
[ValidateSet('auto', 'true', 'false')][string]$RequireSignedCert = 'auto'
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
@@ -71,6 +75,17 @@ $iscc = Find-Iscc
|
||||
Write-Host "ISCC: $iscc"
|
||||
|
||||
# --- signing cert (supplied stable pfx OR ephemeral self-signed) -----------------------------
|
||||
# FAIL CLOSED on a real release. The ephemeral fallback below exists so canary/CI/dev builds keep
|
||||
# working without the secret, but it is a per-build throwaway cert: nobody can pin it, and an
|
||||
# installer signed with one is indistinguishable from one signed by an attacker. Silently falling
|
||||
# back on a tag build would ship exactly that to users under the release's name — so on refs/tags/v*
|
||||
# a missing MSIX_CERT_PFX_B64 (or -NoSign) is a build failure, not a downgrade. ('auto' resolves from
|
||||
# GITHUB_REF so a workflow can't forget to opt in; -RequireSignedCert true/false overrides.)
|
||||
$requireCert = if ($RequireSignedCert -eq 'auto') { $env:GITHUB_REF -like 'refs/tags/v*' }
|
||||
else { [Convert]::ToBoolean($RequireSignedCert) }
|
||||
if ($NoSign -and $requireCert) {
|
||||
throw "release build ($env:GITHUB_REF) with -NoSign — refusing to publish an unsigned installer."
|
||||
}
|
||||
$pfxPath = Join-Path $OutDir 'signing.pfx'
|
||||
$cerPath = Join-Path $OutDir "punktfunk-host-windows_${Version}.cer"
|
||||
$signtool = $null
|
||||
@@ -81,6 +96,11 @@ if (-not $NoSign) {
|
||||
Write-Host "signing with supplied code-signing cert (MSIX_CERT_PFX_B64)"
|
||||
[IO.File]::WriteAllBytes($pfxPath, [Convert]::FromBase64String($PfxBase64))
|
||||
}
|
||||
elseif ($requireCert) {
|
||||
throw ("release build ($env:GITHUB_REF) with no MSIX_CERT_PFX_B64 — refusing to fall back to " +
|
||||
"an ephemeral self-signed cert. Restore the MSIX_CERT_PFX_B64 / MSIX_CERT_PASSWORD " +
|
||||
"repo secrets, or pass -RequireSignedCert false if this really is a test build.")
|
||||
}
|
||||
else {
|
||||
Write-Host "no MSIX_CERT_PFX_B64 -> generating an ephemeral self-signed cert (subject $Publisher)"
|
||||
if (-not $PfxPassword) { $PfxPassword = 'punktfunk' }
|
||||
|
||||
Reference in New Issue
Block a user