Electron security
Skill ShieldNet-360/secure-vibe/dist/agent-skills/.agents/skills/electron-security
SecureVibe — prevention-first security for AI-written code. Signed SKILL.md knowledge that makes AI coding assistants write secure code at generation time, plus a deterministic CI gate. Offline · keyless · Ed25519-signed. By ShieldNet360.
npx -y skills add ShieldNet-360/secure-vibe --skill electron-securityAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Harden Electron apps: renderer trust boundary (nodeIntegration, contextIsolation, sandbox), contextBridge/IPC allowlists, shell.openExternal, navigation guards, deep-link auth, safeStorage — Applies to: when generating Electron main-process code (BrowserWindow, ipcMain, app events); when generating a preload script or contextBridge surface; when wiring custom-protocol / deep-link handlers; when storing tokens or secrets in an Electron app; when reviewing Electron IPC, navigation, or window configuration
SKILL.md
6.4 KB, as published. Nobody here has run it
Electron Desktop Security
Harden Electron apps: renderer trust boundary (nodeIntegration, contextIsolation, sandbox), contextBridge/IPC allowlists, shell.openExternal, navigation guards, deep-link auth, safeStorage
ALWAYS
- Configure every
BrowserWindowwithnodeIntegration: false,contextIsolation: true, andsandbox: true. The preload + contextBridge is the supported way to give the renderer capabilities — the page never needs Node. - Expose a minimal, typed API from the preload via
contextBridge.exposeInMainWorld. Expose named functions only — never hand the rendereripcRenderer,require,process, or whole modules. - Validate every IPC argument in the main-process handler: type-check, bound, and allowlist. The renderer is an attacker-controlled input source.
- Spawn child processes with
execFile/spawnand an argument array — neverexecwith a shell string built from renderer input. Allowlist each argument (e.g.^[A-Za-z0-9_-]+$). - Confine filesystem paths:
path.resolve(base, input)then verify the resultstartsWith(base + path.sep). Reject absolute paths and..segments. - Allowlist
shell.openExternaltohttps:(andmailto:if needed) after parsing the URL. Rejectfile:, custom schemes, and anything else. - Add navigation guards:
app.on('web-contents-created', …)withcontents.on('will-navigate', …)andcontents.setWindowOpenHandler(…)that deny by default against a strict origin allowlist. - Remember the contextBridge surface is exposed to whatever origin the webContents currently holds —
exposeInMainWorlddoes not re-check origin after a navigation. So a single missingwill-navigateguard lets a remote / attacker origin inherit your entire IPC surface (this is how a stored hyperlink → navigation becomes 1-click RCE). The nav guard is the primary control; as defense-in-depth, gate the preload onlocationbefore exposing. - Before attaching session tokens / cookies to an outbound request, verify the target host is on your own-API allowlist. Never attach credentials to a renderer-supplied URL.
- Bind custom-protocol / deep-link auth to a one-time
state/ PKCE value the app generated and is waiting for; validate before storing any token. - Store tokens with Electron
safeStorage(OS keychain / DPAPI / libsecret), not app-level crypto. Enable ASAR integrity + code signing for release. - Treat every server the app connects to — backend, simulation / compute node, auto-update channel, a multi-tenant cloud session — as potentially attacker-controlled (compromised, co-tenant, or MITM). Never load a server URL into a
BrowserWindow/<webview>that carries your preload, and never feed a server response into an IPC sink (file path, shell arg,openExternalURL) without the same validation you apply to renderer input. - Harden parsers that consume untrusted server / stream data (binary frames, SDF / XML, model files): bound every length field before allocating, cap recursion and
<include>-style expansion (circular refs → infinite loop / fetch), and wrap the parse in try/catch. Otherwise a malicious server crashes or hangs the renderer (DoS), even when memory-safety prevents RCE.
NEVER
- Set
nodeIntegration: true, disablecontextIsolation, disablewebSecurity, or setallowRunningInsecureContent: true— especially when the window loads remote or navigable content. - Concatenate renderer input into a shell string (
child_process.exec(\docker kill ${names}`)`) — command injection. - Concatenate a renderer-supplied path for
fsread/write (${BASE}${filePath}) — path traversal / arbitrary file write. - Call
shell.openExternalon an arbitrary or renderer-controlled URL —file:/ custom protocol handlers are a local-launch / RCE vector. - Attach
Authorization/ session cookies to a URL the renderer chose without a host allowlist — XSS then exfiltrates the token. - Accept a deep-link auth token (
myapp://auth?refresh-token=…) without origin /statevalidation — login CSRF / session fixation. - Encrypt tokens at rest with AES-CBC (no integrity) or a key derived solely from a locally recoverable machine ID, and never keep a
PLAINTEXT:fallback path. - Ship a frameless / chromeless navigable window (
frame: false, no address bar): after a redirect the user has no visual cue they left the app, so a phished navigation can silently clone your UI. Frameless is acceptable only behind a hard navigation guard. - Assume the renderer (or its rendered content) is the only untrusted input — a backend / sim / update server the app trusts can itself be compromised or, in multi-tenant deployments, driven by another tenant.
KNOWN FALSE POSITIVES
- Dev builds that load over
http://localhost:<port>with relaxed settings — the rules apply to release builds; ensure dev config never ships. - A custom protocol (
myapp://) for OAuth callbacks is expected — the control isstate/PKCE validation, not the scheme's existence. contextBridge-exposed functions are intentional capabilities; review what each one does (and whether it validates input), not the fact that the bridge exists.shell.openExternalon a hard-codedhttps://constant (not user input) is fine.- A frameless window that loads only local first-party content (
file:/// packaged app) behind a deny-by-default nav guard is fine — the risk is frameless plus navigable to remote origins. - Connecting to a backend and rendering its data (telemetry JSON, numbers, binary frames) is normal desktop behaviour. The control is bounding / validating that data and never treating it as HTML, a filesystem path, or a shell argument — not avoiding the connection itself.