new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Electron · Tutorial · all subjects

security/context-isolation

52 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

Context isolation default status since Electron 12

Context isolation has been enabled by default since Electron 12 and is a recommended security setting for all applications.

Context isolation prevents window object sharing

When context isolation is enabled, the window object that a preload script has access to is a different object than the website has access to. For example, if you set window.hello = 'wave' in your preload script, window.hello will be undefined if the website tries to access it.

What is context isolation in Electron

Context Isolation is a feature that ensures that both preload scripts and Electron's internal logic run in a separate context to the website loaded in webContents. This prevents the website from accessing Electron internals or the powerful APIs that the preload script has access to.

Exposing APIs without context isolation using direct window assignment

Before context isolation became standard, preload scripts could expose APIs by directly assigning to window properties. For example, setting window.myAPI = { doAThing: () => {} } would make doAThing() accessible to the renderer process.

Using contextBridge to expose APIs with context isolation enabled

The contextBridge module is used to safely expose APIs from a preload script's isolated context to the website's context. Example: const { contextBridge } = require('electron'); contextBridge.exposeInMainWorld('myAPI', { doAThing: () => {} }). The API is accessible from the website on window.myAPI just like it was before context isolation.

contextBridge limitations with custom prototypes and symbols

You cannot send custom prototypes or symbols over the contextBridge. Consult the contextBridge documentation for full understanding of its limitations.

Unsafe pattern: directly exposing ipcRenderer.send over contextBridge

Directly exposing powerful APIs like ipcRenderer.send without argument filtering is unsafe because it allows any website to send arbitrary IPC messages. This should never be done.

Safe pattern: wrapping IPC calls in contextBridge

The correct way to expose IPC-based APIs is to provide one method per IPC message. For example: contextBridge.exposeInMainWorld('myAPI', { loadPreferences: () => ipcRenderer.invoke('load-prefs') }). This allows you to control what messages can be sent.

TypeScript declaration file for contextBridge exposed APIs

When using TypeScript, create a declaration file (e.g., interface.d.ts) to extend the Window interface with types for APIs exposed over contextBridge. Example: export interface IElectronAPI { loadPreferences: () => Promise<void> }; declare global { interface Window { electronAPI: IElectronAPI } }. This ensures the TypeScript compiler knows about the exposed properties on the window object.

Expose ipcRenderer through contextBridge in preload scripts

Use the contextBridge API in preload scripts to expose limited IPC functionality to the renderer process. This prevents direct access to ipcRenderer and limits renderer access to specific Electron APIs for security reasons. Create global APIs (e.g., window.electronAPI) that wrap ipcRenderer methods.

Avoid leaking ipcRenderer through event.sender in preload callbacks

When exposing ipcRenderer.on listeners through the context bridge, do not pass the callback directly to ipcRenderer.on. This will leak ipcRenderer via event.sender. Instead, use a custom handler that invokes the callback with only the desired arguments.

Context isolation required for secure IPC patterns

Before implementing IPC patterns, you should be familiar with context isolation and preload scripts to safely import Node.js and Electron modules in a context-isolated renderer process. Preload scripts are the secure way to expose IPC APIs to renderer processes.

App Sandbox requirement for Mac App Store

Apps submitted to the Mac App Store must run under Apple's App Sandbox. Only the MAS build of Electron can run with App Sandbox; the standard darwin build will fail to launch under App Sandbox.

@electron/osx-sign automatically adds App Sandbox entitlements

When signing an app with @electron/osx-sign, it will automatically add the necessary entitlements for App Sandbox to your app's entitlements.

Minimum App Sandbox entitlements for app bundle

If signing without @electron/osx-sign, the app bundle's entitlements.plist must include: com.apple.security.app-sandbox set to true, and com.apple.security.application-groups with an array containing TEAM_ID.your.bundle.id, where TEAM_ID is your Apple Developer account's Team ID and your.bundle.id is the App ID.

Executable entitlements for App Sandbox

The following entitlements must be added to all executables in the app bundle: com.apple.security.app-sandbox set to true, and com.apple.security.inherit set to true.

ElectronTeamID in Info.plist

The app bundle's Info.plist must include the ElectronTeamID key with your Apple Developer account's Team ID as its value. When using @electron/osx-sign, this key is added automatically by extracting the Team ID from the certificate's name, but you may need to add it manually if @electron/osx-sign cannot find the correct Team ID.

Resource access restrictions with App Sandbox

Due to app sandboxing, the resources which can be accessed by the app are strictly limited. Every app running under App Sandbox runs under a limited set of permissions, which limits potential damage from malicious code.

Entitlements file format for App Sandbox

Entitlements are specified using property list (.plist) or XML format files. You must provide an entitlement file for the application bundle itself and a child entitlement file describing inheritance of properties for all other enclosing executable files like binaries, frameworks (.framework), and dynamically linked libraries (.dylib).

Network client access entitlement

To enable outgoing network connections for your MAS app, add the entitlement com.apple.security.network.client set to true.

Network server access entitlement

To enable incoming network connections for your MAS app to open a network listening socket, add the entitlement com.apple.security.network.server set to true.

Read-only file access entitlement for dialog.showOpenDialog

To use dialog.showOpenDialog in your MAS app, add the entitlement com.apple.security.files.user-selected.read-only set to true.

Read-write file access entitlement for dialog.showSaveDialog

To use dialog.showSaveDialog in your MAS app, add the entitlement com.apple.security.files.user-selected.read-write set to true.

Context isolation with MessagePorts to main world

When context isolation is enabled, IPC messages from the main process to the renderer are delivered to the isolated world, not the main world. To send messages directly to the main world of a context-isolated page, use MessagePorts. The preload script receives the port via ipcRenderer.on() and transfers it to the main world using window.postMessage(), bypassing the isolated world.

Failed preload window attachment example

Example showing that direct window attachment from preload does not work: preload.js: ```js window.myAPI = { desktop: true } ``` renderer.js: ```js console.log(window.myAPI) // => undefined ```

Full Node.js environment in renderer historically enabled but disabled for security

Renderer processes can historically be spawned with a full Node.js environment for ease of development, but this feature was disabled for security reasons and is no longer the default.

Context isolation prevents direct window attachment from preload

Because contextIsolation is enabled by default, preload scripts cannot directly attach variables to window. Attempting to assign properties like window.myAPI directly will not be visible to the renderer because the preload script is isolated from the renderer's main world to avoid leaking privileged APIs.

Sandbox enabled by default for renderer processes in Electron 20+

Starting from Electron 20, the sandbox is enabled for renderer processes without any further configuration.

Sandbox disables Node.js integration

Enabling Node.js integration for a renderer process by setting nodeIntegration: true disables the sandbox for that process. Conversely, the sandbox is also disabled whenever Node.js integration is enabled in the renderer.

Sandboxed processes have limited system resource access

The sandbox limits the harm that malicious code can cause by limiting access to most system resources. Sandboxed processes can only freely use CPU cycles and memory.

Sandboxed processes use IPC to delegate privileged tasks

Sandboxed processes use dedicated communication channels to delegate tasks to more privileged processes. When the sandbox is enabled, renderer processes can only perform privileged tasks such as interacting with the filesystem, making changes to the system, or spawning subprocesses by delegating these tasks to the main process via inter-process communication (IPC).

Processes affected by Chromium sandbox

In Chromium, sandboxing is applied to most processes other than the main process. This includes renderer processes, as well as utility processes such as the audio service, the GPU service and the network service.

Preload scripts in sandboxed renderers have polyfilled Node.js APIs

Preload scripts attached to sandboxed renderers have a polyfilled subset of Node.js APIs available. A require function similar to Node's require module is exposed, but can only import a subset of Electron and Node's built-in modules.

Preload script available Electron modules in sandbox

In sandboxed preload scripts, the electron module exposes only the following renderer process modules: contextBridge, crashReporter, ipcRenderer, nativeImage, webFrame, and webUtils.

Preload script available Node modules in sandbox

In sandboxed preload scripts, the following Node.js modules are available: events, timers, and url. Node: imports are also supported for these modules via node:events, node:timers, and node:url.

Preload script global primitives in sandbox

In sandboxed preload scripts, the following Node.js primitives are polyfilled as globals: Buffer, process, clearImmediate, and setImmediate.

Disable sandbox per-renderer with sandbox: false

Renderer sandboxing can be disabled on a per-process basis with the sandbox: false preference in the BrowserWindow constructor under webPreferences.

Disable sandbox example with BrowserWindow

Example code to disable sandbox for a single renderer: app.whenReady().then(() => { const win = new BrowserWindow({ webPreferences: { sandbox: false } }) win.loadURL('https://google.com') })

Disable sandbox via nodeIntegration in BrowserWindow

Sandboxing is disabled whenever Node.js integration is enabled in the renderer through the BrowserWindow constructor with the nodeIntegration: true flag.

Enable sandbox globally with app.enableSandbox()

To force sandboxing for all renderers, use the app.enableSandbox() API. This API must be called before the app's ready event. When called, any sandbox: false calls are overridden.

Disable Chromium sandbox entirely with --no-sandbox flag

You can disable Chromium's sandbox entirely with the --no-sandbox CLI flag, which will disable the sandbox for all processes including utility processes. This flag should only be used for testing purposes and never in production.

Bundler required to split preload script code

Because the require function in sandboxed preload scripts is a polyfill with limited functionality, you cannot use CommonJS modules to separate your preload script into multiple files. If you need to split preload code, use a bundler such as webpack or Parcel.

Context isolation required to prevent API leaks from preload

Because the environment presented to the preload script is substantially more privileged than that of a sandboxed renderer, it is still possible to leak privileged APIs to untrusted code running in the renderer process unless contextIsolation is enabled.

Untrusted content rendering security limitations in Electron

Rendering untrusted content in Electron has fundamental security limitations: Electron does not have the dedicated resources or expertise that Chromium has; some security features in Chrome require centralized authorities and dedicated servers which contradict Electron's goals; there are thousands of different Electron apps with different behaviors making it challenging to ensure security in all use cases; and Electron cannot directly push security updates to users, relying instead on app vendors to upgrade their Electron version.

Content Security Policy header for Electron HTML

Include Content-Security-Policy headers in HTML files loaded into Electron: 'default-src 'self'; script-src 'self''. This restricts loading to same-origin resources and inline scripts from the same origin.

Preload script sandboxing from Electron 20 onwards

From Electron 20 onwards, preload scripts are sandboxed by default and no longer have access to a full Node.js environment. They have a polyfilled require function that only provides access to a limited set of APIs.

Available APIs in sandboxed preload scripts

Sandboxed preload scripts have access to the following: Electron modules (renderer process modules), Node.js modules (events, timers, url), and polyfilled globals (Buffer, process, clearImmediate, setImmediate).

contextBridge API for exposing privileged APIs

The contextBridge API allows you to define global objects in preload scripts to expose privileged APIs to the renderer. This is the secure way to add features to the renderer that require privileged access.

Attaching preload script to BrowserWindow

To attach a preload script to a renderer process, pass the script's path to the webPreferences.preload option in the BrowserWindow constructor.

Example preload script exposing version information

const { contextBridge } = require('electron') contextBridge.exposeInMainWorld('versions', { node: () => process.versions.node, chrome: () => process.versions.chrome, electron: () => process.versions.electron // we can also expose variables, not just functions }) This example exposes selected properties of Electron's process.versions object to the renderer process in a versions global variable.

Preload script definition and purpose

A preload script is a special script that bridges Electron's different process types together. It runs before a web page loads in the renderer, similar to a Chrome extension's content scripts. Preload scripts have access to both the HTML DOM and a limited subset of Node.js and Electron APIs.

Example BrowserWindow with preload script

const { app, BrowserWindow } = require('electron') const path = require('node:path') const createWindow = () => { const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { preload: path.join(__dirname, 'preload.js') } }) win.loadFile('index.html') } app.whenReady().then(() => { createWindow() }) This example shows how to configure a BrowserWindow to use a preload script by passing its path to the webPreferences.preload option.

Give your agent this brain