Obliterate Sentry
This commit is contained in:
		
							parent
							
								
									e4bf71784e
								
							
						
					
					
						commit
						62485e8694
					
				
					 3 changed files with 98 additions and 42 deletions
				
			
		|  | @ -289,6 +289,8 @@ page.on("console", async e => { | ||||||
| 
 | 
 | ||||||
| page.on("error", e => console.error("[Error]", e.message)); | page.on("error", e => console.error("[Error]", e.message)); | ||||||
| page.on("pageerror", e => { | page.on("pageerror", e => { | ||||||
|  |     if (e.message.includes("Sentry successfully disabled")) return; | ||||||
|  | 
 | ||||||
|     if (!e.message.startsWith("Object") && !e.message.includes("Cannot find module")) { |     if (!e.message.startsWith("Object") && !e.message.includes("Cannot find module")) { | ||||||
|         console.error("[Page Error]", e.message); |         console.error("[Page Error]", e.message); | ||||||
|         report.otherErrors.push(e.message); |         report.otherErrors.push(e.message); | ||||||
|  |  | ||||||
|  | @ -18,6 +18,7 @@ | ||||||
| 
 | 
 | ||||||
| import { definePluginSettings } from "@api/Settings"; | import { definePluginSettings } from "@api/Settings"; | ||||||
| import { Devs } from "@utils/constants"; | import { Devs } from "@utils/constants"; | ||||||
|  | import { Logger } from "@utils/Logger"; | ||||||
| import definePlugin, { OptionType, StartAt } from "@utils/types"; | import definePlugin, { OptionType, StartAt } from "@utils/types"; | ||||||
| 
 | 
 | ||||||
| const settings = definePluginSettings({ | const settings = definePluginSettings({ | ||||||
|  | @ -71,9 +72,64 @@ export default definePlugin({ | ||||||
| 
 | 
 | ||||||
|     startAt: StartAt.Init, |     startAt: StartAt.Init, | ||||||
|     start() { |     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: <g></g>) 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", { |         Object.defineProperty(window, "DiscordSentry", { | ||||||
|             configurable: true, |             configurable: true, | ||||||
|  | 
 | ||||||
|             set() { |             set() { | ||||||
|  |                 new Logger("NoTrack", "#8caaee").error("Failed to disable Sentry. Falling back to deleting window.DiscordSentry"); | ||||||
|                 Reflect.deleteProperty(window, "DiscordSentry"); |                 Reflect.deleteProperty(window, "DiscordSentry"); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|  | @ -56,20 +56,27 @@ Object.defineProperty(window, WEBPACK_CHUNK, { | ||||||
| // normally, this is populated via webpackGlobal.push, which we patch below.
 | // normally, this is populated via webpackGlobal.push, which we patch below.
 | ||||||
| // However, Discord has their .m prepopulated.
 | // However, Discord has their .m prepopulated.
 | ||||||
| // Thus, we use this hack to immediately access their wreq.m and patch all already existing factories
 | // 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", { | Object.defineProperty(Function.prototype, "m", { | ||||||
|     configurable: true, |     configurable: true, | ||||||
| 
 | 
 | ||||||
|     set(v: any) { |     set(v: any) { | ||||||
|  |         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.
 |         // When using react devtools or other extensions, we may also catch their webpack here.
 | ||||||
|         // This ensures we actually got the right one
 |         // This ensures we actually got the right one
 | ||||||
|         const { stack } = new Error(); |         const { stack } = new Error(); | ||||||
|         if ((stack?.includes("discord.com") || stack?.includes("discordapp.com")) && !Array.isArray(v)) { |         if (!(stack?.includes("discord.com") || stack?.includes("discordapp.com")) || Array.isArray(v)) { | ||||||
|             const fileName = stack.match(/\/assets\/(.+?\.js)/)?.[1] ?? ""; |             return; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|  |         const fileName = stack.match(/\/assets\/(.+?\.js)/)?.[1] ?? ""; | ||||||
|         logger.info("Found Webpack module factory", fileName); |         logger.info("Found Webpack module factory", fileName); | ||||||
|  | 
 | ||||||
|         patchFactories(v); |         patchFactories(v); | ||||||
| 
 | 
 | ||||||
|         // Define a setter for the bundlePath property of WebpackRequire. Only the main Webpack has this property.
 |         // Define a setter for the bundlePath property of WebpackRequire. Only the main Webpack has this property.
 | ||||||
|  | @ -86,7 +93,6 @@ Object.defineProperty(Function.prototype, "m", { | ||||||
|                 }); |                 }); | ||||||
| 
 | 
 | ||||||
|                 clearTimeout(setterTimeout); |                 clearTimeout(setterTimeout); | ||||||
| 
 |  | ||||||
|                 if (bundlePath !== "/assets/") return; |                 if (bundlePath !== "/assets/") return; | ||||||
| 
 | 
 | ||||||
|                 logger.info(`Main Webpack found in ${fileName}, initializing internal references to WebpackRequire`); |                 logger.info(`Main Webpack found in ${fileName}, initializing internal references to WebpackRequire`); | ||||||
|  | @ -101,14 +107,6 @@ Object.defineProperty(Function.prototype, "m", { | ||||||
|         // If this is the main Webpack, wreq.p will always be set before the timeout runs.
 |         // If this is the main Webpack, wreq.p will always be set before the timeout runs.
 | ||||||
|         const setterTimeout = setTimeout(() => Reflect.deleteProperty(this, "p"), 0); |         const setterTimeout = setTimeout(() => Reflect.deleteProperty(this, "p"), 0); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|         Object.defineProperty(this, "m", { |  | ||||||
|             value: v, |  | ||||||
|             configurable: true, |  | ||||||
|             enumerable: true, |  | ||||||
|             writable: true |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| function patchPush(webpackGlobal: any) { | function patchPush(webpackGlobal: any) { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue