Repository navigation
Disable Node runtime fuses in packaged builds - #6364
nbarrett-logitech wants to merge 1 commit into
Conversation
Packaged builds shipped Electron's default fuses, which leave the app binary usable as a general-purpose Node runtime: it honored ELECTRON_RUN_AS_NODE, NODE_OPTIONS and the --inspect family of flags. Nothing in the app needs any of these at runtime. Turn off the RunAsNode, EnableNodeOptionsEnvironmentVariable and EnableNodeCliInspectArguments fuses through electron-builder's built-in electronFuses option, which flips them after the afterPack hook and before signing, on macOS and Windows. Unsigned macOS builds re-apply the ad-hoc signature that flipping invalidates, so local arm64 builds made with SLOBS_NO_SIGN still launch.
|
|
There was a problem hiding this comment.
🔵 Needs a closer look
Security-sensitive packaging fuse changes lack signed Windows startup/relaunch checks and signed, notarized macOS launch/native-helper validation, with reported verification limited to unsigned packages.
No open issues 🎉
What changed in this PR
Hardens packaged Streamlabs Desktop builds on macOS and Windows by disabling Electron features that allow the app binary to act as a Node runtime or expose main-process debugging.
Changes:
- Disables the RunAsNode, Node environment options, and Node CLI inspection fuses before signing.
- Restores the ad-hoc signature for unsigned macOS builds so local arm64 packages remain launchable.
| File | Description |
|---|---|
electron-builder/base.config.js |
Configures production fuse restrictions and conditional macOS ad-hoc signing. |
Review effort: Balanced
BundleMonUnchanged files (4)
No change in files bundle size Final result: ✅ View report in BundleMon website ➡️ |
Description
Packaged builds ship Electron's default fuses, and three of them leave the signed app binary usable as a general-purpose Node runtime: it honors
ELECTRON_RUN_AS_NODE,NODE_OPTIONSand the--inspectfamily of flags. Nothing in the app needs any of them at runtime, and Electron's security guidance is to turn them off for production. This PR turns them off at package time on macOS and Windows.It uses electron-builder 26's built-in
electronFusesoption, which flips the fuses after ourafterPackhook and before signing, so the signature covers the flipped binary.@electron/fusesalready ships as a dependency of electron-builder, sopackage.jsondoesn't change. The preview and beta configs inherit it frombase.config.js.electron-builder/base.config.jselectronFusesturning offRunAsNode,EnableNodeOptionsEnvironmentVariableandEnableNodeCliInspectArguments. Unsigned macOS builds (SLOBS_NO_SIGN) re-apply the ad-hoc signature that flipping invalidates, so local arm64 builds still launch.Behavior changes, packaged builds only.
yarn startand the e2e suite run the unpackaged Electron fromnode_modulesand don't change.child_process.fork()throws in the packaged app. Nothing uses it today. UseutilityProcessfor a standalone Node process.NODE_OPTIONSandNODE_EXTRA_CA_CERTSare ignored.--inspectno longer attaches a debugger to a packaged main process.SLOBS_PRODUCTION_DEBUGstill opens DevTools on every window, because it doesn't go through a fuse.Why nothing breaks. Everything the app spawns is a native executable: the OBS server, the crash handler, the Windows updater, and the vision and avatar binaries. Every relaunch (
app.relaunch(), the updater's--exec) starts the app normally. No production dependency callschild_process.fork(). None of the 174 native binaries in the production tree (the macOS builds of obs-studio-node and its helpers, crash-handler, realm and the rest) referenceELECTRON_RUN_AS_NODEorNODE_OPTIONS.Left out on purpose:
OnlyLoadAppFromAsarand embedded asar integrity validation. Turning those on needs a signed-build check first.afterPackre-signs and edits files inapp.asar.unpackedafter the asar header is hashed (codesign,install_name_toolonnode-libuiohook, copying the OBS frameworks in), and those edits have to survive validation on both platforms. That's worth its own PR.Verified locally (unsigned packages built from this branch with the real
base.config.js):Streamlabs Desktop.app(arm64)Streamlabs OBS.exe(x64)On the macOS package,
codesign --verify --deep --strictpasses. A normal launch brings up the worker, main and child windows, the OBS server and its helpers, and the crash handler.NODE_OPTIONSand--inspectare ignored, the app log has no errors, and SIGTERM shuts it down cleanly. I packaged the Windows target on a Mac only to read the exe's fuses, so it has not been launched on Windows.QA Instructions
npx @electron/fuses read --app "<install dir>\Streamlabs OBS.exe". ExpectRunAsNode,EnableNodeOptionsEnvironmentVariableandEnableNodeCliInspectArgumentsto read DisabledStreamlabs Desktop.app, then launch it and confirm the preview renders and the virtual camera starts