From 7c923b9962600e0a0126739b19043490f7c2463e Mon Sep 17 00:00:00 2001 From: Vendicated Date: Thu, 27 Jun 2024 16:14:32 +0200 Subject: [PATCH 01/10] fix enabled by default logic --- src/api/Settings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/Settings.ts b/src/api/Settings.ts index 70ba0bd4a..88337a917 100644 --- a/src/api/Settings.ts +++ b/src/api/Settings.ts @@ -129,7 +129,7 @@ export const SettingsStore = new SettingsStoreClass(settings, { if (path === "plugins" && key in plugins) return target[key] = { - enabled: IS_REPORTER ?? plugins[key].required ?? plugins[key].enabledByDefault ?? false + enabled: IS_REPORTER || plugins[key].required || plugins[key].enabledByDefault || false }; // Since the property is not set, check if this is a plugin's setting and if so, try to resolve From f81cd5d9a4ee88e5849c35ae2839e6c6f44e69a9 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 27 Jun 2024 06:35:01 -0300 Subject: [PATCH 02/10] Fix broken patches --- scripts/generateReport.ts | 1 - src/plugins/fakeProfileThemes/index.tsx | 2 +- src/plugins/userVoiceShow/index.tsx | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index d8cbb44a0..0b94aaf72 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -136,7 +136,6 @@ async function printReport() { body: JSON.stringify({ description: "Here's the latest Vencord Report!", username: "Vencord Reporter" + (CANARY ? " (Canary)" : ""), - avatar_url: "https://cdn.discordapp.com/avatars/1017176847865352332/c312b6b44179ae6817de7e4b09e9c6af.webp?size=512", embeds: [ { title: "Bad Patches", diff --git a/src/plugins/fakeProfileThemes/index.tsx b/src/plugins/fakeProfileThemes/index.tsx index 85aadca13..9e784da68 100644 --- a/src/plugins/fakeProfileThemes/index.tsx +++ b/src/plugins/fakeProfileThemes/index.tsx @@ -109,7 +109,7 @@ interface ProfileModalProps { } const ColorPicker = findComponentByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); -const ProfileModal = findComponentByCodeLazy("isTryItOutFlow:", "pendingThemeColors:", "avatarDecorationOverride:", ".CUSTOM_STATUS"); +const ProfileModal = findComponentByCodeLazy("isTryItOutFlow:", "pendingThemeColors:", "pendingAvatarDecoration:", "EDIT_PROFILE_BANNER"); const requireColorPicker = extractAndLoadChunksLazy(["USER_SETTINGS_PROFILE_COLOR_DEFAULT_BUTTON.format"], /createPromise:\(\)=>\i\.\i(\("?.+?"?\)).then\(\i\.bind\(\i,"?(.+?)"?\)\)/); diff --git a/src/plugins/userVoiceShow/index.tsx b/src/plugins/userVoiceShow/index.tsx index feba28316..53044a558 100644 --- a/src/plugins/userVoiceShow/index.tsx +++ b/src/plugins/userVoiceShow/index.tsx @@ -98,8 +98,8 @@ export default definePlugin({ { find: ".popularApplicationCommandIds,", replacement: { - match: /applicationId:\i\.id}\),(?=.{0,50}setNote:\i)/, - replace: "$&$self.patchPopout(arguments[0]),", + match: /(?<=,)(?=!\i&&!\i&&.{0,50}setNote:)/, + replace: "$self.patchPopout(arguments[0]),", } }, // below username From e37a0cfec97af9c9299a48601417edd267360ec3 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Thu, 27 Jun 2024 16:16:29 +0200 Subject: [PATCH 03/10] bump to v1.9.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index eebacb332..f2f5e3a2e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.9.1", + "version": "1.9.2", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { From cbc7f7230a88f0b9b0508ae12cfa2df0c0a6a64e Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 27 Jun 2024 23:37:10 -0300 Subject: [PATCH 04/10] Fix acquiring WebpackRequire --- src/webpack/patchWebpack.ts | 105 +++++++++++++----------------------- 1 file changed, 36 insertions(+), 69 deletions(-) diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index 48f1b8147..bd47cefad 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -18,7 +18,7 @@ import { WEBPACK_CHUNK } from "@utils/constants"; import { Logger } from "@utils/Logger"; -import { canonicalizeMatch, canonicalizeReplacement } from "@utils/patches"; +import { canonicalizeReplacement } from "@utils/patches"; import { PatchReplacement } from "@utils/types"; import { WebpackInstance } from "discord-types/other"; @@ -27,7 +27,6 @@ import { patches } from "../plugins"; import { _initWebpack, beforeInitListeners, factoryListeners, moduleListeners, subscriptions, wreq } from "."; const logger = new Logger("WebpackInterceptor", "#8caaee"); -const initCallbackRegex = canonicalizeMatch(/{return \i\(".+?"\)}/); let webpackChunk: any[]; @@ -53,71 +52,6 @@ Object.defineProperty(window, WEBPACK_CHUNK, { } }); -// wreq.O is the webpack onChunksLoaded function -// Discord uses it to await for all the chunks to be loaded before initializing the app -// We monkey patch it to also monkey patch the initialize app callback to get immediate access to the webpack require and run our listeners before doing it -Object.defineProperty(Function.prototype, "O", { - configurable: true, - - set(onChunksLoaded: any) { - // When using react devtools or other extensions, or even when discord loads the sentry, we may also catch their webpack here. - // This ensures we actually got the right one - // this.e (wreq.e) is the method for loading a chunk, and only the main webpack has it - const { stack } = new Error(); - if ((stack?.includes("discord.com") || stack?.includes("discordapp.com")) && String(this.e).includes("Promise.all")) { - logger.info("Found main WebpackRequire.onChunksLoaded"); - - delete (Function.prototype as any).O; - - const originalOnChunksLoaded = onChunksLoaded; - onChunksLoaded = function (this: unknown, result: any, chunkIds: string[], callback: () => any, priority: number) { - if (callback != null && initCallbackRegex.test(callback.toString())) { - Object.defineProperty(this, "O", { - value: originalOnChunksLoaded, - configurable: true - }); - - const wreq = this as WebpackInstance; - - const originalCallback = callback; - callback = function (this: unknown) { - logger.info("Patched initialize app callback invoked, initializing our internal references to WebpackRequire and running beforeInitListeners"); - _initWebpack(wreq); - - for (const beforeInitListener of beforeInitListeners) { - beforeInitListener(wreq); - } - - originalCallback.apply(this, arguments as any); - }; - - callback.toString = originalCallback.toString.bind(originalCallback); - arguments[2] = callback; - } - - originalOnChunksLoaded.apply(this, arguments as any); - }; - - onChunksLoaded.toString = originalOnChunksLoaded.toString.bind(originalOnChunksLoaded); - - // Returns whether a chunk has been loaded - Object.defineProperty(onChunksLoaded, "j", { - set(v) { - delete onChunksLoaded.j; - onChunksLoaded.j = v; - originalOnChunksLoaded.j = v; - }, - configurable: true - }); - } - - Object.defineProperty(this, "O", { - value: onChunksLoaded, - configurable: true - }); - } -}); - // wreq.m is the webpack module factory. // normally, this is populated via webpackGlobal.push, which we patch below. // However, Discord has their .m prepopulated. @@ -133,13 +67,46 @@ Object.defineProperty(Function.prototype, "m", { // This ensures we actually got the right one const { stack } = new Error(); if ((stack?.includes("discord.com") || stack?.includes("discordapp.com")) && !Array.isArray(v)) { - logger.info("Found Webpack module factory", stack.match(/\/assets\/(.+?\.js)/)?.[1] ?? ""); + const fileName = stack.match(/\/assets\/(.+?\.js)/)?.[1] ?? ""; + + logger.info("Found Webpack module factory", fileName); patchFactories(v); + + // Define a setter for the bundlePath property of WebpackRequire. Only the main Webpack has this property. + // So if the setter is called, this means we can initialize the internal references to WebpackRequire. + Object.defineProperty(this, "p", { + configurable: true, + + set(this: WebpackInstance, bundlePath: string) { + Object.defineProperty(this, "p", { + value: bundlePath, + configurable: true, + enumerable: true, + writable: true + }); + + clearTimeout(setterTimeout); + + if (bundlePath !== "/assets/") return; + + logger.info(`Main Webpack found in ${fileName}, initializing internal references to WebpackRequire`); + _initWebpack(this); + + for (const beforeInitListener of beforeInitListeners) { + beforeInitListener(this); + } + } + }); + // setImmediate to clear this property setter if this is not the main Webpack. + // If this is the main Webpack, wreq.p will always be set before the timeout runs. + const setterTimeout = setTimeout(() => Reflect.deleteProperty(this, "p"), 0); } Object.defineProperty(this, "m", { value: v, - configurable: true + configurable: true, + enumerable: true, + writable: true }); } }); From 96873ccef70564823a76a89e310ed02de7ff1131 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 27 Jun 2024 23:58:01 -0300 Subject: [PATCH 05/10] Temp fix for disabling Sentry --- src/plugins/_core/noTrack.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/plugins/_core/noTrack.ts b/src/plugins/_core/noTrack.ts index 67f6c6448..9283bef52 100644 --- a/src/plugins/_core/noTrack.ts +++ b/src/plugins/_core/noTrack.ts @@ -20,6 +20,12 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; +// FIXME Do this without monkey patching maybe +Object.defineProperty(window, "DiscordSentry", { + configurable: true, + set(v) { } +}); + const settings = definePluginSettings({ disableAnalytics: { type: OptionType.BOOLEAN, From df6ffd90e3160dc9583fe41039c5ec01166b982d Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 28 Jun 2024 00:53:09 -0300 Subject: [PATCH 06/10] Fix broken patches --- src/plugins/_core/noTrack.ts | 27 ++++++++++++--------------- src/plugins/showHiddenThings/index.ts | 4 ++-- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/plugins/_core/noTrack.ts b/src/plugins/_core/noTrack.ts index 9283bef52..26b36735a 100644 --- a/src/plugins/_core/noTrack.ts +++ b/src/plugins/_core/noTrack.ts @@ -18,13 +18,7 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; -import definePlugin, { OptionType } from "@utils/types"; - -// FIXME Do this without monkey patching maybe -Object.defineProperty(window, "DiscordSentry", { - configurable: true, - set(v) { } -}); +import definePlugin, { OptionType, StartAt } from "@utils/types"; const settings = definePluginSettings({ disableAnalytics: { @@ -52,13 +46,6 @@ export default definePlugin({ replace: "()=>{}", }, }, - { - find: "window.DiscordSentry=", - replacement: { - match: /^.+$/, - replace: "()=>{}", - } - }, { find: ".METRICS,", replacement: [ @@ -80,5 +67,15 @@ export default definePlugin({ replace: "getDebugLogging(){return false;" } }, - ] + ], + + startAt: StartAt.Init, + start() { + Object.defineProperty(window, "DiscordSentry", { + configurable: true, + set() { + Reflect.deleteProperty(window, "DiscordSentry"); + } + }); + } }); diff --git a/src/plugins/showHiddenThings/index.ts b/src/plugins/showHiddenThings/index.ts index b92c16c86..599bcd36d 100644 --- a/src/plugins/showHiddenThings/index.ts +++ b/src/plugins/showHiddenThings/index.ts @@ -110,8 +110,8 @@ export default definePlugin({ find: '"pepe","nude"', predicate: () => settings.store.disableDisallowedDiscoveryFilters, replacement: { - match: /\?\["pepe",.+?\]/, - replace: "?[]", + match: /(?<=[?=])\["pepe",.+?\]/, + replace: "[]", }, }, // patch request that queries if term is allowed From 484d70fb1537c5540f4377e5fcde982c421f74d6 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Fri, 28 Jun 2024 13:20:24 +0200 Subject: [PATCH 07/10] bump to v1.9.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f2f5e3a2e..dc90a646c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.9.2", + "version": "1.9.3", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { From e4bf71784e158ad1a690427c4720240bf8b58964 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Fri, 28 Jun 2024 16:04:51 +0200 Subject: [PATCH 08/10] TextReplace: support /v regex flag --- src/plugins/textReplace/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/textReplace/index.tsx b/src/plugins/textReplace/index.tsx index 416ce83fb..615477d07 100644 --- a/src/plugins/textReplace/index.tsx +++ b/src/plugins/textReplace/index.tsx @@ -77,7 +77,7 @@ const settings = definePluginSettings({ }); function stringToRegex(str: string) { - const match = str.match(/^(\/)?(.+?)(?:\/([gimsuy]*))?$/); // Regex to match regex + const match = str.match(/^(\/)?(.+?)(?:\/([gimsuyv]*))?$/); // Regex to match regex return match ? new RegExp( match[2], // Pattern From 62485e8694b539128eb5555c5da2dc37bf219861 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 28 Jun 2024 18:17:38 -0300 Subject: [PATCH 09/10] Obliterate Sentry --- scripts/generateReport.ts | 2 + src/plugins/_core/noTrack.ts | 56 ++++++++++++++++++++++++ src/webpack/patchWebpack.ts | 82 ++++++++++++++++++------------------ 3 files changed, 98 insertions(+), 42 deletions(-) diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 0b94aaf72..9ffe6fb08 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -289,6 +289,8 @@ page.on("console", async e => { page.on("error", e => console.error("[Error]", e.message)); page.on("pageerror", e => { + if (e.message.includes("Sentry successfully disabled")) return; + if (!e.message.startsWith("Object") && !e.message.includes("Cannot find module")) { console.error("[Page Error]", e.message); report.otherErrors.push(e.message); diff --git a/src/plugins/_core/noTrack.ts b/src/plugins/_core/noTrack.ts index 26b36735a..ef2849bbc 100644 --- a/src/plugins/_core/noTrack.ts +++ b/src/plugins/_core/noTrack.ts @@ -18,6 +18,7 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; +import { Logger } from "@utils/Logger"; import definePlugin, { OptionType, StartAt } from "@utils/types"; const settings = definePluginSettings({ @@ -71,9 +72,64 @@ export default definePlugin({ startAt: StartAt.Init, start() { + // Sentry is initialized in its own WebpackInstance. + // It has everything it needs preloaded, so, it doesn't include any chunk loading functionality. + // Because of that, its WebpackInstance doesnt export wreq.m or wreq.c + + // To circuvent this and disable Sentry we are gonna hook when wreq.g of its WebpackInstance is set. + // When that happens we are gonna obtain a reference to its internal module cache (wreq.c) and proxy its prototype, + // so, when the first require to initialize the Sentry is attempted, we are gonna forcefully throw an error and abort everything. + Object.defineProperty(Function.prototype, "g", { + configurable: true, + + set(v: any) { + Object.defineProperty(this, "g", { + value: v, + configurable: true, + enumerable: true, + writable: true + }); + + // Ensure this is most likely the Sentry WebpackInstance. + // Function.g is a very generic property and is not uncommon for another WebpackInstance (or even a React component: ) to include it + const { stack } = new Error(); + if (!(stack?.includes("discord.com") || stack?.includes("discordapp.com")) || this.c != null || !String(this).includes("exports:{}")) { + return; + } + + const cacheExtractSym = Symbol("vencord.cacheExtract"); + Object.defineProperty(Object.prototype, cacheExtractSym, { + configurable: true, + + get() { + // One more condition to check if this is the Sentry WebpackInstance + if (Array.isArray(this)) { + return { exports: {} }; + } + + new Logger("NoTrack", "#8caaee").info("Disabling Sentry by proxying its WebpackInstance cache"); + Object.setPrototypeOf(this, new Proxy(this, { + get() { + throw new Error("Sentry successfully disabled"); + } + })); + + Reflect.deleteProperty(Object.prototype, cacheExtractSym); + Reflect.deleteProperty(window, "DiscordSentry"); + return { exports: {} }; + } + }); + + // WebpackRequire our fake module id + this(cacheExtractSym); + } + }); + Object.defineProperty(window, "DiscordSentry", { configurable: true, + set() { + new Logger("NoTrack", "#8caaee").error("Failed to disable Sentry. Falling back to deleting window.DiscordSentry"); Reflect.deleteProperty(window, "DiscordSentry"); } }); diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index bd47cefad..4b3b28a8e 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -56,58 +56,56 @@ Object.defineProperty(window, WEBPACK_CHUNK, { // normally, this is populated via webpackGlobal.push, which we patch below. // However, Discord has their .m prepopulated. // Thus, we use this hack to immediately access their wreq.m and patch all already existing factories -// -// Update: Discord now has TWO webpack instances. Their normal one and sentry -// Sentry does not push chunks to the global at all, so this same patch now also handles their sentry modules Object.defineProperty(Function.prototype, "m", { configurable: true, set(v: any) { - // When using react devtools or other extensions, we may also catch their webpack here. - // This ensures we actually got the right one - const { stack } = new Error(); - if ((stack?.includes("discord.com") || stack?.includes("discordapp.com")) && !Array.isArray(v)) { - const fileName = stack.match(/\/assets\/(.+?\.js)/)?.[1] ?? ""; - - logger.info("Found Webpack module factory", fileName); - patchFactories(v); - - // Define a setter for the bundlePath property of WebpackRequire. Only the main Webpack has this property. - // So if the setter is called, this means we can initialize the internal references to WebpackRequire. - Object.defineProperty(this, "p", { - configurable: true, - - set(this: WebpackInstance, bundlePath: string) { - Object.defineProperty(this, "p", { - value: bundlePath, - configurable: true, - enumerable: true, - writable: true - }); - - clearTimeout(setterTimeout); - - if (bundlePath !== "/assets/") return; - - logger.info(`Main Webpack found in ${fileName}, initializing internal references to WebpackRequire`); - _initWebpack(this); - - for (const beforeInitListener of beforeInitListeners) { - beforeInitListener(this); - } - } - }); - // setImmediate to clear this property setter if this is not the main Webpack. - // If this is the main Webpack, wreq.p will always be set before the timeout runs. - const setterTimeout = setTimeout(() => Reflect.deleteProperty(this, "p"), 0); - } - Object.defineProperty(this, "m", { value: v, configurable: true, enumerable: true, writable: true }); + + // When using react devtools or other extensions, we may also catch their webpack here. + // This ensures we actually got the right one + const { stack } = new Error(); + if (!(stack?.includes("discord.com") || stack?.includes("discordapp.com")) || Array.isArray(v)) { + return; + } + + const fileName = stack.match(/\/assets\/(.+?\.js)/)?.[1] ?? ""; + logger.info("Found Webpack module factory", fileName); + + patchFactories(v); + + // Define a setter for the bundlePath property of WebpackRequire. Only the main Webpack has this property. + // So if the setter is called, this means we can initialize the internal references to WebpackRequire. + Object.defineProperty(this, "p", { + configurable: true, + + set(this: WebpackInstance, bundlePath: string) { + Object.defineProperty(this, "p", { + value: bundlePath, + configurable: true, + enumerable: true, + writable: true + }); + + clearTimeout(setterTimeout); + if (bundlePath !== "/assets/") return; + + logger.info(`Main Webpack found in ${fileName}, initializing internal references to WebpackRequire`); + _initWebpack(this); + + for (const beforeInitListener of beforeInitListeners) { + beforeInitListener(this); + } + } + }); + // setImmediate to clear this property setter if this is not the main Webpack. + // If this is the main Webpack, wreq.p will always be set before the timeout runs. + const setterTimeout = setTimeout(() => Reflect.deleteProperty(this, "p"), 0); } }); From 5c1c786cf9065c8d2e510a655229713a85c1834e Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 28 Jun 2024 18:29:40 -0300 Subject: [PATCH 10/10] Fix ReviewDB patch --- src/plugins/reviewDB/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/reviewDB/index.tsx b/src/plugins/reviewDB/index.tsx index 800de4a1f..7fdc1509a 100644 --- a/src/plugins/reviewDB/index.tsx +++ b/src/plugins/reviewDB/index.tsx @@ -87,7 +87,7 @@ export default definePlugin({ } }, { - find: ".VIEW_FULL_PROFILE,", + find: ".BITE_SIZE,user:", replacement: { match: /(?<=\.BITE_SIZE,children:\[)\(0,\i\.jsx\)\(\i\.\i,\{user:(\i),/, replace: "$self.BiteSizeReviewsButton({user:$1}),$&"