93 electron app security review
Skill FluxonLab/Skillry/plugins/security/skills/93-electron-app-security-review
Installable, permission-bounded, multi-platform agent skills & subagents for Claude Code, Codex, Copilot & Gemini/Antigravity — 125 skills + 73 subagents across 18 departments, with a validation harness, native plugin marketplace, and full upstream attribution. by FluxonLab.
npx -y skills add FluxonLab/Skillry --skill 93-electron-app-security-reviewAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 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
Use when you need to audit the internal security of an Electron application — context isolation, IPC input validation, contextBridge API surface, preload script hygiene, renderer CSP, navigation restrictions, and auto-update signature verification. Distinct from desktop launcher/packaging review.
SKILL.md
11.6 KB, as published. Nobody here has run it
Electron App Security Review
Purpose
Audit the runtime security of an Electron application from the inside: verify that contextIsolation, sandboxing, and webSecurity are configured correctly, that IPC channels validate all inputs and expose a minimal contextBridge API, that preload scripts do not leak Node.js APIs to the renderer, that the renderer Content Security Policy blocks script injection, that navigation is restricted to known origins, and that auto-update mechanisms verify signatures. This skill focuses on in-process security — not build/packaging (see skill 28 for that).
When to use
- A PR modifies
BrowserWindowoptions,contextBridgeexposure,ipcMainhandlers, or preload scripts. - A security audit is requested for an Electron app before a public release.
- A bug report hints at renderer-to-main privilege escalation or arbitrary code execution from a compromised renderer.
- The app loads remote web content (URLs, iframes,
webview) that could be attacker-controlled. - You are onboarding into an Electron project and need to establish its security baseline.
When not to use
- The task is purely UI/UX or involves only renderer-side React/TypeScript — no Electron APIs involved.
- The issue is a packaging or code-signing problem — use the desktop launcher review skill instead.
- The app has no IPC and renders only local static files with no user input.
Procedure
1. Audit BrowserWindow security options
# Find all BrowserWindow instantiations
grep -rn "new BrowserWindow\|BrowserWindow({" src/ electron/ main/ --include="*.ts" --include="*.js"
# Check for insecure option combinations
grep -rn "contextIsolation\|nodeIntegration\|sandbox\|webSecurity\|allowRunningInsecureContent" \
src/ electron/ main/ --include="*.ts" --include="*.js"
Critical combinations to flag:
nodeIntegration: true— grants full Node.js to the renderer; Critical unless the app is a trusted local tool with no remote content.contextIsolation: false— removes the JS context boundary; Critical.sandbox: false— disables OS-level sandboxing; High.webSecurity: false— disables same-origin policy; High unless localhost-only dev override.allowRunningInsecureContent: true— allows HTTP in HTTPS webview; High.
Expected safe configuration:
webPreferences: {
contextIsolation: true, // REQUIRED
nodeIntegration: false, // REQUIRED
sandbox: true, // REQUIRED in Electron ≥ 20
webSecurity: true, // default; never set false in production
preload: path.join(__dirname, 'preload.js'),
}
2. Review contextBridge and preload script exposure
# Find all contextBridge.exposeInMainWorld calls
grep -rn "contextBridge\.exposeInMainWorld\|exposeInMainWorld" \
src/ electron/ preload/ --include="*.ts" --include="*.js"
# Find any direct ipcRenderer exposure (dangerous anti-pattern)
grep -rn "ipcRenderer" preload/ src/ --include="*.ts" --include="*.js" | head -30
# Check if the entire ipcRenderer object is exposed
grep -rn "ipcRenderer\s*[,}]" preload/ src/ --include="*.ts" --include="*.js"
Anti-pattern — never expose raw ipcRenderer:
// WRONG: full ipcRenderer in renderer context
contextBridge.exposeInMainWorld('electron', { ipcRenderer })
Correct pattern — expose only named channels:
contextBridge.exposeInMainWorld('api', {
getUser: () => ipcRenderer.invoke('get-user'),
saveFile: (data: string) => ipcRenderer.invoke('save-file', data),
// No: send, on, removeAllListeners exposed generically
})
# Check for require() or __dirname in renderer files (should not exist with contextIsolation)
grep -rn "require(\|__dirname\|__filename" src/renderer/ --include="*.ts" --include="*.js" | head -20
3. Audit ipcMain handler input validation
# List all ipcMain.handle and ipcMain.on registrations
grep -rn "ipcMain\.handle\|ipcMain\.on\|ipcMain\.once" \
src/ electron/ main/ --include="*.ts" --include="*.js"
# Check for handlers that use event.reply or invoke results without validation
grep -rn -A 10 "ipcMain\.handle(" src/ electron/ main/ --include="*.ts" --include="*.js" \
| grep -B 5 "as any\|unknown\|@ts-ignore" | head -30
For each ipcMain.handle, verify:
- Argument types are validated (zod, manual type guards, or at minimum
typeofchecks). event.senderFrame.urlis checked against an allowlist before processing sensitive operations.- File paths passed from the renderer are validated against a safe base directory (path traversal).
# Path traversal risk: file operations with renderer-supplied paths
grep -rn "fs\.\|path\.join\|readFile\|writeFile\|unlink" \
src/ electron/ main/ --include="*.ts" --include="*.js" | head -30
4. Check renderer Content Security Policy
# CSP in main process (session.defaultSession.webRequest)
grep -rn "Content-Security-Policy\|contentSecurityPolicy\|webRequest\.onHeaders" \
src/ electron/ main/ --include="*.ts" --include="*.js"
# CSP meta tag in HTML entry points
grep -rn "Content-Security-Policy" src/ public/ --include="*.html"
# Check for unsafe-inline or unsafe-eval in CSP
grep -rn "unsafe-inline\|unsafe-eval" src/ electron/ public/ --include="*.ts" --include="*.html"
Minimum acceptable CSP for an Electron app loading only local files:
default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
connect-src 'self';
unsafe-eval is never acceptable. unsafe-inline in script-src is a Critical finding.
5. Restrict navigation and new window opening
# Check will-navigate handler
grep -rn "will-navigate\|webContents\.on.*navigate\|beforeunload" \
src/ electron/ main/ --include="*.ts" --include="*.js"
# Check setWindowOpenHandler / new-window
grep -rn "setWindowOpenHandler\|new-window\|window\.open" \
src/ electron/ main/ --include="*.ts" --include="*.js"
# Check webview tag usage (high risk; should use BrowserView or avoid entirely)
grep -rn "<webview\|webview:" src/ --include="*.tsx" --include="*.html"
Correct navigation restriction pattern:
mainWindow.webContents.on('will-navigate', (event, url) => {
if (!url.startsWith('file://') && !url.startsWith('https://your-app.com')) {
event.preventDefault()
}
})
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
shell.openExternal(url) // open in browser, not new Electron window
return { action: 'deny' }
})
6. Verify auto-update signature checking
# Find electron-updater or autoUpdater usage
grep -rn "electron-updater\|autoUpdater\|checkForUpdates" \
src/ electron/ main/ --include="*.ts" --include="*.js" | head -20
# Confirm publisherName or signature verification is configured
grep -rn "publisherName\|verifyUpdateCodeSignature\|allowDowngrade\|allowPrerelease" \
electron-builder.yml electron-builder.json package.json 2>/dev/null
Checks:
electron-updatermust verify the update package signature before applying it.publisherNamemust match the code-signing certificate used in releases.autoUpdater.allowDowngrade = false— prevent rollback attacks.- Update channel URL must use HTTPS.
7. Check for dangerous APIs exposed to renderer
# shell.openExternal with user-supplied URL (open redirect / RCE risk)
grep -rn "shell\.openExternal" src/ electron/ --include="*.ts" --include="*.js"
# child_process or exec accessible from IPC handlers
grep -rn "child_process\|exec(\|spawn(\|execFile(" \
src/ electron/ main/ --include="*.ts" --include="*.js" | head -20
# eval or Function constructor in main process
grep -rn "\beval(\|new Function(" src/ electron/ main/ --include="*.ts" --include="*.js"
Concrete checks
-
contextIsolation: truein everyBrowserWindowwebPreferences. -
nodeIntegration: falsein everyBrowserWindowwebPreferences. -
sandbox: trueset (Electron ≥ 20 default, but verify no override). -
webSecurity: falseis absent from production code paths. - No raw
ipcRendererobject exposed viacontextBridge. - Every
ipcMain.handlevalidates argument types before processing. -
event.senderFrame.urlis checked for sensitive IPC channels. - File paths from renderer are resolved and confirmed within a safe base directory.
- CSP is set via
webRequest.onHeadersReceived; nounsafe-evalinscript-src. -
will-navigateevent blocks navigation to unknown origins. -
setWindowOpenHandlerreturns{ action: 'deny' }and opens URLs in external browser. -
<webview>tags are absent or havedisablewebsecurityunset. -
autoUpdater/electron-updaterverifies update signatures (publisherNameconfigured). -
shell.openExternalonly receives validated, allowlisted URLs — never raw user input. - No
child_process.execorevalreachable from IPC input.
Commands
# One-shot security baseline check
echo "=== BrowserWindow insecure options ==="
grep -rn "nodeIntegration: true\|contextIsolation: false\|sandbox: false\|webSecurity: false" \
src/ electron/ main/ --include="*.ts" --include="*.js"
echo "=== Raw ipcRenderer exposure ==="
grep -rn "exposeInMainWorld.*ipcRenderer\|ipcRenderer\s*[,}]" \
preload/ src/ --include="*.ts" --include="*.js"
echo "=== CSP unsafe directives ==="
grep -rn "unsafe-eval\|unsafe-inline.*script" src/ public/ electron/ --include="*.ts" --include="*.html"
echo "=== shell.openExternal calls ==="
grep -rn "shell\.openExternal" src/ electron/ --include="*.ts" --include="*.js"
echo "=== child_process in main ==="
grep -rn "child_process\|exec(\|spawn(" src/ electron/ main/ --include="*.ts" --include="*.js"
echo "=== Auto-update config ==="
grep -rn "publisherName\|verifyUpdateCodeSignature" electron-builder.* package.json 2>/dev/null
Required output
Produce a structured report with:
- BrowserWindow configuration — table of each window's key security options with pass/fail.
- contextBridge exposure audit — list every exposed API; flag any that expose raw
ipcRendereror Node APIs. - IPC handler audit — for each
ipcMain.handle/on, validation status (present / absent / partial) and sender-verification status. - CSP status — current policy text, any
unsafe-*violations, recommended policy. - Navigation restrictions — presence and correctness of
will-navigateandsetWindowOpenHandler. - Auto-update findings — signature verification configured or not; update URL protocol.
- Severity-ranked findings — Critical / High / Medium / Low with file:line and concrete fix.
- Next safe action — the single most critical remediation step.
Safety checks
- Do not execute any IPC calls or trigger auto-update during the review.
- Do not print or log secret values found in source files (API keys, tokens).
- Read-only analysis only; do not modify
BrowserWindowconfigs or preload scripts without explicit approval. - If a Critical finding (e.g.
nodeIntegration: truewith remote content) is found, recommend blocking the PR before merge.
Completion criteria
Done means: every BrowserWindow instance is checked for the five core security options, every contextBridge exposure is listed and assessed, every ipcMain handler has a validation status, CSP is documented and evaluated, navigation restrictions are confirmed or flagged, auto-update signature verification is confirmed or flagged, and every finding has file:line + severity + concrete fix.