feat(updater): fix manifest sha512 hash-encoding sniffing, add opt-in Linux package-signature verification#9990
Open
mmaietta wants to merge 5 commits into
Open
feat(updater): fix manifest sha512 hash-encoding sniffing, add opt-in Linux package-signature verification#9990mmaietta wants to merge 5 commits into
mmaietta wants to merge 5 commits into
Conversation
…ing, add Linux package-verification opt-in Tier-1 v27 changes. The legacy path/sha512 gating is split into its own follow-up commit; sha2/SHA-256 removal is deferred to v28 per the existing grace period. - Remove the deprecated UpdateCheckResult.versionInfo alias (use updateInfo). (BREAKING) - Always treat manifest sha512 as base64 instead of heuristically sniffing hex vs base64, removing a crafted-manifest mis-parse vector. SHA-256 (sha2) acceptance is retained under its existing v28-removal grace period. - Add AppUpdater.allowUnverifiedLinuxPackages. Because electron-builder does not sign Linux packages, this defaults to true (preserving today's behavior: unsigned .deb/.rpm install). Set it to false to enforce package signature/GPG verification, which gates --allow-unauthenticated (apt), --allow-unsigned-rpm (zypper) and --nogpgcheck (dnf/yum). rpm --nodeps is left in place (dependency, not signature, bypass). BREAKING CHANGE: UpdateCheckResult.versionInfo removed; use updateInfo. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: 474def7 The changes in this PR will be included in the next version bump. This PR includes changesets to release 12 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
(BREAKING, due to versionInfo`)…nd enforce per package manager - Move the new allowUnverified parameter of DebUpdater/RpmUpdater.installWithCommandRunner to the last position with a default of true, restoring source and runtime compatibility for existing 6.x callers; doInstall still threads this.allowUnverifiedLinuxPackages. - dnf/yum enforce mode now passes --setopt=localpkg_gpgcheck=1: local package files are governed by localpkg_gpgcheck (default False on dnf4/dnf5/yum), so omitting --nogpgcheck alone enforced nothing. - Log enforce/bypass per branch instead of unconditionally: dpkg warns that the setting has no effect (dpkg performs no signature verification); bare rpm warns that enforcement cannot be applied via the CLI (default %_pkgverify_level is "digest"). - Rewrite the allowUnverifiedLinuxPackages JSDoc with the per-package-manager reality. - Revert the blackbox test call sites now that the added positional argument is gone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ZTrgpZ9bNcWZ3BuamMhnz
…removing support Replace the hard-coded base64 encoding with strict legacy-hex detection: a manifest sha512 that is exactly 128 hexadecimal characters is decoded as hex with a deprecation debug log (removal planned for v28); anything else is decoded as base64, which electron-builder has emitted since 19.x (2017). Detection cannot collide: base64-encoded sha512 is always 88 characters ending in "==", and a wrong pick only fails closed with ERR_CHECKSUM_MISMATCH. Also drop the unsound "crafted-manifest mis-parsing vector" rationale from the comment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ZTrgpZ9bNcWZ3BuamMhnz
…; docs: changesets + troubleshooting - linuxUpdaterUnitTest: assert exact argv with toEqual for every enforce/bypass branch, add dpkg-branch tests (dpkg -i + apt-get -f fallback), a trailing-default compatibility test, warning-path tests for dpkg and bare rpm, and doInstall wiring tests proving this.allowUnverifiedLinuxPackages is threaded through DebUpdater (apt) and RpmUpdater (zypper); drop the (A2) marker and use NoOpLogger instead of an any-cast stub. - httpExecutorTest: cover detectSha512Encoding (128-char hex vs base64) and DigestTransform end-to-end for both encodings plus the fail-closed mismatch path. - Changesets: scope the enforcement claims per package manager, note the trailing optional parameter, replace the hash-encoding security claim with the strict-detection/deprecation description, and add electron-updater to the hash-encoding changeset front-matter. - troubleshooting.md: note that manifest sha512 should be base64 as of v27 (hex is deprecated, removed in v28) with a conversion hint. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ZTrgpZ9bNcWZ3BuamMhnz
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Mike Maietta · Slack thread
Summary
Manifest
sha512hex encoding is now strictly detected and deprecated. The download verifier previously sniffed whether a manifest'ssha512was hex- or base64-encoded with a fuzzy heuristic (sha512.length === 128 && !includes('+') && !includes('Z') && !includes('=')). It now uses a strict check: a value that is exactly 128 hexadecimal characters is treated as legacy hex (with a deprecation debug log — support will be removed in v28); anything else is decoded as base64, which is what electron-builder has emitted since 19.x. The two forms cannot collide (base64-encoded sha512 is always 88 characters ending in==), and a wrong pick can only fail closed with a checksum mismatch. SHA-256 (sha2) acceptance is unchanged and remains under its existing v28-removal grace period.NEW —
AppUpdater.allowUnverifiedLinuxPackages(defaulttrue). Because electron-builder does not sign Linux packages, this defaults totrueto preserve today's behavior:.deb/.rpmauto-updates install with the package manager's signature/GPG checks bypassed where a bypass flag exists. Set it tofalse(for pipelines that sign their own packages and whose targets trust the keys) to enforce verification where the package manager supports it.DebUpdater/RpmUpdaterinto their staticinstallWithCommandRunnermethods as a trailing optional parameter (allowUnverified = true), so existing 6.x call sites remain source- and runtime-compatible (no API break).falseenforces depends on the package manager on the target system: dpkg (the default for.deb) — no effect, dpkg performs no signature verification (a warning is logged;.debenforcement requires a debsig-verify/debsigs policy on the target system); apt (.debfallback) —--allow-unauthenticatedis omitted; zypper — enforced, unsigned/untrusted packages fail to install; dnf/yum — enforced via--setopt=localpkg_gpgcheck=1, since local package files are governed bylocalpkg_gpgcheck(default off) and merely omitting--nogpgcheckwould enforce nothing; bare rpm (no-resolver fallback) — cannot be enforced via the CLI (a warning is logged; the default%_pkgverify_levelisdigest, admins must configure%_pkgverify_level signature).rpm --nodepsis left in place unconditionally: it is a dependency-resolution bypass, not a signature bypass, and the bare-rpmbranch is the no-resolver fallback.The deprecated
UpdateCheckResult.versionInfoalias is retained. (This branch previously removed it; that removal has been reverted, soversionInfo— a@deprecatedalias ofupdateInfo— continues to be populated bycheckForUpdates()for backward compatibility.)Changesets
builder-util-runtime: patch (plus anelectron-updaterpatch note so it lands in the changelog updater users read) — strict legacy-hex detection with deprecation, removal planned for v28.electron-updater: minor —allowUnverifiedLinuxPackages.Design notes
Default is
true, notfalse. Flipping the default to enforce verification would break every existing electron-builder Linux auto-updater, since electron-builder does not sign Linux packages. The safe-by-default posture here is "preserve working updates"; enforcement is strictly opt-in for projects that sign packages out-of-band.Why the hash-encoding change is not breaking. Within the electron-builder ecosystem,
sha512inlatest*.ymlis always base64, so no real electron-builder manifest changes behavior. Hex values (pre-19.x manifests or hand-rolled manifests built from rawsha512sumoutput) keep working in v27 — they are now detected strictly instead of fuzzily, logged as deprecated, and will be removed in v28. Nothing breaks in this release, which is why this is apatch; theversionInfoalias removal (reverted here) was the only genuinely breaking part of the original change.Tests
linuxUpdaterUnitTestasserts the exact install argv (toEqual) for bothallowUnverifiedLinuxPackagesmodes across apt, zypper, dnf, yum, and rpm — including--setopt=localpkg_gpgcheck=1in dnf/yum enforce mode andrpm --nodepsremaining regardless. It also covers the dpkg branch (directdpkg -iplus theapt-get install -f -yfallback), the trailing-default 6.x call-site compatibility, the dpkg/bare-rpm warning paths, anddoInstallwiring tests proving the instance field is threaded throughDebUpdater(apt) andRpmUpdater(zypper).httpExecutorTestcoversdetectSha512Encoding(128-char hex vs 88-char base64) andDigestTransformend-to-end for both encodings, plus the fail-closed mismatch path.nsisUpdaterTest,macUpdaterTest,macUpdaterLogicTest,linuxUpdaterUnitTest,updateUtilTest) pass with the restoredversionInfoalias — the field is additive and no snapshot captures the full check-result object (the helper snapshots onlyupdateInfo).