跳到正文
DEV Community· Maxi·· 3 小時前AI 評分64

如何不執行 Electron 應用程式,直接從發佈 ZIP 讀取 fuses、執行時版本與 ASAR 完整性

Read an Electron app's fuses, runtime and ASAR integrity from the release ZIP, without running it

AI 導讀

hexvero 可不執行 Electron 應用程式,直接從發佈 ZIP 讀取 Electron、Chromium 和 Node 版本、fuses 狀態及 ASAR 完整性。

正文

Most Electron hardening lives outside your JavaScript. Which Electron, Chromium and Node a build ships, which fuses are flipped, and whether the app.asar is covered by an integrity hash are all decided at packaging time and baked into the binary. You won't see any of it in a code review of main.js.

You can read all of it back from the shipped files. Here is what that looks like on two public releases of Electron Fiddle (MIT, the official Electron playground), straight from the macOS release ZIPs on GitHub. Nothing is unpacked by hand and nothing is executed.

One command per build

npx hexvero analyze Electron.Fiddle-darwin-arm64-0.39.0.zip

The relevant part of the output:

Runtime
  Electron 40.8.5 · Chromium 144.0.7559.236 · Node 24.14.0
  read from Contents/Frameworks/Electron Framework.framework/Versions/A/Electron Framework
  support: Electron 40.8.5 is past its end of support (2026-07-01, from the snapshot of 2026-10-18, as of 2026-10-24); 40.8.5 was released on 2026-03-26
  fuses: runAsNode off, cookieEncryption off, nodeOptions off, nodeCliInspect off, embeddedAsarIntegrityValidation off, onlyLoadAppFromAsar on, loadBrowserProcessSpecificV8Snapshot off, grantFileProtocolExtraPrivileges on
  ASAR integrity: covers the loaded archive

Where each line comes from:

  • Versions. The Electron Framework binary contains the user-agent fragment Chrome/<version> Electron/<version> and Node's release URL. A universal binary holds one copy per architecture, and the value only counts as known if all copies agree.
  • Fuses. Electron writes them as a small wire right after a fixed sentinel string in the binary: a schema byte, a count byte, then one byte per fuse (0 off, 1 on, r removed). That's the same wire @electron/fuses flips when you package.
  • ASAR integrity. On macOS this is ElectronAsarIntegrity in Contents/Info.plist: a hash of the archive's header. hexvero hashes the header of the app.asar actually in the bundle and says whether the record covers it.
  • Support. This compares the version against a dated snapshot of Electron's release data. It doesn't claim anything about vulnerabilities.

The detail worth a second look

Fiddle's bundle carries an integrity record that matches its app.asar, but the fuse embeddedAsarIntegrityValidation is off. Per Electron's docs, that fuse is what makes Electron check the record when it loads the archive. So the hash ships, but nothing verifies it at startup. Packagers write the record by default; the fuse is opt-in.

That isn't a vulnerability claim about Fiddle, which is a developer tool. It's the kind of fact that's invisible unless you read the bundle, and it matters for anything that relies on the archive not being swapped on disk. Trail of Bits' CVE-2025-55305 write-up shows why this area deserves attention: they bypassed ASAR integrity through V8 heap snapshots in Signal, 1Password and Slack.

What changed between 0.38.0 and 0.39.0

npx hexvero compare Electron.Fiddle-darwin-arm64-0.38.0.zip Electron.Fiddle-darwin-arm64-0.39.0.zip runtime
runtime: 0 added, 0 removed, 3 changed, 12 unchanged · matched by item
  ~ electron  value: "40.1.0" → "40.8.5"
  ~ chromium  value: "144.0.7559.96" → "144.0.7559.236"
  ~ node  value: "24.11.1" → "24.14.0"

A patch-level Electron bump, and no fuse moved. The same comparison also covers the app's code, so other collections show what the release actually added:

ipc
  ~ .webpack/renderer/main_window/preload.js::ipcRenderer.invoke::NPM_ADD_MODULES  args: "({dir, packageManager}, ...)" → "({dir, packageManager, useSocketFirewall}, ...)"
storage
  + .webpack/renderer/main_window/index.js::localStorage::isUsingSocketFirewall::write
endpoints
  + .webpack/sfw/dist/sfw.mjs::http-client::https://api.github.com/repos/SocketDev/sfw-free/releases/latest

That lines up with the release: 0.39.0 added an option to install packages through Socket's firewall. A new field travels over an existing IPC channel, there's a new persisted setting, and a bundled CLI calls GitHub's API. All three are visible from the ZIPs alone.

Turn it into a release gate

The question that matters at release time is "did this build get weaker than the last one?", not "is this build perfect?". hexvero check has a fuse-weakened rule for exactly that. It fails when a fuse moves to its less safe state (runAsNode, nodeOptions, nodeCliInspect or grantFileProtocolExtraPrivileges turned on; embeddedAsarIntegrityValidation, onlyLoadAppFromAsar or cookieEncryption turned off), when an integrity record stops covering the archive Electron loads, or when the Electron line goes past end of support:

npx hexvero init previous/YourApp.app            # writes .hexvero/policy.json
npx hexvero check previous/YourApp.app dist/YourApp.app --policy .hexvero/policy.json
# exit 0 pass, 3 violated, 4 not decidable

It writes SARIF if you want the result in GitHub code scanning. The same policy also covers new IPC channels and preload members, more permissive webPreferences, and new external hosts.

Limits

  • It reads bytes and never runs the app. A fuse that's on doesn't prove the behavior it guards is reachable, and a passing check isn't a security verdict.
  • Linux builds have no integrity record to read. Installers (.dmg, setup .exe, AppImage) need to be installed or unpacked first; .app bundles, install directories and release ZIPs are read as they are.
  • Anything it can't read is reported as unknown, never silently as "fine".

hexvero is open source (MIT): github.com/maximilianfeix/hexvero. If it reads something wrong on your own builds, an issue with the output is the most useful feedback I can get.

Written with AI assistance. Every command and output above is real, from hexvero 0.11.1 on the official Fiddle release ZIPs, run on 2026-10-24.

來源:DEV Community · dev.to