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 · all subjects

context isolation & security

39 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 enabled by default since Electron 12

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

Context isolation definition and purpose

Context Isolation is a feature that ensures preload scripts and Electron's internal logic run in a separate context from the website loaded in webContents. This prevents the website from accessing Electron internals or the powerful APIs that the preload script has access to. The window object that the preload script accesses is a different object than what the website can access—for example, if you set window.hello = 'wave' in a preload script with context isolation enabled, the website trying to access window.hello will get undefined.

Exposing APIs with context isolation disabled

Without context isolation, preload scripts and website renderer code share a common global window object. APIs can be exposed by attaching properties directly: window.myAPI = { doAThing: () => {} } in the preload script, then accessed as window.myAPI.doAThing() in the renderer.

contextBridge module for safe API exposure

The contextBridge module is used to safely expose APIs from a preload script's isolated context to the website's context when context isolation is enabled. Use contextBridge.exposeInMainWorld('myAPI', { doAThing: () => {} }) in the preload script to expose APIs that are then accessible as window.myAPI from the renderer, similar to the pre-context-isolation approach but securely.

contextBridge limitations

The contextBridge module cannot send custom prototypes or symbols over the bridge.

Unsafe contextBridge pattern: exposing ipcRenderer directly

Directly exposing ipcRenderer.send via contextBridge.exposeInMainWorld('myAPI', { send: ipcRenderer.send }) is unsafe because it allows any website to send arbitrary IPC messages without filtering. Instead, provide one method per IPC message that performs argument validation.

Safe contextBridge pattern: wrapping IPC methods

Instead of exposing ipcRenderer directly, wrap individual IPC operations: contextBridge.exposeInMainWorld('myAPI', { loadPreferences: () => ipcRenderer.invoke('load-prefs') }). This provides one method per IPC message and allows filtering of arguments.

TypeScript types for contextBridge exposed APIs

When building an Electron app with TypeScript and using contextBridge, create a declaration file (e.g., interface.d.ts) to augment the global Window interface with your exposed APIs. Define an interface for your API (e.g., IElectronAPI with properties like loadPreferences: () => Promise<void>) and declare it on the global Window interface. This ensures the TypeScript compiler recognizes exposed APIs on window when writing renderer scripts.

TypeScript declaration file example for contextBridge

Example declaration file content: export interface IElectronAPI { loadPreferences: () => Promise<void>, } declare global { interface Window { electronAPI: IElectronAPI } } This allows TypeScript to recognize window.electronAPI.loadPreferences() in renderer code.

contextBridge does not guarantee security by itself

Simply enabling contextIsolation and using contextBridge does not automatically make everything safe. You must still implement proper argument filtering and validation when exposing APIs to prevent websites from misusing powerful functionality.

Migration from context isolation disabled to enabled

When migrating from context isolation disabled (where APIs were attached to window directly) to enabled (using contextBridge), the API usage in renderer code remains the same from the website's perspective—window.myAPI is still accessed the same way—but the underlying mechanism changes from direct property attachment to safe contextBridge exposure.

contextBridge API types and structure

The api provided to contextBridge must be a Function, string, number, Array, boolean, or an object whose keys are strings and values are a Function, string, number, Array, boolean, or another nested object meeting the same conditions. Function values are proxied to the other context. All other values are copied and frozen; any data or primitives sent in the API become immutable and updates on either side do not result in updates on the other side.

contextBridge parameter and return type support table

Type support for contextBridge: string (Simple, ✅ parameter, ✅ return, N/A limitations); number (Simple, ✅ parameter, ✅ return, N/A); boolean (Simple, ✅ parameter, ✅ return, N/A); Object (Complex, ✅ parameter, ✅ return, keys must be simple types, values must be supported, prototype modifications dropped, custom classes copy values but not prototype); Array (Complex, ✅ parameter, ✅ return, same limitations as Object); Error (Complex, ✅ parameter, ✅ return, message and stack trace may change due to different context, custom properties lost); Promise (Complex, ✅ parameter, ✅ return, N/A); Function (Complex, ✅ parameter, ✅ return, prototype modifications dropped, classes/constructors will not work); Cloneable Types (Simple, ✅ parameter, ✅ return, see structured clone algorithm); Element (Complex, ✅ parameter, ✅ return, prototype modifications dropped, custom elements will not work); Blob (Complex, ✅ parameter, ✅ return, N/A); VideoFrame (Complex, ✅ parameter, ✅ return, N/A); Symbol (N/A, ❌ parameter, ❌ return, cannot be copied across contexts so are dropped).

Cannot expose ipcRenderer over contextBridge

Attempting to send the entire ipcRenderer module as an object over the contextBridge will result in an empty object on the receiving side of the bridge. Sending ipcRenderer in full can let any code send any message, which is a security issue. Instead, provide a safe wrapper that exposes only specific ipcRenderer methods needed by the renderer, such as a function that calls ipcRenderer.on() with a specific event name.

Can I expose Node.js global symbols via contextBridge

Yes, the contextBridge can be used by the preload script to give the renderer access to Node APIs. The table of supported types also applies to Node APIs exposed through contextBridge. Many Node APIs grant access to local system resources, so be very cautious about which globals and APIs you expose to untrusted remote content.

contextBridge exposeInMainWorld example

// Preload (Isolated World) const { contextBridge, ipcRenderer } = require('electron') contextBridge.exposeInMainWorld( 'electron', { doThing: () => ipcRenderer.send('do-a-thing') } ) // Renderer (Main World) window.electron.doThing() This example shows exposing a simple API that wraps an ipcRenderer.send() call to the renderer process.

contextBridge complex API example

const { contextBridge, ipcRenderer } = require('electron') contextBridge.exposeInMainWorld( 'electron', { doThing: () => ipcRenderer.send('do-a-thing'), myPromises: [Promise.resolve(), Promise.reject(new Error('whoops'))], anAsyncFunction: async () => 123, data: { myFlags: ['a', 'b', 'c'], bootTime: 1234 }, nestedAPI: { evenDeeper: { youCanDoThisAsMuchAsYouWant: { fn: () => ({ returnData: 123 }) } } } } ) This example demonstrates exposing functions, promises, async functions, data objects, and deeply nested object structures through contextBridge.

contextBridge exposeInIsolatedWorld example

const { contextBridge, ipcRenderer } = require('electron') contextBridge.exposeInIsolatedWorld( 1004, 'electron', { doThing: () => ipcRenderer.send('do-a-thing') } ) // Renderer (In isolated world id 1004) window.electron.doThing() This example shows exposing an API to a specific isolated world with ID 1004.

contextBridge exposing Node.js global symbols example

const { contextBridge } = require('electron') const crypto = require('node:crypto') contextBridge.exposeInMainWorld('nodeCrypto', { sha256sum (data) { const hash = crypto.createHash('sha256') hash.update(data) return hash.digest('hex') } }) This example shows how to expose a Node.js API (crypto module) through contextBridge in a preload script, allowing the renderer to use Node.js functionality in a controlled manner.

contextBridge safe ipcRenderer wrapper example

// Preload (Isolated World) contextBridge.exposeInMainWorld('electron', { onMyEventName: (callback) => ipcRenderer.on('MyEventName', (e, ...args) => callback(args)) }) // Renderer (Main World) window.electron.onMyEventName(data => { /* ... */ }) This example demonstrates the proper way to expose ipcRenderer functionality: by creating a safe wrapper that exposes only specific event listeners rather than the entire ipcRenderer module.

ipcRenderer cannot be sent over contextBridge

Starting with a recent change in Electron, ipcRenderer can no longer be sent over the contextBridge. Attempting to expose the entire ipcRenderer module results in an empty object being received, as exposing the full ipcRenderer is a security risk.

Main World definition

The Main World is the JavaScript context that your main renderer code runs in. By default, the page you load in your renderer executes code in this world.

Isolated World definition

When contextIsolation is enabled in your webPreferences (this is the default behavior since Electron 12.0.0), preload scripts run in an Isolated World. The preload scripts have access to Node.js and Electron APIs but are isolated from the renderer's main world context.

contextBridge is a safe bi-directional synchronous bridge

contextBridge creates a safe, bi-directional, synchronous bridge across isolated contexts. It allows preload scripts running in an isolated context to safely expose APIs to the renderer's main world while maintaining context isolation for security.

ipcRenderer can no longer be sent over contextBridge

As of Electron v40.330, ipcRenderer cannot be sent over the contextBridge. This is a breaking change in behavior.

ipcRenderer with context isolation enabled

If you want to call ipcRenderer from a renderer process with context isolation enabled, place the API call in your preload script and expose it using the contextBridge API.

Do not expose IpcRendererEvent to renderer for security

Do not expose the event argument from ipcRenderer.on to the renderer for security reasons. Wrap any callback received from the renderer in another function like this: ipcRenderer.on('my-channel', (event, ...args) => callback(...args)). Not wrapping the callback would expose dangerous Electron APIs to the renderer process.

Sandboxed renderer process API subset

In sandboxed renderers, the process object contains only a subset of APIs: crash(), hang(), getCreationTime(), getHeapStatistics(), getBlinkMemoryInfo(), getProcessMemoryInfo(), getSystemMemoryInfo(), getSystemVersion(), getCPUUsage(), uptime(), argv, execPath, env, pid, arch, platform, sandboxed, contextIsolated, type, version, versions, mas, windowsStore, and contextId.

process.sandboxed property

process.sandboxed is a readonly boolean that is true when the renderer process is sandboxed, and undefined otherwise.

process.contextIsolated property

process.contextIsolated is a readonly boolean that indicates whether the current renderer context has contextIsolation enabled. It is undefined in the main process.

contextBridge for secure API exposure

Use the contextBridge module to securely expose APIs from preload scripts to the renderer: const { contextBridge } = require('electron') contextBridge.exposeInMainWorld('myAPI', { desktop: true }) The renderer can then access window.myAPI.

Context Isolation enabled by default

Context Isolation is enabled by default. This means preload scripts are isolated from the renderer's main world to avoid leaking any privileged APIs into your web content's code. You cannot directly attach variables from the preload script to window global due to context isolation.

Direct window assignment fails with context isolation

Attempting to directly assign to window global in a preload script does not work with context isolation enabled: window.myAPI = { desktop: true } In the renderer, console.log(window.myAPI) returns undefined.

ClipboardItem security warning for constructor

Do not construct a ClipboardItem directly from an untrusted object such as a payload received from a renderer over IPC. MIME keys are a capability surface: 'text/uri-list' places real file references on the OS clipboard, and 'electron application/osclipboard;format=...' and 'web'-prefixed formats write raw platform data. Validate and allowlist the MIME types and payload shape before building a ClipboardItem from data you did not author.

Example: Set certificate verify proc

const { BrowserWindow } = require('electron') const win = new BrowserWindow() win.webContents.session.setCertificateVerifyProc((request, callback) => { const { hostname } = request if (hostname === 'github.com') { callback(0) } else { callback(-2) } })

ses.setCertificateVerifyProc() method signature and callback values

ses.setCertificateVerifyProc(proc) sets the certificate verify proc for the session. proc is Function | null that receives request (Object with hostname, certificate, validatedCertificate, isIssuedByKnownRoot boolean, verificationResult string, errorCode Integer) and callback (Function with verificationResult Integer parameter). Callback values: 0 indicates success and disables Certificate Transparency verification; -2 indicates failure; -3 uses the verification result from chromium. Calling setCertificateVerifyProc(null) reverts to default certificate verify proc. Result is cached by the network service.

ses.setPermissionCheckHandler() method and permission types

ses.setPermissionCheckHandler(handler) sets the handler for responding to permission checks. handler is Function<boolean> | null with parameters: webContents (WebContents | null - null for cross-origin subframes and certain checks like notifications), permission (string - permission check type), requestingOrigin (string - origin URL), details (Object with optional embeddingOrigin, securityOrigin, mediaType, requestingUrl, isMainFrame, filePath, isDirectory, fileAccessType). Returns true to allow permission, false to deny. Call setPermissionCheckHandler(null) to clear. Permission types: clipboard-read, clipboard-sanitized-write, geolocation, fullscreen, hid, idle-detection, media, mediaKeySystem, midi, midiSysex, notifications, openExternal, pointerLock, serial, storage-access, top-level-storage-access, usb, deprecated-sync-clipboard-read, fileSystem. Must implement setPermissionRequestHandler as well. Note: isMainFrame will always be false for fileSystem requests due to Chromium limitations.

ses.setSSLConfig() method parameters

ses.setSSLConfig(config) sets the SSL configuration for the session; all subsequent network requests use the new configuration. config parameter is an Object with: minVersion (string, optional) - can be 'tls1', 'tls1.1', 'tls1.2', or 'tls1.3', defaults to 'tls1'; maxVersion (string, optional) - can be 'tls1.2' or 'tls1.3', defaults to 'tls1.3'; disabledCipherSuites (Integer[], optional) - list of cipher suites to prevent from use beyond those disabled by net policy. Supported form: 0xAABB where AA is cipher_suite[0] and BB is cipher_suite[1] per RFC 2246. TLSv1.3 ciphers cannot be disabled via this mechanism. Existing connections like WebSocket will not terminate, but old sockets in pool will not reuse for new connections.

Example: Set permission check handler

const { session } = require('electron') const url = require('node:url') session.fromPartition('some-partition').setPermissionCheckHandler((webContents, permission, requestingOrigin) => { if (new URL(requestingOrigin).hostname === 'some-host' && permission === 'notifications') { return true // granted } return false // denied })

Give your agent this brain