From bf49c2c52895c914c69ca8885726292865cdf58b Mon Sep 17 00:00:00 2001 From: ciregenz Date: Tue, 28 Jul 2026 15:49:59 -0700 Subject: [PATCH] [eric] win: sign the bundled claude.exe (AV-quarantine class) + require-signed gate + post-sign smoke --- .github/workflows/release-windows.yml | 3 +++ scripts/build-app-win.ps1 | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/.github/workflows/release-windows.yml b/.github/workflows/release-windows.yml index c6d328e9..1840d81f 100644 --- a/.github/workflows/release-windows.yml +++ b/.github/workflows/release-windows.yml @@ -172,6 +172,9 @@ jobs: run: | node scripts/ci/verify-signature.js --require-signed --target electron/dist/win-unpacked/OpenSwarm.exe if ($LASTEXITCODE -ne 0) { throw "inner OpenSwarm.exe is not validly signed" } + # The bundled Claude CLI must ship signed too: AV quarantine of the unsigned Bun exe is the field's "Claude Code not found" permanent-death class. + node scripts/ci/verify-signature.js --require-signed --target electron/dist/win-unpacked/resources/python-env/Lib/site-packages/claude_agent_sdk/_bundled/claude.exe + if ($LASTEXITCODE -ne 0) { throw "bundled claude.exe is not validly signed" } # Squirrel writes Setup.exe into dist\squirrel-windows\, not dist\ root. node scripts/ci/verify-signature.js --require-signed --target electron/dist/squirrel-windows/OpenSwarm-Setup-x64.exe if ($LASTEXITCODE -ne 0) { throw "OpenSwarm-Setup-x64.exe (installer) is not validly signed" } diff --git a/scripts/build-app-win.ps1 b/scripts/build-app-win.ps1 index a4cc0d71..d56d5e51 100644 --- a/scripts/build-app-win.ps1 +++ b/scripts/build-app-win.ps1 @@ -486,6 +486,30 @@ $BuildInfo = [ordered]@{ $BuildInfo | ConvertTo-Json -Compress | Set-Content -Path (Join-Path $ProjectRoot 'electron\build-info.json') -Encoding utf8 Write-Host "Stamped build-info.json: sha=$BuildShortSha channel=$BuildChannel" +# --- Step 4.5: Sign the bundled Claude CLI before packaging --- +# The Bun-compiled claude.exe ships unsigned inside python-env, and Windows AV +# quarantines it out of installed apps (field class: "Claude Code not found", +# permanent product death). Sign it with the same Azure Trusted Signing hook as +# the app exe, then smoke it: Authenticode appends to the PE, and a Bun exe that +# locates its embedded bundle end-of-file-relative would break; a broken CLI must +# fail the build here, never ship. +if ($Sign) { + $BundledCli = Join-Path $ProjectRoot 'electron\python-env\Lib\site-packages\claude_agent_sdk\_bundled\claude.exe' + if (-not (Test-Path $BundledCli)) { throw "bundled claude.exe not found at $BundledCli (python-env layout changed?)" } + Write-Host "[4.5/5] Signing bundled Claude CLI..." + Push-Location $ProjectRoot + try { + & node -e "require('./electron/build/sign-windows.js').default({ path: process.argv[1] }).then(() => process.exit(0), (e) => { console.error(e); process.exit(1); })" $BundledCli + if ($LASTEXITCODE -ne 0) { throw "signing bundled claude.exe failed" } + } finally { + Pop-Location + } + & $BundledCli --version + if ($LASTEXITCODE -ne 0) { throw "signed claude.exe no longer runs (signing corrupted the Bun binary); aborting build" } + Write-Host "Bundled Claude CLI signed and still runs." + Write-Host "" +} + # --- Step 5: Package with electron-builder --- Write-Host "[5/5] Packaging with electron-builder..." Push-Location (Join-Path $ProjectRoot 'electron')