v0.104.0: Disabling the Node integration in Electron

Introduction#

Before v0.104.0, code executed at renderer level (e.g. front-end scripts) could directly require @electron/remote or even Node.js APIs (e.g. process or fs). While this makes them powerful, it also exposes a significant attack surface — any XSS or untrusted script in a note becomes remote code execution on the host.

To mitigate this, the client-side no longer has access to these APIs and everything must go through the Electron API instead.

Overview of the API#

The Frontend and Backend APIs reside in a global variable, api, that can be used anywhere in the script. For example, to display a message to the user the following front-end script can be used:

api.showMessage("Hello world.");

The Electron API is different: it lives on window.electronApi (exposed by the desktop preload script), not on api, and is only available in the Electron desktop build. Frontend scripts that use it should guard with if (window.electronApi) so they degrade cleanly in the browser and standalone (WASM) builds.

window.electronApi?.window.setZoomFactor(1.2);

Additional protection#

Some of the APIs have additional safe-guards built in which prevent unauthorized access.

The shell group is the most heavily validated: openExternal is restricted to an allowlisted set of URL schemes (blocking file:, smb:, Follina-class schemes, etc.), openPath must resolve under the Trilium data directory or temporary directory, and downloadURL is locked to the app's own origin. Invalid input throws on the main side rather than being silently passed through.