unsloth/.github/scripts/assert-bundle-signed.ps1
Daniel Han 3c6343b455
Sign the NSIS plugin DLLs, and fail the release on any unsigned file (#7819)
* Sign the NSIS plugin DLLs, and fail the release on any unsigned file

Both 0.1.51-beta and 0.1.512-beta ship four unsigned DLLs: NSISdl.dll,
System.dll, StartMenu.dll and nsDialogs.dll. Only nsis_tauri_utils.dll carries
the Unsloth AI Inc. signature.

tauri-bundler is doing the work already. When a signing identity is configured
it copies Plugins/x86-unicode aside, signs all five files listed in
NSIS_PLUGIN_FILES, and exports the copy as the NSISPLUGINS environment
variable. Nothing consumes it: no NSIS template references $%NSISPLUGINS%, and
the only plugin directory added is ADDITIONALPLUGINSPATH, which points at the
copy's additional subdirectory and holds exactly one file. So one DLL resolves
from the signed copy and the other four fall back to the default unsigned one.

Add the directory in our template, guarded so unsigned local builds still
compile, and add a check that unpacks each bundle and fails listing every
unsigned executable rather than stopping at the first.

* Fail the signing gate when a bundle cannot be unpacked

7-Zip leaves a partial tree behind on error and can fall back to its PE
handler, which yields sections rather than the payload. Both cases passed
the gate having verified nothing. Move !addplugindir above the includes so
the signed copy is registered before any plugin is packed.

* Tighten comments on the bundle signing gate

* Sign the bundled install script and gate on it

install.ps1 ships as a bundle resource and is the first thing the app runs
after install, but the bundler never signs it and the gate did not look at
scripts. Sign it before the build packs it in, and add .ps1/.psm1 to the
checked set. Verified on a Windows runner that signtool attaches a valid
signature to a .ps1; trusted-signing-cli lists ps1 as supported and does
not filter by extension.

* Tighten comments on the bundle signing gate

* Reduce the Windows false positive surface

Three changes, none of which alter behaviour:

install.rs no longer passes -WindowStyle Hidden -ExecutionPolicy Bypass to
powershell.exe. CREATE_NO_WINDOW already suppresses the console and the
NSIS-extracted script carries no mark-of-the-web, so RemoteSigned loads it.
That flag pair is the command line Microsoft ships as a detection test.

install.ps1 installs uv from a pinned, SHA-256 verified release archive
instead of evaluating a downloaded script in-process.

installer.nsi fills in CompanyName and InternalName, which upstream's
template leaves empty.

* Match astral's installer on unmanaged installs, mirrors, and the PATH probe

UV_UNMANAGED_INSTALL forces no-modify-path in astral's installer, so it must
here too, and honour UV_INSTALLER_GHE_BASE_URL / UV_INSTALLER_GITHUB_BASE_URL
so a mirrored environment still works. Every mirror serves the same asset, so
the pinned hash is unchanged; UV_DOWNLOAD_URL stays unhonoured because it
points at an arbitrary version the pin would then reject.

Record where uv actually landed. Refresh-SessionPath rebuilds PATH machine
first and drops the in-process prepend, and the recovery probe never checked
the XDG_DATA_HOME or unmanaged destinations, so a good install could still
report failure. Required by the change above, which leaves that prepend as
the only thing putting uv on PATH.

* Tighten comments on the false positive reduction changes

---------

Co-authored-by: danielhanchen <danielhanchen@users.noreply.github.com>
2026-08-04 04:12:15 -07:00

93 lines
3.6 KiB
PowerShell

# Fail if any executable inside a Windows bundle is unsigned. NSIS runs its
# plugin DLLs from $PLUGINSDIR, so a signed installer proves nothing about them.
param(
# Bundles to unpack; every PE inside is verified.
[Parameter(Mandatory = $true)][string[]] $Path,
# 7-Zip, preinstalled on windows-latest.
[string] $SevenZip = '7z',
# Known-unsigned leaf names to accept. Keep empty where possible.
[string[]] $Allow = @()
)
$ErrorActionPreference = 'Continue'
# .ps1/.psm1 included: install.ps1 ships as a bundle resource and runs on first
# launch. Authenticode covers scripts, and Smart App Control checks them.
$exeExtensions = @('.exe', '.dll', '.sys', '.ocx', '.cpl', '.scr', '.ps1', '.psm1')
$unsigned = @()
$checked = 0
foreach ($bundle in $Path) {
if (-not (Test-Path $bundle -PathType Leaf)) {
Write-Host "::error::bundle not found: $bundle"
exit 1
}
$name = Split-Path $bundle -Leaf
Write-Host ''
Write-Host "=== $name ==="
$sig = Get-AuthenticodeSignature $bundle
$checked++
if ($sig.Status -ne 'Valid') {
Write-Host " UNSIGNED $name ($($sig.Status))"
$unsigned += [pscustomobject]@{ Bundle = $name; File = $name; Status = [string]$sig.Status }
} else {
Write-Host " signed $name <- $($sig.SignerCertificate.Subject)"
}
$dest = Join-Path $env:RUNNER_TEMP ("sigcheck-" + [System.IO.Path]::GetFileNameWithoutExtension($name))
Remove-Item $dest -Recurse -Force -ErrorAction SilentlyContinue
& $SevenZip x -y "-o$dest" $bundle | Out-Null
# 7-Zip leaves a partial tree behind on error, so a created dir proves nothing.
if ($LASTEXITCODE -ne 0) {
Write-Host "::error::7-Zip exited $LASTEXITCODE unpacking $name; contents not verified"
exit 1
}
if (-not (Test-Path $dest)) {
Write-Host "::error::could not unpack $name; cannot verify its contents"
exit 1
}
$inner = Get-ChildItem $dest -Recurse -File |
Where-Object { $exeExtensions -contains $_.Extension.ToLower() }
# No hits means 7-Zip dumped PE sections, not that the payload is clean.
if (-not $inner) {
Write-Host "::error::no executable payload found inside $name; contents not verified"
exit 1
}
foreach ($f in ($inner | Sort-Object Name)) {
$checked++
$s = Get-AuthenticodeSignature $f.FullName
if ($s.Status -eq 'Valid') {
Write-Host (" signed {0}" -f $f.Name)
} elseif ($Allow -contains $f.Name) {
Write-Host (" ALLOWED {0} ({1}) - explicitly accepted as unsigned" -f $f.Name, $s.Status)
} else {
# UnknownError = no signature or unbuilt chain; StatusMessage tells which.
Write-Host (" UNSIGNED {0} ({1}) {2}" -f $f.Name, $s.Status, $s.StatusMessage)
$unsigned += [pscustomobject]@{ Bundle = $name; File = $f.Name; Status = [string]$s.Status }
}
}
Remove-Item $dest -Recurse -Force -ErrorAction SilentlyContinue
}
Write-Host ''
Write-Host "checked $checked file(s) across $($Path.Count) bundle(s)"
if (-not $unsigned) {
Write-Host 'Every executable in every bundle is validly signed.'
exit 0
}
Write-Host ''
Write-Host '================ UNSIGNED FILES ================'
$unsigned | Format-Table Bundle, File, Status -AutoSize | Out-String | Write-Host
foreach ($u in $unsigned) {
Write-Host "::error file=$($u.File)::$($u.File) in $($u.Bundle) is $($u.Status) and needs signing"
}
Write-Host ''
Write-Host 'These ship inside the installer and land on the user machine.'
Write-Host 'For NSIS plugin DLLs see the NSISPLUGINS note in windows/installer.nsi.'
exit 1