Security: harden the auto-update channel (no authenticity, no quarantine window)
Summary
wmux auto-downloads and silently installs updates from GitHub Releases with no authenticity verification and no release-age safeguard. The only integrity check is a SHA-512 that ships in the same release as the binary it describes, so
it proves the bytes weren't corrupted — not who produced them. Combined with autoInstallOnAppQuit = true, any actor who can publish a release to amirlehmam/wmux (leaked CI token, compromised maintainer account, poisoned build-time dependency, or GitHub-side tampering) gets silent RCE on every installed machine, with near-instant fan-out.
Given the recent wave of npm/supply-chain source-injection attacks, this is the highest-leverage trust dependency in the project.
Where it lives
src/main/updater.ts — autoUpdater.autoDownload = true, autoUpdater.autoInstallOnAppQuit = true, no publisherName / signature checks.
electron-builder.json:7-11 — update feed = GitHub releases of amirlehmam/wmux.
.github/workflows/release.yml:58-119 — Authenticode signing via SignPath is commented out → shipped wmux.exe is unsigned.
.github/workflows/release.yml:152-178 — latest.yml is just sha512(zip) generated in the same job and uploaded to the same release (integrity, not authenticity).
src/main/index.ts:200-203 — both initAutoUpdater() (electron-updater) and initUpdateChecker() (notify-only) run when packaged.
Threat model
| Attacker capability |
Today's outcome |
Why current controls don't stop it |
Leak/abuse GITHUB_TOKEN in CI |
Publish malicious release |
SHA-512 is attacker-generated alongside the binary |
| Compromise maintainer GitHub account |
Replace release assets |
No signature; unsigned exe → no publisherName check |
Poison a build-time dep (npm ci) |
Inject code at build, ships "legit" |
CI produces the hash over already-poisoned bytes |
| Tamper with release assets server-side |
Swap zip + latest.yml |
Both fetched from the same untrusted origin |
In every row, autoInstallOnAppQuit = true + 6h polling means the payload reaches all installs and self-installs on next quit, before anyone can react.
What's actually fine (so we don't over-rotate)
- electron-updater does verify the download against the SHA-512, so passive CDN corruption / partial-download tampering is caught.
- The update check path (
update-checker.ts) is notify-only and harmless.
- No telemetry/exfiltration involved; this is purely about the install channel.
The gap is authenticity and propagation speed, not transport integrity.
Proposed fixes (in priority order)
1. Minimum release age (quarantine window) — cheap, high value, do first Do not auto-download/install a release until it has been public for N days (suggest 3–7, configurable). Implement by reading published_at from the GitHub release API and gating electron-updater accordingly; only call checkForUpdates() / allow download when now - published_at >= MIN_RELEASE_AGE.
- Denies attackers the instant fan-out that makes these attacks lethal.
- Gives the maintainer/community time to detect and yank a bad release before
clients adopt it.
- Mirrors the npm
minimum-release-age mitigation now widely recommended for the
same class of supply-chain attacks.
- Security/critical fixes can opt out via an explicit allowlist or a signed
"expedite" flag if ever needed.
2. Independent signature on the update metadata — real authenticity fix Sign the release artifact (or latest.yml) with an offline key whose public key is baked into the app at build time (e.g. minisign / cosign / Sigstore).
Verify the signature in updater.ts before allowing install. This decouples authenticity from "whoever can push to the GitHub release" — a release-channel compromise alone no longer yields a valid update.
3. Code signing (Authenticode) + publisher pinning Re-enable the SignPath steps (release.yml:58-119) and set electron-updater's publisherName so updates must be signed by the known publisher. Also fixes SmartScreen friction. (Tracked dependency: SignPath OSS quota.)
4. Make install user-confirmed, not silent
Replace autoInstallOnAppQuit = true with a prompt: surface "vX downloaded review release notes" and require an explicit click. Removes the silent-propagation property even if 1–3 are delayed.
5. Harden the supply chain around the build
- Build provenance / attestation (
actions/attest-build-provenance, SLSA).
- Least-privilege
GITHUB_TOKEN, protected v* tags, required 2FA on the repo.
- Pin/lock build-time deps and consider an npm
minimum-release-age for
dependencies too (defends row 3 of the threat model).
- Staged/percentage rollout so a bad release reaches few users first.
6. Housekeeping
- The comment in
update-checker.ts:5-7 ("can't use electron-updater … no latest.yml") is stale - release.yml now emits latest.yml and updater.ts is active. Clarify the two update paths to avoid confusion.
Suggested acceptance criteria
References
- electron-updater code-signing &
publisherName verification docs
- Sigstore / minisign for offline artifact signing
- npm
minimum-release-age (supply-chain cooldown) prior art
Security: harden the auto-update channel (no authenticity, no quarantine window)
Summary
wmux auto-downloads and silently installs updates from GitHub Releases with no authenticity verification and no release-age safeguard. The only integrity check is a SHA-512 that ships in the same release as the binary it describes, so
it proves the bytes weren't corrupted — not who produced them. Combined with
autoInstallOnAppQuit = true, any actor who can publish a release toamirlehmam/wmux(leaked CI token, compromised maintainer account, poisoned build-time dependency, or GitHub-side tampering) gets silent RCE on every installed machine, with near-instant fan-out.Given the recent wave of npm/supply-chain source-injection attacks, this is the highest-leverage trust dependency in the project.
Where it lives
src/main/updater.ts—autoUpdater.autoDownload = true,autoUpdater.autoInstallOnAppQuit = true, nopublisherName/ signature checks.electron-builder.json:7-11— update feed = GitHub releases ofamirlehmam/wmux..github/workflows/release.yml:58-119— Authenticode signing via SignPath is commented out → shippedwmux.exeis unsigned..github/workflows/release.yml:152-178—latest.ymlis justsha512(zip)generated in the same job and uploaded to the same release (integrity, not authenticity).src/main/index.ts:200-203— bothinitAutoUpdater()(electron-updater) andinitUpdateChecker()(notify-only) run when packaged.Threat model
GITHUB_TOKENin CIpublisherNamechecknpm ci)latest.ymlIn every row,
autoInstallOnAppQuit = true+ 6h polling means the payload reaches all installs and self-installs on next quit, before anyone can react.What's actually fine (so we don't over-rotate)
update-checker.ts) is notify-only and harmless.The gap is authenticity and propagation speed, not transport integrity.
Proposed fixes (in priority order)
1. Minimum release age (quarantine window) — cheap, high value, do first Do not auto-download/install a release until it has been public for N days (suggest 3–7, configurable). Implement by reading
published_atfrom the GitHub release API and gating electron-updater accordingly; only callcheckForUpdates()/ allow download whennow - published_at >= MIN_RELEASE_AGE.clients adopt it.
minimum-release-agemitigation now widely recommended for thesame class of supply-chain attacks.
"expedite" flag if ever needed.
2. Independent signature on the update metadata — real authenticity fix Sign the release artifact (or
latest.yml) with an offline key whose public key is baked into the app at build time (e.g. minisign / cosign / Sigstore).Verify the signature in
updater.tsbefore allowing install. This decouples authenticity from "whoever can push to the GitHub release" — a release-channel compromise alone no longer yields a valid update.3. Code signing (Authenticode) + publisher pinning Re-enable the SignPath steps (
release.yml:58-119) and set electron-updater'spublisherNameso updates must be signed by the known publisher. Also fixes SmartScreen friction. (Tracked dependency: SignPath OSS quota.)4. Make install user-confirmed, not silent
Replace
autoInstallOnAppQuit = truewith a prompt: surface "vX downloaded review release notes" and require an explicit click. Removes the silent-propagation property even if 1–3 are delayed.5. Harden the supply chain around the build
actions/attest-build-provenance, SLSA).GITHUB_TOKEN, protectedv*tags, required 2FA on the repo.minimum-release-agefordependencies too (defends row 3 of the threat model).
6. Housekeeping
update-checker.ts:5-7("can't use electron-updater … no latest.yml") is stale -release.ymlnow emitslatest.ymlandupdater.tsis active. Clarify the two update paths to avoid confusion.Suggested acceptance criteria
GITHUB_TOKENscope minimized.update-checker.tscomment corrected.References
publisherNameverification docsminimum-release-age(supply-chain cooldown) prior art