Is Your Electron Clipboard App Spying? How to Check Its IPC
Electron apps are not single binaries in the old Win32 sense. They are a Node.js main process plus one or more Chromium renderer processes, glued together by an IPC channel. For a clipboard manager, that architecture matters: every copied item, every screenshot, every pinned token flows across the IPC bridge, and the bridge's shape decides what a malicious or compromised webpage could reach. This guide shows how a non-developer can read an Electron app's IPC surface from outside, what the three relevant settings actually buy, and where the audit stops being possible without source. For neighbouring reading, see Apache-2.0 clipboard tools you can actually audit, the open-source clipboard landscape in 2026, what local-first means for clipboard apps, and managers that stay local on purpose.
What IPC actually is
Electron splits every app into two kinds of process. The main process is Node.js: it can read and write files, spawn children, talk to the OS, and create windows. The renderer process is Chromium: it runs the HTML, CSS, and JavaScript that the user sees. The two cannot share memory directly. They talk over an IPC channel — ipcMain on one side, ipcRenderer on the other, with a preload script sitting between the renderer page and the real Node APIs.
That channel is the security boundary. If the renderer can call arbitrary ipcRenderer.invoke channels, and those channels expose the filesystem, the app is effectively running with full Node privileges inside a webpage. If the renderer only sees a narrow contextBridge.exposedInMainWorld API with five named methods, the surface is small. Reading an Electron app's IPC surface means asking one question: how much of Node does the renderer see, and through what shape of API?
The three settings that decide
Electron's process model exposes three BrowserWindow webPreferences flags that, taken together, decide the IPC surface. The official Electron security documentation lists these as the defaults you should not weaken.
| Setting | Safe default | What it does | What it prevents |
|---|---|---|---|
contextIsolation | true | Separates the renderer's JS world from the preload's JS world | A compromised webpage cannot overwrite window.fetch to intercept preload calls |
nodeIntegration | false | The renderer page cannot require('fs') or require('child_process') | A malicious page cannot spawn shells or read arbitrary files |
sandbox | true | The renderer is sandboxed; even the preload loses most Node APIs | Limits blast radius even if a renderer bug is found |
When all three are at the safe defaults, the renderer can only do what a normal webpage can do, plus whatever the preload explicitly exposes through contextBridge. When nodeIntegration is true, every renderer window is effectively a Node shell with a webpage attached. When contextIsolation is false, the renderer page can mutate globals the preload installed, which has historically led to prototype pollution and same-origin bypasses. When sandbox is false, the preload keeps full Node access, which means a single bug in the preload is a Node-level bug.
Electron's documentation is direct about this. The security tutorial states that contextIsolation is the single most important flag and that disabling it removes the protection that contextBridge provides.
How to inspect an app without source
A user without the source code can still learn a surprising amount. The steps below work for any installed Electron app on Windows, including closed-source ones.
Step 1: confirm it is Electron
Look in the install directory. If you see resources/app.asar or resources/app/, the app is Electron. The version is usually in resources/app.asar (unpacked with npx asar extract) or in a package.json inside that archive.
Step 2: extract the asar and read the main process code
npx @electron/asar extract resources/app.asar ./app-source
The main process code is typically in main.js, background.js, or src/main/. Search for new BrowserWindow and inspect the webPreferences block. That block is where the three flags live. If you see nodeIntegration: true or contextIsolation: false, that is a finding worth noting in the app's issue tracker.
Step 3: read the preload
The preload is usually a single file referenced from the BrowserWindow constructor as webPreferences.preload. It is the only place that should touch Node directly. A well-shaped preload looks like this:
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('edgeDropAPI', {
pinItem: (id) => ipcRenderer.invoke('pin:item', id),
unpinItem: (id) => ipcRenderer.invoke('unpin:item', id),
clearAll: () => ipcRenderer.invoke('clear:all'),
});
A poorly-shaped preload exposes ipcRenderer directly:
const { ipcRenderer } = require('electron');
window.ipcRenderer = ipcRenderer; // do not do this
The first shape lets the renderer call four named channels. The second lets the renderer call anything ipcMain has registered, including channels the renderer was never meant to use.
Step 4: enumerate ipcMain.handle calls
Inside the extracted source, search for ipcMain.handle( and ipcMain.on(. Each call is a channel. Count them, read their handlers, and ask whether the handler trusts its inputs. A handler like ipcMain.handle('read:file', (e, path) => fs.readFileSync(path)) is a directory traversal bug waiting to happen. A handler that validates path against an allowlist is fine.
What you cannot learn from outside
There is a hard limit to a binary audit. You can read the JS shipped in the asar, but you cannot see what the main process does at runtime when it receives a clipboard event, what it logs, or what it sends over the network. For that, you need either source code or runtime instrumentation.
Reasonable middle grounds:
- Source-available apps (Apache-2.0, MIT, MPL) let you read everything. Edge-Drop, Espanso, CopyQ, and Ditto all fall here.
- Network monitoring with Wireshark or Microsoft's built-in
pktmonshows whether an app opens unexpected sockets. This is what telemetry-free desktop utilities: how to check walks through. - Process Monitor from Sysinternals shows filesystem and registry activity, including writes outside the install directory.
These three together give you most of what an audit needs. For a closed-source Electron app, you can read the asar but you cannot read a server it talks to.
A worked example: reading Edge-Drop's surface
Edge-Drop is Electron 34, React 18, Apache-2.0, source on GitHub. Reading its BrowserWindow construction shows the defaults the brief lists: contextIsolation: true, nodeIntegration: false, sandbox: true on the shelf window. The preload exposes a small contextBridge API for pin, unpin, drag-out, and a few shelf operations. Clipboard polling happens in the main process, not in the renderer, which is the right place for it — the renderer only sees the items the main process chooses to forward.
This is not a unique posture. Most actively-maintained Electron apps in 2026 ship these defaults because Electron's own boilerplate (electron-forge and electron-vite) sets them. What is worth checking is whether the app overrides them. A webPreferences: { nodeIntegration: true } in a 2026 app is a red flag worth an issue.
What this buys you as a user
The point of reading the IPC surface is not to find bugs yourself. It is to ask two questions before installing:
- Does the renderer have direct Node access? If yes, any webpage the app loads (including a remote URL it fetches for changelogs, a settings page, or a login form) can read files and spawn processes. This is the original Vector editorial bug class.
- Does the preload expose a narrow surface? If the preload does
window.ipcRenderer = ipcRenderer, the answer is no, and the entireipcMainregistration set is in scope.
A clipboard app that handles files, images, and tokens should answer "no" to the first and "yes" to the second. If it does not, the question of whether you should run a public beta gets harder, and the question of whether the app could disappear gets more acute — because a closed binary with weak IPC isolation and a quiet maintainer is a long-term liability.
Honest limits
Reading an asar tells you what the app shipped with. It does not tell you what the app does at runtime, what it logs, or whether a future update will widen the surface. For that, watch the changelog and the diff between releases. A small, well-scoped IPC surface that grows by one or two channels per release is normal. A surface that grows by twenty, or that adds shell.openExternal calls to user-controlled strings, is worth a question on the issue tracker.
For most users, the practical takeaway is narrower: prefer apps whose source you can read, whose webPreferences match the Electron defaults, and whose preload exposes a small named API. That combination does not guarantee safety, but it is the cheapest signal you have, and it costs nothing to check.
Related reading
- Open Source Clipboard Landscape in 2026
- How to Evaluate a Public-Beta Desktop App
- What Local-First Software Means for Clipboard Apps
- How to Enable Clipboard History in Windows 11
Sources
- Electron — Security documentation — official list of process-model security flags including
contextIsolation,nodeIntegration, andsandbox - Electron — Context Isolation — explains why
contextIsolation: trueis the default and what disabling it removes - Electron — Process Model — describes the main, renderer, and preload process split that IPC sits between
- Electron — contextBridge API — the recommended shape for exposing a narrow API from preload to renderer
- Electron — Sandbox — explains what
sandbox: trueremoves from the preload and why it is the default
Deepender Yadav is a B.Tech Computer Science Engineering student and software developer interested in building practical software and open-source projects.
GitHub · LinkedInCopy. Stack. Drop.
Transform your clipboard into an interactive edge shelf. Stack, pin, and drag assets into any app with zero friction.
Download for Windows Get from Microsoft Store
How to Install Guide · First 10 Minutes Guide · Drag & Drop Guide · Edge-Drop vs Win+V · Support
Free · Lightweight · Privacy First