DEV Community

Gyu
Gyu

Posted on • Originally published at qiita.com

Electron's docs quietly dropped their recommended security tool. What now?

Electron's docs quietly dropped their recommended security tool. What now?

If you've worked on an Electron app, you've probably seen the security checklist in the official docs. Until recently, that page also pointed to a static analysis tool called Electronegativity to help you check it automatically.

That recommendation is gone.

So right now, the docs just say "here are 20 items, check them by hand, and keep checking them on every release" — no tool recommendation at all.

This post covers what got removed, why, what you can use instead today, and (if you don't want to add a tool) the five spots worth checking by hand, with code.

This is an English write-up of an article I posted on Qiita, expanded a bit for a different audience.

Why it got removed

Electronegativity was released by Doyensec back in 2019 — an OSS tool that parses AST and DOM to catch Electron-specific misconfigurations and anti-patterns. For years it was the one tool the official docs pointed to.

Problem: feature development stopped around 2022. The repo itself says it's no longer being actively maintained, and it doesn't reliably keep up with newer Electron versions.

There was a paid successor, ElectroNG ($688/year, launched 2022), but that's not for sale anymore either — the buy page now just says to email them if you're interested in acquiring the source and infrastructure.

So the entire "dedicated Electron static analysis tool" slot is empty right now.

What can you use instead

Here's what I found when I went looking for what to actually run in CI.

CodeQL

General-purpose, but its standard query set includes some frameworks/electron queries that cover a handful of Electron-specific sinks. If you're already running CodeQL, this is close to free. It's not built around the Electron checklist specifically though, so don't expect full coverage.

Semgrep

You can write your own rules for individual checklist items. This works well in practice — writing a handful of narrow rules for the exact things that bit your app is a solid, low-effort first move.

One catch: dataflow analysis (taint tracking) in the free tier only works within a single function's scope. Cross-function/cross-file taint tracking is a paid Pro feature.

What general tools miss

ESLint plugins, npm audit, Snyk — these catch dependency CVEs and general JS lint issues just fine. What they don't catch, as a group, is the stuff specific to Electron's process model: misconfigured webPreferences, unsafe things exposed from preload, IPC handlers that don't validate their input.

Five things worth checking by hand

Whether or not you add a tool, these are the spots I'd actually look at in review.

1. webPreferences

The basics, and also where most apps get bitten.

// dangerous
new BrowserWindow({
  webPreferences: {
    nodeIntegration: true,
    contextIsolation: false,
  },
});
Enter fullscreen mode Exit fullscreen mode

This gives the renderer direct access to Node. One XSS and you're looking at filesystem access or command execution.

// fixed
new BrowserWindow({
  webPreferences: {
    nodeIntegration: false,
    contextIsolation: true,
    sandbox: true,
    preload: path.join(__dirname, "preload.js"),
  },
});
Enter fullscreen mode Exit fullscreen mode

Worth noting: Electron's own docs say nodeIntegration: false alone isn't enough — you need contextIsolation: true alongside it to actually block Node primitives. Treat these two as a pair.

Also, contextIsolation has been the default (true) since Electron 12, and sandbox since Electron 20. If you're on a reasonably current version and haven't explicitly turned these off, you're probably fine here already. The thing worth flagging in review is someone deliberately turning them off.

2. Inconsistent settings across windows

This is the one I think gets missed most often.

Main window is configured safely, but a child window creation site somewhere else in the codebase isn't.

// main window: safe
const mainWindow = new BrowserWindow({
  webPreferences: { contextIsolation: true, nodeIntegration: false },
});

// child window: got sloppy
ipcMain.handle("open-win", (event, arg) => {
  const child = new BrowserWindow({
    webPreferences: { nodeIntegration: true, contextIsolation: false },
  });
});
Enter fullscreen mode Exit fullscreen mode

If you only check the main window, you'll miss this every time. Worth diffing all the BrowserWindow creation sites in a project against each other, not just eyeballing the "main" one.

3. Command execution, especially with privilege escalation

Highest severity item on this list, full stop.

// dangerous: untrusted value interpolated into a shell command,
// combined with privilege escalation
sudo.exec(`networksetup -setdnsservers Wi-Fi ${userInput}`, options);
Enter fullscreen mode Exit fullscreen mode

If userInput isn't validated, an attacker gets arbitrary command execution with admin privileges. Combined with something like sudo-prompt, the blast radius is the whole machine.

// fixed: args as an array, no shell involved
execFile("networksetup", ["-setdnsservers", "Wi-Fi", validatedInput], options);
Enter fullscreen mode Exit fullscreen mode

execFile with an array of arguments means the value never gets re-interpreted as part of a command — as long as you don't pass shell: true, which routes it back through a shell anyway.

4. CSP

default-src 'self'; script-src 'self' 'unsafe-inline'
Enter fullscreen mode Exit fullscreen mode

unsafe-inline or unsafe-eval in your CSP is a hole in your XSS defenses. A wildcard * in a source list is the same problem.

If you're writing a checker for this yourself: don't naively string-match for *, or you'll flag partial wildcards like *.example.com as false positives. Tokenize the CSP properly — split on ; for directives, then on whitespace for sources.

5. shell.openExternal

// dangerous: no scheme validation
shell.openExternal(urlFromRenderer);
Enter fullscreen mode Exit fullscreen mode

Pass anything other than http/https (say, a file: URL) and you can end up opening something you didn't intend to.

// fixed: validate scheme against an allowlist
const parsed = new URL(urlFromRenderer);
if (parsed.protocol === "https:" || parsed.protocol === "http:") {
  shell.openExternal(urlFromRenderer);
}
Enter fullscreen mode Exit fullscreen mode

Same idea applies to setWindowOpenHandler and will-navigate — worth checking whether you're actually controlling navigation there too.

If you want to automate this

Everything above is "remember to check this on every PR," which, honestly, doesn't hold up well in practice. A 40-file PR lands on a Friday afternoon and that one webPreferences line gets missed. That's a CI problem, not a human-vigilance problem.

So automating even a narrow slice of this is worth it. As mentioned above, writing 3-4 Semgrep rules for the specific things that have actually bitten your app is a good, cheap first step.

Full disclosure since this is relevant: I've been building an OSS tool in this space called electron-audit — parses JS/TS with Babel, never runs your app, outputs SARIF. The thing I cared about most is not becoming the boy who cried wolf: every finding carries a confidence (high vs. heuristic) separate from severity, and the CI exit code only fails on high-confidence critical/high findings — so the softer, heuristic dataflow-based findings get reported but don't block your build.

If you know of other tools trying to fill this same gap, genuinely curious — would like to hear about them.

tl;dr

  • Electron's docs removed the Electronegativity recommendation (#48878)
  • Reason: Electronegativity is unmaintained, and its paid successor ElectroNG isn't for sale anymore either
  • Right now there's no tool the docs point you to
  • Alternatives: CodeQL (general-purpose, a few Electron-specific queries), Semgrep (free tier taint is single-function only), or newer community OSS
  • If you're not adding a tool, at minimum check: webPreferences, cross-window setting consistency, command execution, CSP, and openExternal

The checklist itself isn't hard. Keeping it enforced release after release is the actual problem.

Top comments (0)