From 38de5cadbe8ba14576f10752b9e214fcc6be70c6 Mon Sep 17 00:00:00 2001 From: Michael Clark <5285928+MikesGlitch@users.noreply.github.com> Date: Thu, 8 Oct 2026 21:55:39 +0000 Subject: [PATCH] :electron: Disable node options fuse (#9140) * disable node options fuse * shorted release notes --- packages/desktop-electron/certificates.ts | 31 +++++++++++++++++++ packages/desktop-electron/index.ts | 6 +++- packages/desktop-electron/package.json | 2 +- packages/desktop-electron/server.ts | 7 +++++ .../disable-node-options-fuse.md | 6 ++++ 5 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 packages/desktop-electron/certificates.ts create mode 100644 upcoming-release-notes/disable-node-options-fuse.md diff --git a/packages/desktop-electron/certificates.ts b/packages/desktop-electron/certificates.ts new file mode 100644 index 0000000000..e076b57892 --- /dev/null +++ b/packages/desktop-electron/certificates.ts @@ -0,0 +1,31 @@ +import fs from 'fs'; +import tls from 'tls'; + +/** + * Adds a self-signed (or private CA) certificate to the set of CA certificates + * that this process trusts for TLS connections, so `fetch` can reach a sync + * server that uses it. + * + * This must run before any TLS connection is opened: Node only applies the + * default CA list to connections made after it is set. + * + * NODE_EXTRA_CA_CERTS can't be used for this. The packaged app disables the + * Electron `nodeOptions` fuse (to stop NODE_OPTIONS code injection), and + * Electron ignores NODE_EXTRA_CA_CERTS whenever that fuse is disabled. + */ +export function trustSelfSignedCertificate(certificatePath: string) { + try { + const certificate = fs.readFileSync(certificatePath, 'utf8'); + tls.setDefaultCACertificates([ + ...tls.getCACertificates('default'), + certificate, + ]); + console.info(`Trusting self-signed certificate: ${certificatePath}`); + } catch (error) { + // Keep starting the backend; connecting to the server will fail with a + // certificate error the user can act on. + console.warn( + `Could not load the self-signed certificate at ${certificatePath}: ${String(error)}`, + ); + } +} diff --git a/packages/desktop-electron/index.ts b/packages/desktop-electron/index.ts index c402c015fe..8588acc5e5 100644 --- a/packages/desktop-electron/index.ts +++ b/packages/desktop-electron/index.ts @@ -308,9 +308,13 @@ async function createBackgroundProcess() { }; if (globalPrefs['server-self-signed-cert']) { + // The backend process trusts this certificate via tls.setDefaultCACertificates + // (see certificates.ts). NODE_EXTRA_CA_CERTS can't be used for this: the + // packaged app disables the Electron `nodeOptions` fuse, which makes + // Electron ignore both NODE_OPTIONS and NODE_EXTRA_CA_CERTS. envVariables = { ...envVariables, - NODE_EXTRA_CA_CERTS: globalPrefs['server-self-signed-cert'], // add self signed cert to env - fetch can pick it up + ACTUAL_SERVER_SELF_SIGNED_CERT: globalPrefs['server-self-signed-cert'], }; } diff --git a/packages/desktop-electron/package.json b/packages/desktop-electron/package.json index b9d189da22..47bd651030 100644 --- a/packages/desktop-electron/package.json +++ b/packages/desktop-electron/package.json @@ -51,7 +51,7 @@ "afterSign": "./build/desktop-electron/afterSignHook.js", "electronFuses": { "runAsNode": false, - "enableNodeOptionsEnvironmentVariable": true, + "enableNodeOptionsEnvironmentVariable": false, "enableNodeCliInspectArguments": false }, "mac": { diff --git a/packages/desktop-electron/server.ts b/packages/desktop-electron/server.ts index c5339b55c2..64e4e8b8ad 100644 --- a/packages/desktop-electron/server.ts +++ b/packages/desktop-electron/server.ts @@ -1,3 +1,4 @@ +import { trustSelfSignedCertificate } from './certificates'; import { retry as promiseRetry } from './retry'; const BACKEND_IMPORT_MAX_RETRIES = 30; @@ -108,5 +109,11 @@ const lazyLoadBackend = async (isDev: boolean) => { const isDev = false; +// Must happen before the backend bundle is loaded so that no TLS connection is +// opened before the certificate is trusted. +if (process.env.ACTUAL_SERVER_SELF_SIGNED_CERT) { + trustSelfSignedCertificate(process.env.ACTUAL_SERVER_SELF_SIGNED_CERT); +} + // Start the app void lazyLoadBackend(isDev); diff --git a/upcoming-release-notes/disable-node-options-fuse.md b/upcoming-release-notes/disable-node-options-fuse.md new file mode 100644 index 0000000000..69286733a6 --- /dev/null +++ b/upcoming-release-notes/disable-node-options-fuse.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [MikesGlitch] +--- + +Harden the desktop app's Electron build options