Path: blob/main/plugins/default-browser-emulator/injected-scripts/_proxyUtils.ts
1029 views
/////// MASK TO STRING ////////////////////////////////////////////////////////////////////////////////////////////////12// eslint-disable-next-line prefer-const -- must be let: could change for different browser (ie, Safari)3let nativeToStringFunctionString = `${Function.toString}`;4// when functions are re-bound to work around the loss of scope issue in chromium, they blow up their native toString5const overriddenFns = new Map<Function, string>();67// eslint-disable-next-line no-extend-native8Object.defineProperty(Function.prototype, 'toString', {9...Object.getOwnPropertyDescriptor(Function.prototype, 'toString'),10value: new Proxy(Function.prototype.toString, {11apply(target: () => string, thisArg: any, args?: any): any {12if (overriddenFns.has(thisArg)) {13return overriddenFns.get(thisArg);14}15// from puppeteer-stealth: Check if the toString prototype of the context is the same as the global prototype,16// if not indicates that we are doing a check across different windows17const hasSameProto = Object.getPrototypeOf(Function.prototype.toString).isPrototypeOf(18thisArg.toString,19);20if (hasSameProto === false) {21// Pass the call on to the local Function.prototype.toString instead22return thisArg.toString(...(args ?? []));23}24try {25return target.apply(thisArg, args);26} catch (error) {27cleanErrorStack(error, (line, i) => {28if (line.includes('Object.toString') && i === 1) {29return line.replace('Object.toString', 'Function.toString');30}31return line;32});33throw error;34}35},36setPrototypeOf(target: any, v: any): boolean {37let protoTarget = v;38if (v === Function.prototype.toString) {39protoTarget = target;40}41try {42return ObjectCached.setPrototypeOf(target, protoTarget);43} catch (error) {44throw cleanErrorStack(error, null, true);45}46},47}),48});49overriddenFns.set(Function.prototype.toString, nativeToStringFunctionString);5051/////// END TOSTRING //////////////////////////////////////////////////////////////////////////////////////////////////5253// From puppeteer-stealth: this is to prevent someone snooping at Reflect calls54const ReflectCached = {55construct: Reflect.construct.bind(Reflect),56get: Reflect.get.bind(Reflect),57set: Reflect.set.bind(Reflect),58apply: Reflect.apply.bind(Reflect),59ownKeys: Reflect.ownKeys.bind(Reflect),60getOwnPropertyDescriptor: Reflect.getOwnPropertyDescriptor.bind(Reflect),61};6263const ObjectCached = {64setPrototypeOf: Object.setPrototypeOf.bind(Object),65};6667enum ProxyOverride {68callOriginal = '_____invoke_original_____',69}7071declare let sourceUrl: string;7273function cleanErrorStack(74error: Error,75replaceLineFn?: (line: string, index: number) => string,76startAfterSourceUrl = false,77) {78if (!error.stack) return error;7980const split = error.stack.includes('\r\n') ? '\r\n' : '\n';81const stack = error.stack.split(/\r?\n/);82const newStack = [];83for (let i = 0; i < stack.length; i += 1) {84let line = stack[i];85if (line.includes(sourceUrl)) {86if (startAfterSourceUrl === true) {87newStack.length = 1;88}89continue;90}91if (replaceLineFn) line = replaceLineFn(line, i);92newStack.push(line);93}94error.stack = newStack.join(split);95return error;96}9798function proxyConstructor<T, K extends keyof T>(99owner: T,100key: K,101overrideFn: (102target?: T[K],103argArray?: T[K] extends new (...args: infer P) => any ? P : never[],104newTarget?: T[K],105) => (T[K] extends new () => infer Z ? Z : never) | ProxyOverride,106) {107const descriptor = Object.getOwnPropertyDescriptor(owner, key);108const toString = descriptor.value.toString();109descriptor.value = new Proxy(descriptor.value, {110construct() {111try {112const result = overrideFn(...arguments);113if (result !== ProxyOverride.callOriginal) {114return result as any;115}116} catch (err) {117throw cleanErrorStack(err);118}119return ReflectCached.construct(...arguments);120},121});122overriddenFns.set(descriptor.value, toString);123Object.defineProperty(owner, key, descriptor);124}125126function proxyFunction<T, K extends keyof T>(127thisObject: T,128functionName: K,129overrideFn: (130target?: T[K],131thisArg?: T,132argArray?: T[K] extends (...args: infer P) => any ? P : never[],133) => (T[K] extends (...args: any[]) => infer Z ? Z : never) | ProxyOverride,134overrideOnlyForInstance = false,135) {136const descriptorInHierarchy = getDescriptorInHierarchy(thisObject, functionName);137if (!descriptorInHierarchy) {138throw new Error(`Could not find descriptor for function: ${functionName}`);139}140const { descriptorOwner, descriptor } = descriptorInHierarchy;141142const toString = descriptor.value.toString();143descriptorOwner[functionName] = new Proxy<any>(descriptorOwner[functionName], {144apply(target, thisArg, argArray) {145if (overrideOnlyForInstance === false || thisArg === thisObject) {146try {147const result = overrideFn(...arguments);148if (result !== ProxyOverride.callOriginal) {149if (result instanceof Promise && result.catch) {150return result.catch(err => {151throw cleanErrorStack(err);152});153}154return result;155}156} catch (err) {157throw cleanErrorStack(err);158}159}160return ReflectCached.apply(...arguments);161},162setPrototypeOf(target: any, v: any): boolean {163let protoTarget = v;164if (v === descriptorOwner[functionName]) {165protoTarget = target;166}167try {168return ObjectCached.setPrototypeOf(target, protoTarget);169} catch (error) {170throw cleanErrorStack(error, null, true);171}172},173});174overriddenFns.set(descriptorOwner[functionName] as any, toString);175return thisObject[functionName];176}177178function proxyGetter<T, K extends keyof T>(179thisObject: T,180propertyName: K,181overrideFn: (target?: T[K], thisArg?: T) => T[K] | ProxyOverride,182overrideOnlyForInstance = false,183) {184const descriptorInHierarchy = getDescriptorInHierarchy(thisObject, propertyName);185if (!descriptorInHierarchy) {186throw new Error(`Could not find descriptor for getter: ${propertyName}`);187}188189const { descriptorOwner, descriptor } = descriptorInHierarchy;190const toString = descriptor.get.toString();191descriptor.get = new Proxy(descriptor.get, {192apply(_, thisArg) {193if (overrideOnlyForInstance === false || thisArg === thisObject) {194const result = overrideFn(...arguments);195if (result !== ProxyOverride.callOriginal) return result;196}197return ReflectCached.apply(...arguments);198},199setPrototypeOf(target: any, v: any): boolean {200let protoTarget = v;201if (v === descriptor.get) {202protoTarget = target;203}204try {205return ObjectCached.setPrototypeOf(target, protoTarget);206} catch (error) {207throw cleanErrorStack(error, null, true);208}209},210});211overriddenFns.set(descriptor.get, toString);212Object.defineProperty(descriptorOwner, propertyName, descriptor);213return descriptor.get;214}215216function proxySetter<T, K extends keyof T>(217thisObject: T,218propertyName: K,219overrideFn: (220target?: T[K],221thisArg?: T,222value?: T[K] extends (value: infer P) => any ? P : never,223) => void | ProxyOverride,224overrideOnlyForInstance = false,225) {226const descriptorInHierarchy = getDescriptorInHierarchy(thisObject, propertyName);227if (!descriptorInHierarchy) {228throw new Error(`Could not find descriptor for setter: ${propertyName}`);229}230const { descriptorOwner, descriptor } = descriptorInHierarchy;231const toString = descriptor.set.toString();232descriptor.set = new Proxy(descriptor.set, {233apply(target, thisArg, args) {234if (!overrideOnlyForInstance || thisArg === thisObject) {235const result = overrideFn(target as any, thisArg, ...args);236if (result !== ProxyOverride.callOriginal) return result;237}238return ReflectCached.apply(...arguments);239},240});241overriddenFns.set(descriptor.set, toString);242Object.defineProperty(descriptorOwner, propertyName, descriptor);243return descriptor.set;244}245246function getDescriptorInHierarchy<T, K extends keyof T>(obj: T, prop: K) {247let proto = obj;248do {249if (proto.hasOwnProperty(prop)) {250return { descriptorOwner: proto, descriptor: Object.getOwnPropertyDescriptor(proto, prop) };251}252proto = Object.getPrototypeOf(proto);253} while (proto);254255return null;256}257258// eslint-disable-next-line @typescript-eslint/no-unused-vars259function addDescriptorAfterProperty(260path: string,261prevProperty: string,262propertyName: string,263descriptor: PropertyDescriptor,264) {265const owner = getObjectAtPath(path);266if (!owner) {267console.log(`ERROR: Parent for property descriptor not found: ${path} -> ${propertyName}`);268return;269}270const descriptors = Object.getOwnPropertyDescriptors(owner);271// if already exists, don't add again272if (descriptors[propertyName]) {273return;274}275276const inHierarchy = getDescriptorInHierarchy(owner, propertyName);277if (inHierarchy && descriptor.value) {278if (inHierarchy.descriptor.get) {279proxyGetter(owner, propertyName, () => descriptor.value, true);280} else {281throw new Error("Can't override descriptor that doesnt have a getter");282}283return;284}285286let hasPassedProperty = false;287for (const [key, existingDescriptor] of Object.entries(descriptors)) {288if (hasPassedProperty) {289// only way to reorder properties is to re-add them290delete owner[key];291Object.defineProperty(owner, key, existingDescriptor);292}293if (key === prevProperty) {294Object.defineProperty(owner, propertyName, descriptor);295hasPassedProperty = true;296}297}298}299300if (typeof module === 'object' && typeof module.exports === 'object') {301module.exports = {302proxyFunction,303};304}305306307