diff --git a/packages/core-extensions/src/disableSentry/host.ts b/packages/core-extensions/src/disableSentry/host.ts index 909b247..54548de 100644 --- a/packages/core-extensions/src/disableSentry/host.ts +++ b/packages/core-extensions/src/disableSentry/host.ts @@ -1,6 +1,5 @@ import { join } from "node:path"; import { Module } from "node:module"; -import { BrowserWindow } from "electron"; const logger = moonlightHost.getLogger("disableSentry"); @@ -24,19 +23,3 @@ if (moonlightHost.asarPath !== "moonlightDesktop") { logger.error("Failed to stub Sentry host side:", err); } } - -moonlightHost.events.on("window-created", (window: BrowserWindow) => { - window.webContents.session.webRequest.onBeforeRequest( - { - urls: [ - "https://*.sentry.io/*", - "https://*.discord.com/error-reporting-proxy/*", - "https://discord.com/assets/sentry.*.js", - "https://*.discord.com/assets/sentry.*.js" - ] - }, - function (details, callback) { - callback({ cancel: true }); - } - ); -}); diff --git a/packages/core-extensions/src/disableSentry/manifest.json b/packages/core-extensions/src/disableSentry/manifest.json index 3e91eda..0f8c815 100644 --- a/packages/core-extensions/src/disableSentry/manifest.json +++ b/packages/core-extensions/src/disableSentry/manifest.json @@ -6,5 +6,11 @@ "tagline": "Turns off Discord's error reporting systems", "authors": ["Cynosphere", "NotNite"], "tags": ["privacy"] - } + }, + "blocked": [ + "https://*.sentry.io/*", + "https://*.discord.com/error-reporting-proxy/*", + "https://discord.com/assets/sentry.*.js", + "https://*.discord.com/assets/sentry.*.js" + ] } diff --git a/packages/core-extensions/src/noTrack/host.ts b/packages/core-extensions/src/noTrack/host.ts deleted file mode 100644 index f24909f..0000000 --- a/packages/core-extensions/src/noTrack/host.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { BrowserWindow } from "electron"; - -moonlightHost.events.on("window-created", (window: BrowserWindow) => { - window.webContents.session.webRequest.onBeforeRequest( - { - urls: [ - "https://*.discord.com/api/v*/science", - "https://*.discord.com/api/v*/metrics" - ] - }, - function (details, callback) { - callback({ cancel: true }); - } - ); -}); diff --git a/packages/core-extensions/src/noTrack/manifest.json b/packages/core-extensions/src/noTrack/manifest.json index bff28f4..f9fc268 100644 --- a/packages/core-extensions/src/noTrack/manifest.json +++ b/packages/core-extensions/src/noTrack/manifest.json @@ -6,5 +6,9 @@ "tagline": "Disables /api/science and analytics", "authors": ["Cynosphere", "NotNite"], "tags": ["privacy"] - } + }, + "blocked": [ + "https://*.discord.com/api/v*/science", + "https://*.discord.com/api/v*/metrics" + ] } diff --git a/packages/injector/src/index.ts b/packages/injector/src/index.ts index 562a5ec..6563836 100644 --- a/packages/injector/src/index.ts +++ b/packages/injector/src/index.ts @@ -21,6 +21,7 @@ const logger = new Logger("injector"); let oldPreloadPath: string | undefined; let corsAllow: string[] = []; +let blockedUrls: RegExp[] = []; let isMoonlightDesktop = false; let hasOpenAsar = false; let openAsarConfigPreload: string | undefined; @@ -41,6 +42,39 @@ ipcMain.handle(constants.ipcSetCorsList, (_, list) => { corsAllow = list; }); +const reEscapeRegExp = /[\\^$.*+?()[\]{}|]/g; +const reMatchPattern = + /^(?\*|[a-z][a-z0-9+.-]*):\/\/(?.+?)\/(?.+)?$/; + +const escapeRegExp = (s: string) => s.replace(reEscapeRegExp, "\\$&"); +ipcMain.handle(constants.ipcSetBlockedList, (_, list: string[]) => { + // We compile the patterns into a RegExp based on a janky match pattern-like syntax + const compiled = list + .map((pattern) => { + const match = pattern.match(reMatchPattern); + if (!match?.groups) return; + + let regex = ""; + if (match.groups.scheme === "*") regex += ".+?"; + else regex += escapeRegExp(match.groups.scheme); + regex += ":\\/\\/"; + + const parts = match.groups.host.split("."); + if (parts[0] === "*") { + parts.shift(); + regex += "(?:.+?\\.)?"; + } + regex += escapeRegExp(parts.join(".")); + + regex += "\\/" + escapeRegExp(match.groups.path).replace("\\*", ".*?"); + + return new RegExp("^" + regex + "$"); + }) + .filter(Boolean) as RegExp[]; + + blockedUrls = compiled; +}); + function patchCsp(headers: Record) { const directives = [ "style-src", @@ -118,6 +152,12 @@ class BrowserWindow extends ElectronBrowserWindow { } }); + // Allow plugins to block some URLs, + // this is needed because multiple webRequest handlers cannot be registered at once + this.webContents.session.webRequest.onBeforeRequest((details, cb) => { + cb({ cancel: blockedUrls.some((u) => u.test(details.url)) }); + }); + if (hasOpenAsar) { // Remove DOM injections // Settings can still be opened via: diff --git a/packages/node-preload/src/index.ts b/packages/node-preload/src/index.ts index 85817b7..6e5d3e6 100644 --- a/packages/node-preload/src/index.ts +++ b/packages/node-preload/src/index.ts @@ -52,9 +52,9 @@ async function injectGlobals() { await loadProcessedExtensions(processedExtensions); contextBridge.exposeInMainWorld("moonlightNode", moonlightNode); - const extCors = moonlightNode.processedExtensions.extensions - .map((x) => x.manifest.cors ?? []) - .flat(); + const extCors = moonlightNode.processedExtensions.extensions.flatMap( + (x) => x.manifest.cors ?? [] + ); for (const repo of moonlightNode.config.repositories) { const url = new URL(repo); @@ -63,6 +63,11 @@ async function injectGlobals() { } ipcRenderer.invoke(constants.ipcSetCorsList, extCors); + + const extBlocked = moonlightNode.processedExtensions.extensions.flatMap( + (e) => e.manifest.blocked ?? [] + ); + ipcRenderer.invoke(constants.ipcSetBlockedList, extBlocked); } async function loadPreload() { diff --git a/packages/types/src/constants.ts b/packages/types/src/constants.ts index 99b7c06..978f4c8 100644 --- a/packages/types/src/constants.ts +++ b/packages/types/src/constants.ts @@ -8,5 +8,6 @@ export const ipcGetAppData = "_moonlight_getAppData"; export const ipcGetIsMoonlightDesktop = "_moonlight_getIsMoonlightDesktop"; export const ipcMessageBox = "_moonlight_messageBox"; export const ipcSetCorsList = "_moonlight_setCorsList"; +export const ipcSetBlockedList = "_moonlight_setBlockedList"; export const apiLevel = 2; diff --git a/packages/types/src/extension.ts b/packages/types/src/extension.ts index 744af71..8eaa777 100644 --- a/packages/types/src/extension.ts +++ b/packages/types/src/extension.ts @@ -47,7 +47,9 @@ export type ExtensionManifest = { incompatible?: string[]; settings?: Record; + cors?: string[]; + blocked?: string[]; }; export enum ExtensionLoadSource {