fix issues & crashes caused by recent Discord changes (#3231)
Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
This commit is contained in:
parent
8dfbb5f9d8
commit
6b0c02daa6
5 changed files with 82 additions and 46 deletions
|
@ -78,9 +78,9 @@ async function runReporter() {
|
|||
result = await Webpack.extractAndLoadChunks(code, matcher);
|
||||
if (result === false) result = null;
|
||||
} else if (method === "mapMangledModule") {
|
||||
const [code, mapper] = args;
|
||||
const [code, mapper, includeBlacklistedExports] = args;
|
||||
|
||||
result = Webpack.mapMangledModule(code, mapper);
|
||||
result = Webpack.mapMangledModule(code, mapper, includeBlacklistedExports);
|
||||
if (Object.keys(result).length !== Object.keys(mapper).length) throw new Error("Webpack Find Fail");
|
||||
} else {
|
||||
// @ts-ignore
|
||||
|
|
|
@ -158,6 +158,9 @@ export default definePlugin({
|
|||
aboveActivity: getIntlMessage("ACTIVITY_SETTINGS")
|
||||
};
|
||||
|
||||
if (!names[settingsLocation] || names[settingsLocation].endsWith("_SETTINGS"))
|
||||
return firstChild === "PREMIUM";
|
||||
|
||||
return header === names[settingsLocation];
|
||||
} catch {
|
||||
return firstChild === "PREMIUM";
|
||||
|
|
|
@ -58,9 +58,9 @@ export const { match, P }: Pick<typeof import("ts-pattern"), "match" | "P"> = ma
|
|||
export const lodash: typeof import("lodash") = findByPropsLazy("debounce", "cloneDeep");
|
||||
|
||||
export const i18n = mapMangledModuleLazy('defaultLocale:"en-US"', {
|
||||
t: filters.byProps(runtimeHashMessageKey("DISCORD")),
|
||||
intl: filters.byProps("string", "format"),
|
||||
t: filters.byProps(runtimeHashMessageKey("DISCORD"))
|
||||
});
|
||||
}, true);
|
||||
|
||||
export let SnowflakeUtils: t.SnowflakeUtils;
|
||||
waitFor(["fromTimestamp", "extractTimestamp"], m => SnowflakeUtils = m);
|
||||
|
|
|
@ -12,7 +12,7 @@ import { canonicalizeReplacement } from "@utils/patches";
|
|||
import { Patch, PatchReplacement } from "@utils/types";
|
||||
|
||||
import { traceFunctionWithResults } from "../debug/Tracer";
|
||||
import { _initWebpack, _shouldIgnoreModule, factoryListeners, findModuleId, moduleListeners, waitForSubscriptions, wreq } from "./webpack";
|
||||
import { _blacklistBadModules, _initWebpack, factoryListeners, findModuleId, moduleListeners, waitForSubscriptions, wreq } from "./webpack";
|
||||
import { AnyModuleFactory, AnyWebpackRequire, MaybePatchedModuleFactory, ModuleExports, PatchedModuleFactory, WebpackRequire } from "./wreq.d";
|
||||
|
||||
export const patches = [] as Patch[];
|
||||
|
@ -190,6 +190,20 @@ define(Function.prototype, "m", {
|
|||
*/
|
||||
|
||||
define(this, "m", { value: proxiedModuleFactories });
|
||||
|
||||
// Overwrite Webpack's defineExports function to define the export descriptors configurable.
|
||||
// This is needed so we can later blacklist specific exports from Webpack search by making them non-enumerable
|
||||
this.d = function (exports: object, getters: object) {
|
||||
for (const key in getters) {
|
||||
if (Object.hasOwn(getters, key) && !Object.hasOwn(exports, key)) {
|
||||
Object.defineProperty(exports, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: getters[key],
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
});
|
||||
|
@ -407,19 +421,8 @@ function runFactoryWithWrap(patchedFactory: PatchedModuleFactory, thisArg: unkno
|
|||
return factoryReturn;
|
||||
}
|
||||
|
||||
if (typeof require === "function") {
|
||||
const shouldIgnoreModule = _shouldIgnoreModule(exports);
|
||||
|
||||
if (shouldIgnoreModule) {
|
||||
if (require.c != null) {
|
||||
Object.defineProperty(require.c, module.id, {
|
||||
value: require.c[module.id],
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: true
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof require === "function" && require.c) {
|
||||
if (_blacklistBadModules(require.c, exports, module.id)) {
|
||||
return factoryReturn;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ import { canonicalizeMatch } from "@utils/patches";
|
|||
|
||||
import { traceFunction } from "../debug/Tracer";
|
||||
import { Flux } from "./common";
|
||||
import { AnyModuleFactory, ModuleExports, WebpackRequire } from "./wreq";
|
||||
import { AnyModuleFactory, AnyWebpackRequire, ModuleExports, WebpackRequire } from "./wreq";
|
||||
|
||||
const logger = new Logger("Webpack");
|
||||
|
||||
|
@ -112,18 +112,38 @@ export function _initWebpack(webpackRequire: WebpackRequire) {
|
|||
// Credits to Zerebos for implementing this in BD, thus giving the idea for us to implement it too
|
||||
const TypedArray = Object.getPrototypeOf(Int8Array);
|
||||
|
||||
function _shouldIgnoreValue(value: any) {
|
||||
const PROXY_CHECK = "is this a proxy that returns values for any key?";
|
||||
function shouldIgnoreValue(value: any) {
|
||||
if (value == null) return true;
|
||||
if (value === window) return true;
|
||||
if (value === document || value === document.documentElement) return true;
|
||||
if (value[Symbol.toStringTag] === "DOMTokenList") return true;
|
||||
if (value[Symbol.toStringTag] === "DOMTokenList" || value[Symbol.toStringTag] === "IntlMessagesProxy") return true;
|
||||
// Discord might export a Proxy that returns non-null values for any property key which would pass all findByProps filters.
|
||||
// One example of this is their i18n Proxy. However, that is already covered by the IntlMessagesProxy check above.
|
||||
// As a fallback if they ever change the name or add a new Proxy, use a unique string to detect such proxies and ignore them
|
||||
if (value[PROXY_CHECK] !== void 0) {
|
||||
// their i18n Proxy "caches" by setting each accessed property to the return, so try to delete
|
||||
Reflect.deleteProperty(value, PROXY_CHECK);
|
||||
return true;
|
||||
}
|
||||
if (value instanceof TypedArray) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function _shouldIgnoreModule(exports: any) {
|
||||
if (_shouldIgnoreValue(exports)) {
|
||||
function makePropertyNonEnumerable(target: Object, key: PropertyKey) {
|
||||
const descriptor = Object.getOwnPropertyDescriptor(target, key);
|
||||
if (descriptor == null) return;
|
||||
|
||||
Reflect.defineProperty(target, key, {
|
||||
...descriptor,
|
||||
enumerable: false
|
||||
});
|
||||
}
|
||||
|
||||
export function _blacklistBadModules(requireCache: NonNullable<AnyWebpackRequire["c"]>, exports: ModuleExports, moduleId: PropertyKey) {
|
||||
if (shouldIgnoreValue(exports)) {
|
||||
makePropertyNonEnumerable(requireCache, moduleId);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -131,14 +151,16 @@ export function _shouldIgnoreModule(exports: any) {
|
|||
return false;
|
||||
}
|
||||
|
||||
let allNonEnumerable = true;
|
||||
let hasOnlyBadProperties = true;
|
||||
for (const exportKey in exports) {
|
||||
if (!_shouldIgnoreValue(exports[exportKey])) {
|
||||
allNonEnumerable = false;
|
||||
if (shouldIgnoreValue(exports[exportKey])) {
|
||||
makePropertyNonEnumerable(exports, exportKey);
|
||||
} else {
|
||||
hasOnlyBadProperties = false;
|
||||
}
|
||||
}
|
||||
|
||||
return allNonEnumerable;
|
||||
return hasOnlyBadProperties;
|
||||
}
|
||||
|
||||
let devToolsOpen = false;
|
||||
|
@ -406,7 +428,10 @@ export function findByCodeLazy(...code: CodeFilter) {
|
|||
* Find a store by its displayName
|
||||
*/
|
||||
export function findStore(name: StoreNameFilter) {
|
||||
const res: any = Flux.Store.getAll().find(filters.byStoreName(name));
|
||||
const res = Flux.Store.getAll
|
||||
? Flux.Store.getAll().find(filters.byStoreName(name))
|
||||
: find(filters.byStoreName(name), { isIndirect: true });
|
||||
|
||||
if (!res)
|
||||
handleModuleNotFound("findStore", name);
|
||||
return res;
|
||||
|
@ -474,12 +499,27 @@ export function findExportedComponentLazy<T extends object = any>(...props: Prop
|
|||
});
|
||||
}
|
||||
|
||||
function getAllPropertyNames(object: Object, includeNonEnumerable: boolean) {
|
||||
const names = new Set<PropertyKey>();
|
||||
|
||||
const getKeys = includeNonEnumerable ? Object.getOwnPropertyNames : Object.keys;
|
||||
do {
|
||||
getKeys(object).forEach(name => names.add(name));
|
||||
object = Object.getPrototypeOf(object);
|
||||
} while (object != null);
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a mangled module by the provided code "code" (must be unique and can be anywhere in the module)
|
||||
* then maps it into an easily usable module via the specified mappers.
|
||||
*
|
||||
* @param code The code to look for
|
||||
* @param mappers Mappers to create the non mangled exports
|
||||
* @param includeBlacklistedExports Whether to include blacklisted exports in the search.
|
||||
* These exports are dangerous. Accessing properties on them may throw errors
|
||||
* or always return values (so a byProps filter will always return true)
|
||||
* @returns Unmangled exports as specified in mappers
|
||||
*
|
||||
* @example mapMangledModule("headerIdIsManaged:", {
|
||||
|
@ -487,7 +527,7 @@ export function findExportedComponentLazy<T extends object = any>(...props: Prop
|
|||
* closeModal: filters.byCode("key==")
|
||||
* })
|
||||
*/
|
||||
export const mapMangledModule = traceFunction("mapMangledModule", function mapMangledModule<S extends string>(code: string | RegExp | CodeFilter, mappers: Record<S, FilterFn>): Record<S, any> {
|
||||
export const mapMangledModule = traceFunction("mapMangledModule", function mapMangledModule<S extends string>(code: string | RegExp | CodeFilter, mappers: Record<S, FilterFn>, includeBlacklistedExports = false): Record<S, any> {
|
||||
const exports = {} as Record<S, any>;
|
||||
|
||||
const id = findModuleId(...Array.isArray(code) ? code : [code]);
|
||||
|
@ -495,8 +535,9 @@ export const mapMangledModule = traceFunction("mapMangledModule", function mapMa
|
|||
return exports;
|
||||
|
||||
const mod = wreq(id as any);
|
||||
const keys = getAllPropertyNames(mod, includeBlacklistedExports);
|
||||
outer:
|
||||
for (const key in mod) {
|
||||
for (const key of keys) {
|
||||
const member = mod[key];
|
||||
for (const newName in mappers) {
|
||||
// if the current mapper matches this module
|
||||
|
@ -510,24 +551,13 @@ export const mapMangledModule = traceFunction("mapMangledModule", function mapMa
|
|||
});
|
||||
|
||||
/**
|
||||
* {@link mapMangledModule}, lazy.
|
||||
|
||||
* Finds a mangled module by the provided code "code" (must be unique and can be anywhere in the module)
|
||||
* then maps it into an easily usable module via the specified mappers.
|
||||
*
|
||||
* @param code The code to look for
|
||||
* @param mappers Mappers to create the non mangled exports
|
||||
* @returns Unmangled exports as specified in mappers
|
||||
*
|
||||
* @example mapMangledModule("headerIdIsManaged:", {
|
||||
* openModal: filters.byCode("headerIdIsManaged:"),
|
||||
* closeModal: filters.byCode("key==")
|
||||
* })
|
||||
* lazy mapMangledModule
|
||||
* @see {@link mapMangledModule}
|
||||
*/
|
||||
export function mapMangledModuleLazy<S extends string>(code: string | RegExp | CodeFilter, mappers: Record<S, FilterFn>): Record<S, any> {
|
||||
if (IS_REPORTER) lazyWebpackSearchHistory.push(["mapMangledModule", [code, mappers]]);
|
||||
export function mapMangledModuleLazy<S extends string>(code: string | RegExp | CodeFilter, mappers: Record<S, FilterFn>, includeBlacklistedExports = false): Record<S, any> {
|
||||
if (IS_REPORTER) lazyWebpackSearchHistory.push(["mapMangledModule", [code, mappers, includeBlacklistedExports]]);
|
||||
|
||||
return proxyLazy(() => mapMangledModule(code, mappers));
|
||||
return proxyLazy(() => mapMangledModule(code, mappers, includeBlacklistedExports));
|
||||
}
|
||||
|
||||
export const DefaultExtractAndLoadChunksRegex = /(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?|Promise\.resolve\(\))\.then\(\i\.bind\(\i,"?([^)]+?)"?\)\)/;
|
||||
|
|
Loading…
Reference in a new issue