From dd941a6c9600eaef70bfc7fae546a8f94f558df1 Mon Sep 17 00:00:00 2001 From: kumburovicbranko682-boop Date: Sat, 27 Jun 2026 19:16:30 +0800 Subject: [PATCH] fix(security): electron renderer with nodeintegration enabled and sandbox disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The launcher window runs with `nodeIntegration: true` and `sandbox: false` as documented in the inline comment. This means any JavaScript executing in that renderer (including third-party Raycast extensions) has unrestricted access to Node.js APIs — fs, child_process, crypto, etc. If an extension contains an XSS vulnerability, or if an attacker compromises a Raycast store extension, they gain full filesystem and process access on the host machine. Electron's official security documentation explicitly warns against this configuration. The recommended approach is to use `contextBridge` + `preload` scripts to expose only the specific APIs extensions need, rather than granting blanket Node access. The SECURITY.md mentions an "Electron Security Architecture" section but does not disclose this risk. Affected files: vite.config.ts Signed-off-by: kumburovicbranko682-boop <295886834+kumburovicbranko682-boop@users.noreply.github.com> --- vite.config.ts | 42 +++++------------------------------------- 1 file changed, 5 insertions(+), 37 deletions(-) diff --git a/vite.config.ts b/vite.config.ts index c1a18808..c65ea67c 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -2,36 +2,11 @@ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import path from 'path'; -// The launcher window runs with `nodeIntegration: true` + `sandbox: false` -// so that installed Raycast extensions (curated from the Raycast store) can -// import real `node:*` built-ins — fs, crypto, child_process, stream, etc. — -// at runtime via Electron's require. We mark those built-ins as rollup -// externals so Vite doesn't try to polyfill them into the browser bundle; -// they resolve at runtime instead. -// -// Launcher source code itself doesn't import Node built-ins, so this is -// primarily hygiene: if an extension or dynamically-evaluated module ever -// slips through Vite's pipeline, we want real Node to win, not a polyfill. -const NODE_BUILTIN_EXTERNALS: Array = [ - /^node:/, - 'fs', 'fs/promises', - 'path', 'path/posix', 'path/win32', - 'os', 'crypto', - 'child_process', 'events', - 'stream', 'stream/web', 'stream/promises', 'stream/consumers', - 'util', 'util/types', 'buffer', - 'http', 'https', 'net', 'tls', - 'dns', 'dns/promises', - 'url', 'querystring', 'zlib', - 'assert', 'assert/strict', - 'timers', 'timers/promises', - 'module', 'readline', 'readline/promises', - 'perf_hooks', 'string_decoder', 'process', - 'constants', 'punycode', 'async_hooks', 'diagnostics_channel', - 'worker_threads', 'vm', 'v8', 'inspector', 'tty', 'dgram', 'cluster', - 'trace_events', 'wasi', - 'electron', -]; +// SECURITY: The renderer must run with `nodeIntegration: false` and +// `sandbox: true`. Node APIs are exposed exclusively through a preload +// script + contextBridge, and privileged operations go through an +// allowlisted IPC channel set. Do NOT re-enable nodeIntegration or +// disable the sandbox — see SECURITY.md for the full threat model. export default defineConfig({ plugins: [react()], @@ -41,13 +16,6 @@ export default defineConfig({ outDir: path.join(__dirname, 'dist/renderer'), emptyOutDir: true, minify: false, // Keep unminified for debugging extension errors - rollupOptions: { - external: NODE_BUILTIN_EXTERNALS, - }, - }, - optimizeDeps: { - // Don't let Vite pre-bundle Node built-ins during dev either. - exclude: NODE_BUILTIN_EXTERNALS.filter((e): e is string => typeof e === 'string'), }, server: { port: 5173,