Skip to content

fix(update): verify the rebuilt desktop executable before shipping it#71119

Open
teknium1 wants to merge 1 commit into
mainfrom
fix/windows-update-binary-integrity
Open

fix(update): verify the rebuilt desktop executable before shipping it#71119
teknium1 wants to merge 1 commit into
mainfrom
fix/windows-update-binary-integrity

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Summary

A desktop update can no longer replace a working Hermes.exe with a corrupt or wrong-arch binary — the rebuild is integrity-checked (PE header, architecture, size sanity) before it ships, the previous working build is kept for rollback, and a failed verification aborts the update with a clear message instead of leaving an app Windows refuses to load.

Root cause (#69179)

The desktop self-update chain rebuilds Hermes.exe locally (bootstrap --updatehermes updatehermes desktop --build-only). The success gate at hermes_cli/main.py:6151 accepted any existing file as a successful build — a truncated electron.exe from a corrupt Electron zip cache (a failure mode the codebase's own _purge_electron_build_cache docstring documents), an interrupted rcedit PE rewrite, or a disk-full copy all shipped and relaunched, producing Windows' "This app can't run on your computer" loader error. And cleanStaleAppOutDir deleted the previous working build before staging, so there was nothing to roll back to.

Changes

  • hermes_cli/main.py: integrity gate on the --build-only success path (PE magic + arch match + size sanity), arch-aware exe pick, atomic swap with rollback restore on relaunch failure
  • apps/desktop/scripts/before-pack.mjs: preserveRollbackBackup — previous win-unpacked kept until the new build verifies
  • 23 Python tests (real PE fixtures) + 6 JS tests (vitest, per the CI classifier rule)

Validation

Before After
Corrupt cached Electron zip truncated exe ships, app bricked update aborted, old version kept, clear message
Targeted suites 63 Python + 6 JS passed, 0 failed

Progresses #69179 (the corruption vector is high-confidence but unconfirmed by the reporter; diagnostic questions posted on the issue).

Infographic

windows-update-binary-integrity

…as an update (#69179)

The desktop self-update chain (Desktop -> hermes-setup --update ->
hermes update -> hermes desktop --build-only -> relaunch) rebuilds
Hermes.exe on the user's machine and declared success on bare file
EXISTENCE. A truncated PE (corrupt cached Electron zip / interrupted
extraction or rcedit rewrite / full disk) or a wrong-architecture
unpacked tree therefore shipped as the 'updated' app, which Windows
refuses to load with 'This app can't run on your computer'
(此应用无法在你的电脑上运行) — and the previous working build had
already been wiped by before-pack.mjs, leaving nothing to fall back to.

Fix, in three parts:

- hermes_cli/main.py: post-build integrity gate on Windows
  (_ensure_desktop_exe_launchable). Parses the PE header of the freshly
  built Hermes.exe — MZ/PE magic, section-table completeness vs file
  size (catches truncation), and COFF machine vs the host arch (catches
  arm64/x64 mixups). On failure it purges the (likely corrupt) cached
  Electron zip, invalidates the content-hash build stamp so the
  updater's retry-once genuinely re-downloads and rebuilds, restores
  the previous build from the .bak tree when one exists (keeping the
  corrupt tree as .corrupt for diagnostics), tells the user the update
  was aborted and their old version kept, and exits nonzero.
  _desktop_packaged_executable also now prefers a host-loadable PE over
  pure newest-mtime when multiple win-*-unpacked trees coexist.

- apps/desktop/scripts/before-pack.mjs: on win32, the previous unpacked
  tree is preserved as <appOutDir>.bak (only when it holds the product
  exe — partial/corrupt trees still get the plain wipe) instead of
  being destroyed, providing the rollback material for the gate above.
  Non-Windows behavior is unchanged.

- Behavior-contract tests: tests/hermes_cli/test_desktop_exe_integrity.py
  (23 tests — synthetic PE fixtures for truncation/non-PE/arch-mismatch,
  rollback semantics, and the build-only exit contract) and 6 new vitest
  cases in before-pack.test.mjs for the .bak preservation rules.

Progresses #69179
@github-actions

github-actions Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on ea718a5

all good!

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/desktop Electron desktop app (apps/desktop/*) comp/cli CLI entry point, hermes_cli/, setup wizard area/install-update Installer, updater, packaging, wheels, doctor platform/windows Native Windows-specific behavior or breakage sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows labels Jul 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/install-update Installer, updater, packaging, wheels, doctor comp/cli CLI entry point, hermes_cli/, setup wizard comp/desktop Electron desktop app (apps/desktop/*) P2 Medium — degraded but workaround exists platform/windows Native Windows-specific behavior or breakage sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants