Skip to content

Fix SmartScreen: signed single-exe installer#44

Open
yikkuro wants to merge 4 commits into
mainfrom
tsun/smartscreen-signed-installer
Open

Fix SmartScreen: signed single-exe installer#44
yikkuro wants to merge 4 commits into
mainfrom
tsun/smartscreen-signed-installer

Conversation

@yikkuro

@yikkuro yikkuro commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Problem

Windows Defender SmartScreen blocks installation. The shipped installer is an unsigned PyInstaller executable delivered inside a .zip; the download carries Mark-of-the-Web, and SmartScreen evaluates the unsigned exe before it launches (so the in-process MOTW strip in deployer/webview_bridge.py runs too late).

Fix

Produce a single, signable download that clears SmartScreen once signed with a reputation-bearing identity.

  • installer/microclaw-setup.nsi — NSIS self-extractor wrapping the existing PyInstaller onedir. Extracts to %LOCALAPPDATA%\MicroClaw\Setup (a real directory, not %TEMP%, so WDAC does not block extracted DLLs) and auto-launches the installer GUI. Chosen over PyInstaller onefile to preserve the onedir WDAC-safety rationale.
  • scripts/windows/sign-artifact.ps1 — opt-in Azure Trusted Signing step. No-op (exit 0) unless TRUSTED_SIGNING_* secrets are set, so local and PR builds (no secrets / may run on forks) stay unsigned and green.
  • build.ps1 — new Step 7 builds MicroClawSetup.exe via makensis, then signs it.
  • .github/workflows/release.yml — on v* tags / manual dispatch, builds and signs the exe, verifies the Authenticode signature is Valid when secrets are configured, and publishes it as a GitHub Release asset.

Why signing the single exe is enough

Only the downloaded file carries Mark-of-the-Web. Files that a trusted local process later extracts are not re-evaluated by SmartScreen, so signing the outer MicroClawSetup.exe (with an EV/Trusted Signing identity, timestamped) clears the warning.

Testing

  • Reproduced the SmartScreen block locally (build + MOTW stamp).
  • Verified MicroClawSetup.exe extract -> auto-launch: 1202 files extracted to %LOCALAPPDATA%\MicroClaw\Setup, installer GUI auto-launched, extracted files carry no MOTW.
  • Verified the signer no-op path (exit 0 with no secrets).

Follow-up (not in this PR)

Configure repo secrets TRUSTED_SIGNING_ENDPOINT/ACCOUNT/PROFILE + AZURE_TENANT_ID/CLIENT_ID/CLIENT_SECRET so a tag build actually produces the signed exe.

Tao Sun (from Dev Box) and others added 4 commits July 14, 2026 13:08
Windows Defender SmartScreen blocks installation because the shipped
installer is an unsigned PyInstaller executable delivered inside a .zip.
The download carries Mark-of-the-Web, and SmartScreen evaluates the
unsigned exe before it launches (so the existing in-process MOTW strip in
webview_bridge.py runs too late).

Fix: produce a single, signable download that clears SmartScreen once
signed with a reputation-bearing identity.

- installer/microclaw-setup.nsi: NSIS self-extractor wrapping the existing
  onedir. Extracts to %LOCALAPPDATA%\MicroClaw\Setup (a real directory, not
  %TEMP%, so WDAC does not block extracted DLLs) and auto-launches the
  installer GUI. Chosen over PyInstaller onefile to preserve the onedir
  WDAC-safety rationale.
- scripts/windows/sign-artifact.ps1: opt-in Azure Trusted Signing step.
  No-op (exit 0) unless TRUSTED_SIGNING_* secrets are set, so local and PR
  builds (which have no secrets / may run on forks) stay unsigned and green.
- build.ps1: new Step 7 builds MicroClawSetup.exe via makensis then signs it.
- .github/workflows/release.yml: on v* tags / manual dispatch, builds and
  signs the exe, verifies the Authenticode signature is Valid when secrets
  are configured, and publishes it as a GitHub Release asset.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
While the signing approach is still under discussion, allow manual
(workflow_dispatch) runs to produce an UNSIGNED MicroClawSetup.exe:

- New boolean input "sign" (default false). When unchecked, the job passes
  empty TRUSTED_SIGNING_* / AZURE_* env vars so sign-artifact.ps1 no-ops and
  emits the unsigned exe.
- Tag builds (push: v*) are unaffected and still sign, since the gate is
  `github.event_name != 'workflow_dispatch' || inputs.sign`.
- Verify-signature step already degrades to a warning (not an error) when the
  signing env is empty, so an unsigned manual build stays green.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The self-hosted [Windows, X64] pool is offline for this repo, so switch the
release job to the GitHub-hosted windows-latest runner and pin the toolchain
with setup-node@22 / setup-dotnet@9 / setup-python@3.12 (reliable on hosted
runners). build.ps1 installs its own npm/pip deps and finds NSIS in the
electron-builder cache, so no further changes are needed.

Also add a TEMPORARY push trigger for the feature branch so the artifact can
be produced and inspected before merging to main (workflow_dispatch only works
from the default branch). This branch entry should be removed before merge.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reverts the temp branch trigger used to produce the artifact before merge.
Release now triggers only on v* tags and manual workflow_dispatch.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@yikkuro

yikkuro commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator Author
@microsoft-github-policy-service agree company="Microsoft"

@microsoft-github-policy-service agree company="Microsoft"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant