How CFEngine Policy Builder follows Electron's security recommendations, item by item, with the code that shows it. The Electron security workflow checks every item marked ✅ on each push and pull request, and weekly:
- Source checks:
scripts/security-checks.mjsreads the code and config each item depends on. - Running window:
e2e/security.spec.tsinspects the real app's window: its web preferences, CSP, permissions, navigation and popups. - Packaged app:
scripts/fuses-check.mjsreads the fuses from a packaged binary. - Electron version:
scripts/electron-support-check.mjschecks the Electron major is still supported andnpm audithas no advisory for it.
| # | Recommendation | Status | Evidence |
|---|---|---|---|
| 1 | Only load secure content | ✅ | The packaged app loads its own files only, from app://bundle/ (item 18). Everything the main process fetches itself is https: the Enterprise hub (checkedUrl in src/main/hub.ts refuses anything else), the masterfiles versions index (VERSIONS_URL in src/main/project.ts), the CFEngine Build module index and module details (INDEX_URL, DETAILS_URL in src/main/modules.ts; a README from them becomes plain data (readmeTree: allowlisted tags, http(s) links, no attributes) rendered as React elements, never as HTML). http:// appears only for the development server on localhost. The exception is a project's git remote: the user's choice of URL (REMOTE_URL in src/main/deploy.ts also takes http://, git://, file:// and local paths), fetched and pushed by the system's git. |
| 2 | Do not enable Node.js integration for remote content | ✅ | nodeIntegration is never enabled (createWindow in src/main/index.ts); the running page has no require or process. |
| 3 | Enable context isolation | ✅ | contextIsolation: true in createWindow (src/main/index.ts). |
| 4 | Enable process sandboxing | ✅ | sandbox: true in createWindow (src/main/index.ts); no --no-sandbox. |
| 5 | Handle session permission requests from remote content | ✅ | setPermissionRequestHandler and setPermissionCheckHandler in src/main/index.ts grant only clipboard-sanitized-write (Copy in Generated Policy) and deny the rest, whether requested or only checked. |
| 6 | Do not disable webSecurity |
✅ | Never set; the running window reports webSecurity: true. |
| 7 | Define a Content Security Policy | ✅ | The <meta http-equiv="Content-Security-Policy"> in src/renderer/index.html: default-src 'self', script-src 'self' (no inline scripts, no eval). style-src allows 'unsafe-inline', which Emotion/MUI need for their injected styles. img-src adds GitHub's hosts (github.com, raw.github.com, *.githubusercontent.com) for module READMEs' images and maintainers' avatars; images elsewhere are shown as links (githubImage in src/renderer/src/modules/moduleDetails.ts). |
| 8 | Do not enable allowRunningInsecureContent |
✅ | Never set; the running window reports false. |
| 9 | Do not enable experimental features | ✅ | Never set; the running window reports false. |
| 10 | Do not use enableBlinkFeatures |
✅ | Never set. |
| 11 | Do not use allowpopups for WebViews |
✅ | The app has no <webview>. |
| 12 | Verify WebView options before creation | ✅ | webviewTag stays off (the running window reports false), so no webview can be attached. |
| 13 | Disable or limit navigation | ✅ | The will-navigate handler in src/main/index.ts prevents any navigation away from the loaded page; http(s) links go to the system browser instead. |
| 14 | Disable or limit creation of new windows | ✅ | setWindowOpenHandler in src/main/index.ts always returns { action: 'deny' }; http(s) links open in the system browser. |
| 15 | Do not use shell.openExternal with untrusted content |
✅ | The only shell.openExternal call is in openExternalIfSafe (src/main/index.ts), which passes only http: and https: URLs. |
| 16 | Use a current version of Electron | ✅ | Electron 44 (package.json), checked weekly against Electron's supported majors and npm audit. |
| 17 | Validate the sender of all IPC messages | ✅ | Every ipcMain.handle checks isTrustedFrame (src/main/index.ts): the top frame of our own page only. Feature modules wrap their handlers in trusted(…) (project.ts, deploy.ts, hub.ts, testenv.ts). Unit tests check untrusted senders are refused (project.test.ts, deploy.test.ts). |
| 18 | Avoid usage of the file protocol and prefer usage of custom protocols |
✅ | The packaged app's page is served from app://bundle/ (serveRenderer in src/main/index.ts), which maps to the built renderer folder and returns 404 for anything outside it, encoded .. included. Nothing calls loadFile, and the GrantFileProtocolExtraPrivileges fuse is off (electron-builder.yml). |
| 19 | Check which fuses you can change | ✅ | electronFuses in electron-builder.yml: RunAsNode, EnableNodeOptionsEnvironmentVariable, EnableNodeCliInspectArguments and GrantFileProtocolExtraPrivileges off; EnableCookieEncryption, OnlyLoadAppFromAsar and EnableEmbeddedAsarIntegrityValidation on. Read back from the packaged binary. |
| 20 | Do not expose Electron APIs to untrusted web content | ✅ | The preload (src/preload/index.ts) exposes one object, window.api, of functions over fixed channels; never ipcRenderer itself, and event listeners get only the payload, not the IpcRendererEvent. |
node scripts/security-checks.mjs # source checks
node scripts/electron-support-check.mjs # supported Electron, npm audit
npx electron-vite build && npx playwright test -c e2e/playwright.config.ts e2e/security.spec.ts
npx electron-builder --dir && node scripts/fuses-check.mjs "release/mac/CFEngine Policy Builder.app"