desktop-framework-electron-e9a79b — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited desktop-framework-electron-e9a79b (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Quick Guide: Electron apps run two process types: a main process (Node.js, manages windows and system APIs) and renderer processes (Chromium, one per window). All communication between them flows through IPC via a preload script that usescontextBridgeto expose a minimal, typed API surface. Never disablecontextIsolation. Never enablenodeIntegrationin renderers. Package with Electron Forge or Electron Builder. Auto-update viaautoUpdater(Squirrel on macOS/Windows) orelectron-updaterfor all platforms.
<critical_requirements>
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)(You MUST keep `contextIsolation: true` (the default) -- disabling it exposes the entire preload scope to untrusted renderer code)
(You MUST use `contextBridge.exposeInMainWorld()` in preload scripts -- never expose `ipcRenderer` directly)
(You MUST NOT enable `nodeIntegration: true` in any BrowserWindow -- it gives renderers full Node.js access, which is a critical security vulnerability)
(You MUST validate and sanitize ALL data received via IPC in the main process -- treat renderer messages as untrusted input)
(You MUST use `ipcMain.handle()` / `ipcRenderer.invoke()` for request-response IPC -- avoid `sendSync` which blocks the renderer)
(You MUST NOT load remote URLs with `nodeIntegration` or disabled `contextIsolation` -- this is equivalent to giving the remote site full system access)
</critical_requirements>
Auto-detection: Electron, electron, BrowserWindow, ipcMain, ipcRenderer, contextBridge, preload, webPreferences, electron-builder, electron-forge, app.whenReady, electronAPI, mainWindow, autoUpdater, nativeTheme, safeStorage, Tray, Menu, dialog, protocol, shell
When to use:
When NOT to use:
<patterns>
Every BrowserWindow must use a preload script and rely on the secure defaults: contextIsolation: true, sandbox: true, nodeIntegration: false.
const mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
preload: path.join(__dirname, "preload.js"),
// contextIsolation: true -- default since Electron 12
// sandbox: true -- default since Electron 20
// nodeIntegration: false -- default since Electron 5
},
});Key point: Never override the security defaults. The preload script is the ONLY bridge between main and renderer. See examples/core.md.
The preload script exposes a narrow, explicitly typed API to the renderer. Never expose ipcRenderer directly.
// preload.js
const { contextBridge, ipcRenderer } = require("electron");
contextBridge.exposeInMainWorld("electronAPI", {
readFile: (filePath) => ipcRenderer.invoke("read-file", filePath),
onUpdateAvailable: (callback) => {
ipcRenderer.on("update-available", (_event, data) => callback(data));
},
});Key point: Each exposed method wraps a single IPC channel. The renderer calls window.electronAPI.readFile(path) with no knowledge of IPC internals. See examples/core.md.
Use ipcMain.handle() in main and ipcRenderer.invoke() in preload for async two-way communication.
// main process
ipcMain.handle("read-file", async (_event, filePath) => {
const content = await fs.readFile(filePath, "utf-8");
return { success: true, content };
});Key point: handle/invoke returns a Promise. Always validate filePath in the handler -- never trust renderer input. See examples/ipc.md for all IPC patterns.
Use webContents.send() from main and listen in the preload with a callback pattern.
// main: send to specific window
mainWindow.webContents.send("update-progress", { percent: 45 });
// preload: expose listener
onUpdateProgress: (callback) => {
ipcRenderer.on("update-progress", (_event, data) => callback(data));
},Key point: The renderer cannot pull from main -- main must push. Always scope listeners to specific channels. See examples/ipc.md.
The main process manages the app lifecycle with platform-specific conventions.
app.whenReady().then(() => {
createWindow();
app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
app.on("window-all-closed", () => {
if (process.platform !== "darwin") app.quit();
});Key point: macOS apps stay alive when all windows close (window-all-closed should not quit). The activate event recreates a window when the dock icon is clicked. See examples/core.md.
Electron exposes native APIs for dialogs, menus, tray icons, notifications, and more -- all accessed from the main process.
const { dialog } = require("electron");
const result = await dialog.showOpenDialog(mainWindow, {
properties: ["openFile", "multiSelections"],
filters: [{ name: "Documents", extensions: ["txt", "md", "json"] }],
});Key point: Native dialogs are modal to a window when passed mainWindow as the first argument. See examples/native-apis.md.
Register your app to handle myapp:// URLs for deep linking from browsers or other apps.
if (process.defaultApp) {
app.setAsDefaultProtocolClient("myapp", process.execPath, [
path.resolve(process.argv[1]),
]);
} else {
app.setAsDefaultProtocolClient("myapp");
}Key point: In development (process.defaultApp is true), you must pass the script path as an argument. On macOS, handle the open-url event on the app object. On Windows/Linux, handle via second-instance event. See examples/native-apis.md.
</patterns>
<decision_framework>
Which IPC pattern?
+-- Renderer needs a response from main?
| +-- YES --> ipcMain.handle() + ipcRenderer.invoke()
+-- Renderer sends data, no response needed?
| +-- YES --> ipcMain.on() + ipcRenderer.send()
+-- Main needs to push data to renderer?
| +-- YES --> webContents.send() + ipcRenderer.on() (in preload)
+-- Two renderers need to communicate?
| +-- YES --> Route through main process (never direct renderer-to-renderer)
+-- High-frequency data transfer (streaming)?
+-- YES --> MessageChannelMain / MessagePort pairHow many windows?
+-- Single window app?
| +-- One BrowserWindow, one preload script
+-- Multi-window (e.g., preferences, about)?
| +-- Separate BrowserWindow per view, each with its own preload
+-- Frameless / custom title bar?
| +-- frame: false + custom drag regions via CSS (-webkit-app-region: drag)
+-- Persistent background work?
+-- Use a hidden BrowserWindow or utilityProcess (Electron 22+)</decision_framework>
Detailed resources:
<red_flags>
Critical Security Issues:
contextIsolation (contextIsolation: false) -- exposes preload globals to renderernodeIntegration: true -- gives renderer full Node.js access (fs, child_process, etc.)ipcRenderer directly via contextBridge instead of wrapping individual channelssandbox: truewebSecurity in production (webSecurity: false disables same-origin policy)shell.openExternal() with unvalidated URLs (can execute arbitrary commands)Architecture Issues:
ipcRenderer.sendSync() -- blocks the renderer process, causes UI freezesremote module (removed in Electron 14+, was a security and performance hazard)window-all-closed per-platform (macOS apps should not quit)Packaging Issues:
devDependencies in production builds (bloated app size)node_modules without pruning or using ASAR archive__dirname in renderer code (unavailable in sandboxed renderers)Common Mistakes:
app.whenReady() -- APIs are unavailable before the ready eventactivate event (macOS dock click does nothing)require() in renderer scripts loaded via <script> tags (not available in sandboxed renderers)nodeIntegrationInSubFrames: true for iframes loading external contentContent-Security-Policy headers for renderer HTML</red_flags>
<critical_reminders>
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)(You MUST keep `contextIsolation: true` (the default) -- disabling it exposes the entire preload scope to untrusted renderer code)
(You MUST use `contextBridge.exposeInMainWorld()` in preload scripts -- never expose `ipcRenderer` directly)
(You MUST NOT enable `nodeIntegration: true` in any BrowserWindow -- it gives renderers full Node.js access, which is a critical security vulnerability)
(You MUST validate and sanitize ALL data received via IPC in the main process -- treat renderer messages as untrusted input)
(You MUST use `ipcMain.handle()` / `ipcRenderer.invoke()` for request-response IPC -- avoid `sendSync` which blocks the renderer)
Failure to follow these rules will create severe security vulnerabilities or broken desktop applications.
</critical_reminders>
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.