From 71bfcecbe6fb178582f05c52210f7d35bd87359e Mon Sep 17 00:00:00 2001 From: V Date: Wed, 24 Dec 2025 15:30:18 +0100 Subject: [PATCH] Enable process sandboxing (#1206) --- src/main/ipc.ts | 52 +++++++++++++-------------------- src/main/mainWindow.ts | 4 +-- src/main/utils/vencordLoader.ts | 15 +++++++++- src/preload/VesktopNative.ts | 15 +++++++--- src/preload/index.ts | 46 ++++++++++++----------------- src/preload/splash.ts | 2 +- src/preload/typedIpc.ts | 4 +-- src/preload/updater.ts | 2 +- src/renderer/index.ts | 11 +++++++ src/shared/IpcEvents.ts | 9 ++++-- 10 files changed, 87 insertions(+), 73 deletions(-) diff --git a/src/main/ipc.ts b/src/main/ipc.ts index e174bce..41c9743 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -18,17 +18,15 @@ import { session, shell } from "electron"; -import { mkdirSync, readFileSync, watch } from "fs"; -import { open, readFile } from "fs/promises"; +import { readFileSync, watch } from "fs"; +import { readFile } from "fs/promises"; import { enableHardwareAcceleration } from "main"; import { release } from "os"; import { join } from "path"; -import { debounce } from "shared/utils/debounce"; import { IpcEvents } from "../shared/IpcEvents"; import { setBadgeCount } from "./appBadge"; import { autoStart } from "./autoStart"; -import { VENCORD_QUICKCSS_FILE, VENCORD_THEMES_DIR } from "./constants"; import { mainWin } from "./mainWindow"; import { Settings, State } from "./settings"; import { handle, handleSync } from "./utils/ipcWrappers"; @@ -37,13 +35,29 @@ import { isDeckGameMode, showGamePage } from "./utils/steamOS"; import { isValidVencordInstall } from "./utils/vencordLoader"; import { VENCORD_FILES_DIR } from "./vencordFilesDir"; -handleSync(IpcEvents.GET_VENCORD_PRELOAD_FILE, () => join(VENCORD_FILES_DIR, "vencordDesktopPreload.js")); +handleSync(IpcEvents.DEPRECATED_GET_VENCORD_PRELOAD_SCRIPT_PATH, () => + join(VENCORD_FILES_DIR, "vencordDesktopPreload.js") +); +handleSync(IpcEvents.GET_VENCORD_PRELOAD_SCRIPT, () => + readFileSync(join(VENCORD_FILES_DIR, "vencordDesktopPreload.js"), "utf-8") +); handleSync(IpcEvents.GET_VENCORD_RENDERER_SCRIPT, () => readFileSync(join(VENCORD_FILES_DIR, "vencordDesktopRenderer.js"), "utf-8") ); -handleSync(IpcEvents.GET_RENDERER_SCRIPT, () => readFileSync(join(__dirname, "renderer.js"), "utf-8")); -handleSync(IpcEvents.GET_RENDERER_CSS_FILE, () => join(__dirname, "renderer.css")); +const VESKTOP_RENDERER_JS_PATH = join(__dirname, "renderer.js"); +const VESKTOP_RENDERER_CSS_PATH = join(__dirname, "renderer.css"); +handleSync(IpcEvents.GET_VESKTOP_RENDERER_SCRIPT, () => readFileSync(VESKTOP_RENDERER_JS_PATH, "utf-8")); +handle(IpcEvents.GET_VESKTOP_RENDERER_CSS, () => readFile(VESKTOP_RENDERER_CSS_PATH, "utf-8")); + +if (IS_DEV) { + watch(VESKTOP_RENDERER_CSS_PATH, { persistent: false }, async () => { + mainWin?.webContents.postMessage( + IpcEvents.VESKTOP_RENDERER_CSS_UPDATE, + await readFile(VESKTOP_RENDERER_CSS_PATH, "utf-8") + ); + }); +} handleSync(IpcEvents.GET_SETTINGS, () => Settings.plain); handleSync(IpcEvents.GET_VERSION, () => app.getVersion()); @@ -164,27 +178,3 @@ function openDebugPage(page: string) { handle(IpcEvents.DEBUG_LAUNCH_GPU, () => openDebugPage("chrome://gpu")); handle(IpcEvents.DEBUG_LAUNCH_WEBRTC_INTERNALS, () => openDebugPage("chrome://webrtc-internals")); - -function readCss() { - return readFile(VENCORD_QUICKCSS_FILE, "utf-8").catch(() => ""); -} - -open(VENCORD_QUICKCSS_FILE, "a+").then(fd => { - fd.close(); - watch( - VENCORD_QUICKCSS_FILE, - { persistent: false }, - debounce(async () => { - mainWin?.webContents.postMessage("VencordQuickCssUpdate", await readCss()); - }, 50) - ); -}); - -mkdirSync(VENCORD_THEMES_DIR, { recursive: true }); -watch( - VENCORD_THEMES_DIR, - { persistent: false }, - debounce(() => { - mainWin?.webContents.postMessage("VencordThemeUpdate", void 0); - }) -); diff --git a/src/main/mainWindow.ts b/src/main/mainWindow.ts index 6b8b946..c068bea 100644 --- a/src/main/mainWindow.ts +++ b/src/main/mainWindow.ts @@ -34,7 +34,7 @@ import { destroyTray, initTray } from "./tray"; import { clearData } from "./utils/clearData"; import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally"; import { applyDeckKeyboardFix, askToApplySteamLayout, isDeckGameMode } from "./utils/steamOS"; -import { downloadVencordFiles, ensureVencordFiles } from "./utils/vencordLoader"; +import { downloadVencordFiles, ensureVencordFiles, vencordSupportsSandboxing } from "./utils/vencordLoader"; import { VENCORD_FILES_DIR } from "./vencordFilesDir"; let isQuitting = false; @@ -323,7 +323,7 @@ function buildBrowserWindowOptions(): BrowserWindowConstructorOptions { backgroundColor, webPreferences: { nodeIntegration: false, - sandbox: false, // TODO + sandbox: vencordSupportsSandboxing(), contextIsolation: true, devTools: true, preload: join(__dirname, "preload.js"), diff --git a/src/main/utils/vencordLoader.ts b/src/main/utils/vencordLoader.ts index 25748b3..96ab1ff 100644 --- a/src/main/utils/vencordLoader.ts +++ b/src/main/utils/vencordLoader.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { mkdirSync } from "fs"; +import { mkdirSync, readFileSync } from "fs"; import { access, constants as FsConstants, writeFile } from "fs/promises"; import { VENCORD_FILES_DIR } from "main/vencordFilesDir"; import { join } from "path"; @@ -75,3 +75,16 @@ export async function ensureVencordFiles() { await Promise.all([downloadVencordFiles(), writeFile(join(VENCORD_FILES_DIR, "package.json"), "{}")]); } + +// TODO: remove this once enough time has passed +export function vencordSupportsSandboxing() { + const supports = readFileSync(join(VENCORD_FILES_DIR, "vencordDesktopMain.js"), "utf-8").includes( + "VencordGetRendererCss" + ); + if (!supports) { + console.warn( + "⚠️ [VencordLoader] Vencord version is outdated and does not support sandboxing. Please update Vencord to the latest version." + ); + } + return supports; +} diff --git a/src/preload/VesktopNative.ts b/src/preload/VesktopNative.ts index b15e65d..91f7103 100644 --- a/src/preload/VesktopNative.ts +++ b/src/preload/VesktopNative.ts @@ -4,9 +4,9 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { Node } from "@vencord/venmic"; -import { ipcRenderer } from "electron"; -import { IpcMessage, IpcResponse } from "main/ipcCommands"; +import type { Node } from "@vencord/venmic"; +import { ipcRenderer } from "electron/renderer"; +import type { IpcMessage, IpcResponse } from "main/ipcCommands"; import type { Settings } from "shared/settings"; import { IpcEvents } from "../shared/IpcEvents"; @@ -34,7 +34,14 @@ export const VesktopNative = { supportsWindowsTransparency: () => sendSync(IpcEvents.SUPPORTS_WINDOWS_TRANSPARENCY), getEnableHardwareAcceleration: () => sendSync(IpcEvents.GET_ENABLE_HARDWARE_ACCELERATION), isOutdated: () => invoke(IpcEvents.UPDATER_IS_OUTDATED), - openUpdater: () => invoke(IpcEvents.UPDATER_OPEN) + openUpdater: () => invoke(IpcEvents.UPDATER_OPEN), + // used by vencord + getRendererCss: () => invoke(IpcEvents.GET_VESKTOP_RENDERER_CSS), + onRendererCssUpdate: (cb: (newCss: string) => void) => { + if (!IS_DEV) return; + + ipcRenderer.on(IpcEvents.VESKTOP_RENDERER_CSS_UPDATE, (_e, newCss: string) => cb(newCss)); + } }, autostart: { isEnabled: () => sendSync(IpcEvents.AUTOSTART_ENABLED), diff --git a/src/preload/index.ts b/src/preload/index.ts index 5003cc7..e91ffbc 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -4,39 +4,29 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { contextBridge, ipcRenderer, webFrame } from "electron"; -import { readFileSync, watch } from "fs"; +import { contextBridge, ipcRenderer, webFrame } from "electron/renderer"; import { IpcEvents } from "../shared/IpcEvents"; import { VesktopNative } from "./VesktopNative"; contextBridge.exposeInMainWorld("VesktopNative", VesktopNative); -require(ipcRenderer.sendSync(IpcEvents.GET_VENCORD_PRELOAD_FILE)); +// TODO: remove this legacy workaround once some time has passed +const isSandboxed = typeof __dirname === "undefined"; +if (isSandboxed) { + // While sandboxed, Electron "polyfills" these APIs as local variables. + // We have to pass them as arguments as they are not global + Function( + "require", + "Buffer", + "process", + "clearImmediate", + "setImmediate", + ipcRenderer.sendSync(IpcEvents.GET_VENCORD_PRELOAD_SCRIPT) + )(require, Buffer, process, clearImmediate, setImmediate); +} else { + require(ipcRenderer.sendSync(IpcEvents.DEPRECATED_GET_VENCORD_PRELOAD_SCRIPT_PATH)); +} webFrame.executeJavaScript(ipcRenderer.sendSync(IpcEvents.GET_VENCORD_RENDERER_SCRIPT)); -webFrame.executeJavaScript(ipcRenderer.sendSync(IpcEvents.GET_RENDERER_SCRIPT)); - -// #region css -const rendererCss = ipcRenderer.sendSync(IpcEvents.GET_RENDERER_CSS_FILE); - -const style = document.createElement("style"); -style.id = "vcd-css-core"; -style.textContent = readFileSync(rendererCss, "utf-8"); - -if (document.readyState === "complete") { - document.documentElement.appendChild(style); -} else { - document.addEventListener("DOMContentLoaded", () => document.documentElement.appendChild(style), { - once: true - }); -} - -if (IS_DEV) { - // persistent means keep process running if watcher is the only thing still running - // which we obviously don't want - watch(rendererCss, { persistent: false }, () => { - document.getElementById("vcd-css-core")!.textContent = readFileSync(rendererCss, "utf-8"); - }); -} -// #endregion +webFrame.executeJavaScript(ipcRenderer.sendSync(IpcEvents.GET_VESKTOP_RENDERER_SCRIPT)); diff --git a/src/preload/splash.ts b/src/preload/splash.ts index 11c41e2..126aa9a 100644 --- a/src/preload/splash.ts +++ b/src/preload/splash.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { contextBridge, ipcRenderer } from "electron"; +import { contextBridge, ipcRenderer } from "electron/renderer"; contextBridge.exposeInMainWorld("VesktopSplashNative", { onUpdateMessage(callback: (message: string) => void) { diff --git a/src/preload/typedIpc.ts b/src/preload/typedIpc.ts index ebb8975..5edbb02 100644 --- a/src/preload/typedIpc.ts +++ b/src/preload/typedIpc.ts @@ -4,8 +4,8 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { ipcRenderer } from "electron"; -import { IpcEvents, UpdaterIpcEvents } from "shared/IpcEvents"; +import { ipcRenderer } from "electron/renderer"; +import type { IpcEvents, UpdaterIpcEvents } from "shared/IpcEvents"; export function invoke(event: IpcEvents | UpdaterIpcEvents, ...args: any[]) { return ipcRenderer.invoke(event, ...args) as Promise; diff --git a/src/preload/updater.ts b/src/preload/updater.ts index 1881f65..2531a6d 100644 --- a/src/preload/updater.ts +++ b/src/preload/updater.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { contextBridge, ipcRenderer } from "electron"; +import { contextBridge, ipcRenderer } from "electron/renderer"; import type { UpdateInfo } from "electron-updater"; import { UpdaterIpcEvents } from "shared/IpcEvents"; diff --git a/src/renderer/index.ts b/src/renderer/index.ts index ce12370..e6525a3 100644 --- a/src/renderer/index.ts +++ b/src/renderer/index.ts @@ -32,3 +32,14 @@ customSettingsSections.push(() => ({ element: SettingsUi, className: "vc-vesktop-settings" })); + +// TODO: remove this legacy workaround once some time has passed +// @ts-expect-error +if (!Vencord.Api.Styles.vencordRootNode) { + const style = document.createElement("style"); + style.id = "vesktop-css-core"; + + VesktopNative.app.getRendererCss().then(css => (style.textContent = css)); + + document.addEventListener("DOMContentLoaded", () => document.documentElement.append(style), { once: true }); +} diff --git a/src/shared/IpcEvents.ts b/src/shared/IpcEvents.ts index da69b02..95049b3 100644 --- a/src/shared/IpcEvents.ts +++ b/src/shared/IpcEvents.ts @@ -5,10 +5,13 @@ */ export const enum IpcEvents { - GET_VENCORD_PRELOAD_FILE = "VCD_GET_VC_PRELOAD_FILE", + GET_VENCORD_PRELOAD_SCRIPT = "VCD_GET_VC_PRELOAD_SCRIPT", + DEPRECATED_GET_VENCORD_PRELOAD_SCRIPT_PATH = "DEPRECATED_GET_VENCORD_PRELOAD_SCRIPT_PATH", GET_VENCORD_RENDERER_SCRIPT = "VCD_GET_VC_RENDERER_SCRIPT", - GET_RENDERER_SCRIPT = "VCD_GET_RENDERER_SCRIPT", - GET_RENDERER_CSS_FILE = "VCD_GET_RENDERER_CSS_FILE", + + GET_VESKTOP_RENDERER_SCRIPT = "VCD_GET_RENDERER_SCRIPT", + GET_VESKTOP_RENDERER_CSS = "VCD_GET_RENDERER_CSS", + VESKTOP_RENDERER_CSS_UPDATE = "VCD_PRELOAD_RENDERER_CSS_UPDATE", GET_VERSION = "VCD_GET_VERSION", SUPPORTS_WINDOWS_TRANSPARENCY = "VCD_SUPPORTS_WINDOWS_TRANSPARENCY",