agentsclimarketplace

Electron security

Skill ShieldNet-360/secure-vibe/dist/agent-skills/.agents/skills/electron-security

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 configurationFrom its SKILL.md

Install
npx -y skills add ShieldNet-360/secure-vibe --skill electron-security

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 15 stars15 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.

SKILL.md

6.4 KB, ~1.3k tokens by cl100k_base, as published. Nobody here has run it

<!-- Native skill bundle for agent-skills (cross-tool convention). Generated by `secure-vibe dev regenerate`. --> <!-- Do not edit by hand; the source of truth is skills/electron-security/SKILL.md. -->

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 BrowserWindow with nodeIntegration: false, contextIsolation: true, and sandbox: 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 renderer ipcRenderer, 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 / spawn and an argument array — never exec with 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 result startsWith(base + path.sep). Reject absolute paths and .. segments.
  • Allowlist shell.openExternal to https: (and mailto: if needed) after parsing the URL. Reject file:, custom schemes, and anything else.
  • Add navigation guards: app.on('web-contents-created', …) with contents.on('will-navigate', …) and contents.setWindowOpenHandler(…) that deny by default against a strict origin allowlist.
  • Remember the contextBridge surface is exposed to whatever origin the webContents currently holdsexposeInMainWorld does not re-check origin after a navigation. So a single missing will-navigate guard 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 on location before 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, openExternal URL) 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, disable contextIsolation, disable webSecurity, or set allowRunningInsecureContent: 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 fs read/write (${BASE}${filePath}) — path traversal / arbitrary file write.
  • Call shell.openExternal on 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 / state validation — 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 is state/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.openExternal on a hard-coded https:// 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.

What ships with it: 1 file

1.3 KB alongside SKILL.md

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.