From 17c80b791d424009f77c83411f46302a5c2b7079 Mon Sep 17 00:00:00 2001 From: kkoooqq <> Date: Thu, 18 Nov 2021 10:27:46 -0500 Subject: [PATCH] feat: providing configured patches --- package.json | 2 +- src/core/BrowserBuilder.ts | 57 ++- src/core/BrowserLauncher.ts | 1 + src/core/DeviceDescriptor.ts | 12 +- src/core/Driver.ts | 8 +- src/core/PptrPatcher.ts | 441 ++---------------- src/plugins/evasions/_utils/index.js | 3 +- .../evasions/audio.fingerprint/index.js | 10 +- src/plugins/evasions/barcodeDetector/index.js | 10 +- src/plugins/evasions/bluetooth/index.js | 12 +- .../evasions/canvas.fingerprint/index.js | 10 +- .../evasions/chrome.loadTimes/index.js | 6 +- .../evasions/emoji.fingerprint/index.js | 10 +- .../evasions/font.fingerprint/index.js | 20 +- .../evasions/iframe.contentWindow/index.js | 9 +- src/plugins/evasions/iframe.src/index.js | 2 + src/plugins/evasions/keyboard/index.js | 16 +- src/plugins/evasions/mimeTypes/index.js | 13 +- .../navigator.batteryManager/index.js | 14 +- .../evasions/navigator.mediaDevices/index.js | 17 +- .../evasions/navigator.permissions/index.js | 39 +- .../navigator.plugins-native/index.js | 229 ++++----- .../evasions/properties.getter/index.js | 83 ++-- src/plugins/evasions/sourceurl/index.js | 1 + .../evasions/user-agent-override/index.js | 8 +- src/plugins/evasions/webgl/index.js | 54 ++- src/plugins/evasions/webrtc/index.js | 24 +- .../evasions/window.history.length/index.js | 6 +- .../evasions/window.speechSynthesis/index.js | 8 +- src/plugins/evasions/workers/index.js | 171 +++---- src/plugins/taskEnv/index.js | 31 -- src/plugins/taskEnv/package.json | 4 - test/__tests__/test-keyboard.js | 41 ++ test/__tests__/test-window.speechSynthesis.js | 80 ++++ 34 files changed, 599 insertions(+), 853 deletions(-) delete mode 100644 src/plugins/taskEnv/index.js delete mode 100644 src/plugins/taskEnv/package.json create mode 100644 test/__tests__/test-keyboard.js create mode 100644 test/__tests__/test-window.speechSynthesis.js diff --git a/package.json b/package.json index f7ab964..ade48c0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fakebrowser", - "version": "0.0.55", + "version": "0.0.60", "description": "🤖 Fake fingerprints to bypass anti-bot systems. Simulate mouse and keyboard operations to make behavior like a real person.", "repository": { "type": "git", diff --git a/src/core/BrowserBuilder.ts b/src/core/BrowserBuilder.ts index cb4efb8..e83d6bf 100644 --- a/src/core/BrowserBuilder.ts +++ b/src/core/BrowserBuilder.ts @@ -1,5 +1,7 @@ // noinspection JSUnusedGlobalSymbols +import * as path from 'path' + import { ConnectParameters, DriverParameters, @@ -15,29 +17,53 @@ import {FakeBrowser, kDefaultWindowsDD} from './FakeBrowser' export class BrowserBuilder { - private readonly _driverParams: DriverParameters + public readonly driverParams: DriverParameters constructor() { - this._driverParams = { + this.driverParams = { doNotHook: false, deviceDesc: kDefaultWindowsDD, userDataDir: '', + evasionPaths: [ + 'chrome.app', + 'chrome.csi', + 'chrome.loadTimes', + 'chrome.runtime', + 'window.history.length', + 'window.matchMedia', + 'navigator.webdriver', + 'sourceurl', + 'navigator.plugins-native', + 'webgl', + 'mimeTypes', + 'navigator.mediaDevices', + 'bluetooth', + 'navigator.permissions', + 'navigator.batteryManager', + 'webrtc', + 'canvas.fingerprint', + 'user-agent-override', + 'iframe.contentWindow', + 'iframe.src', + 'properties.getter', + 'font.fingerprint', + 'emoji.fingerprint', + 'window.speechSynthesis', + 'workers', + 'keyboard', + ].map(e => path.resolve(__dirname, `../plugins/evasions/${e}`)), } } - get driverParams(): DriverParameters { - return this._driverParams - } - get launchParams(): LaunchParameters { - const result = this._driverParams as LaunchParameters + const result = this.driverParams as LaunchParameters result.launchOptions = result.launchOptions || {} return result } get connectParams(): ConnectParameters { - const result = this._driverParams as ConnectParameters + const result = this.driverParams as ConnectParameters result.connectOptions = result.connectOptions || {} return result @@ -54,27 +80,27 @@ export class BrowserBuilder { } deviceDescriptor(value: DeviceDescriptor) { - this._driverParams.deviceDesc = value + this.driverParams.deviceDesc = value return this } displayUserActionLayer(value: boolean) { - this._driverParams.displayUserActionLayer = value + this.driverParams.displayUserActionLayer = value return this } userDataDir(value: string) { - this._driverParams.userDataDir = value + this.driverParams.userDataDir = value return this } log(value: boolean) { - this._driverParams.log = value + this.driverParams.log = value return this } proxy(value: ProxyServer) { - this._driverParams.proxy = value + this.driverParams.proxy = value return this } @@ -88,6 +114,11 @@ export class BrowserBuilder { return this } + evasionPaths(value: string[]) { + this.driverParams.evasionPaths = value + return this + } + async launch(): Promise { if ('undefined' === typeof this.launchParams.maxSurvivalTime) { this.launchParams.maxSurvivalTime = FakeBrowser.globalConfig.defaultBrowserMaxSurvivalTime diff --git a/src/core/BrowserLauncher.ts b/src/core/BrowserLauncher.ts index 3663551..f0a2f81 100644 --- a/src/core/BrowserLauncher.ts +++ b/src/core/BrowserLauncher.ts @@ -48,6 +48,7 @@ export class BrowserLauncher { // or create it if it does not exist. const userDataDir = params.userDataDir + assert(userDataDir) if (!fs.existsSync(userDataDir)) { // may throw diff --git a/src/core/DeviceDescriptor.ts b/src/core/DeviceDescriptor.ts index 1fc9d8b..d4e1560 100644 --- a/src/core/DeviceDescriptor.ts +++ b/src/core/DeviceDescriptor.ts @@ -220,13 +220,14 @@ export type IFontSalt = { } export interface FakeDeviceDescriptor extends DeviceDescriptor { - canvasSalt: number[], + canvasSalt?: number[], // TODO: I should make a correspondence between user's existing fonts and required fonts, // but I don't have time to do it for now. // fakeFonts: Array, - fontSalt: { + fontSalt?: { [key: string]: IFontSalt }, + acceptLanguage?: string, } export default class DeviceDescriptorHelper { @@ -471,13 +472,18 @@ export default class DeviceDescriptorHelper { needsUpdate = true } + // acceptLanguage + if (!fakeDD.acceptLanguage) { + fakeDD.acceptLanguage = this.buildAcceptLanguage(fakeDD) + } + return { fakeDeviceDesc: fakeDD, needsUpdate, } } - static buildAcceptLanguage(deviceDesc: DeviceDescriptor): string { + private static buildAcceptLanguage(deviceDesc: DeviceDescriptor): string { // referer: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Accept-Language // https://developer.mozilla.org/zh-CN/docs/Glossary/Quality_values diff --git a/src/core/Driver.ts b/src/core/Driver.ts index 3a76727..4d3c732 100644 --- a/src/core/Driver.ts +++ b/src/core/Driver.ts @@ -27,7 +27,8 @@ export interface DriverParameters { displayUserActionLayer?: boolean, log?: boolean, proxy?: ProxyServer, - userDataDir: string, + userDataDir?: string, + evasionPaths: string[], } export interface LaunchParameters extends DriverParameters { @@ -213,12 +214,13 @@ export default class Driver { } // browser language - const lang = DeviceDescriptorHelper.buildAcceptLanguage(fakeDD) + assert(fakeDD.acceptLanguage) args.push( - `--lang=${lang}`, + `--lang=${fakeDD.acceptLanguage}`, ) const userDataDir = launchParams.userDataDir + assert(userDataDir) fs.mkdirSync(userDataDir, {recursive: true}) // throw exception args.push( diff --git a/src/core/PptrPatcher.ts b/src/core/PptrPatcher.ts index db0eaa5..28a336c 100644 --- a/src/core/PptrPatcher.ts +++ b/src/core/PptrPatcher.ts @@ -7,444 +7,73 @@ import axios from 'axios' import {CDPSession, Protocol} from 'puppeteer' import {PuppeteerExtra, PuppeteerExtraPlugin} from 'puppeteer-extra' -import {DriverParameters} from './Driver' import {helper} from './helper' +import {DriverParameters} from './Driver' import {FakeBrowser} from './FakeBrowser' -import DeviceDescriptorHelper from './DeviceDescriptor' +import {FakeDeviceDescriptor} from './DeviceDescriptor' + +interface PptrExtraEvasionOpts { + browserUUID: string, + internalHttpServerPort: number, + proxyExportIP?: string, + myRealExportIP: string, + historyLength: number + fakeDD: FakeDeviceDescriptor, +} export class PptrPatcher { static async patch( - uuid: string, + browserUUID: string, pptr: PuppeteerExtra, params: DriverParameters, ) { assert(!!params.fakeDeviceDesc) - await this.patchTaskEnv(uuid, pptr, params) - await this.patchUserActionLayer(uuid, pptr, params) - await this.pathChrome(uuid, pptr, params) - await this.patchWindowHistoryLength(uuid, pptr, params) - await this.patchWindowMatchMedia(uuid, pptr, params) - await this.pathWebdriver(uuid, pptr, params) - await this.pathSourceUrl(uuid, pptr, params) - await this.patchPluginsMineTypes(uuid, pptr, params) - await this.patchWebGL(uuid, pptr, params) - await this.patchMimeTypes(uuid, pptr, params) - await this.patchMediaDevices(uuid, pptr, params) - await this.patchBluetooth(uuid, pptr, params) - await this.patchPermissions(uuid, pptr, params) - await this.patchBatteryManager(uuid, pptr, params) - await this.patchWebRtc(uuid, pptr, params) - await this.patchCanvas2DFingerprint(uuid, pptr, params) - await this.patchUserAgent(uuid, pptr, params) - await this.patchIFrame(uuid, pptr, params) - await this.patchPropertiesGetters(uuid, pptr, params) - await this.patchFonts(uuid, pptr, params) - await this.patchEmojis(uuid, pptr, params) - await this.patchSpeechSynthesis(uuid, pptr, params) - await this.patchWorkers(uuid, pptr, params) - await this.patchKeyboard(uuid, pptr, params) - await this.patchLast(uuid, pptr, params) - } - - // @ts-ignore - private static patchTaskEnv( - uuid: string, - pptr: PuppeteerExtra, - params: DriverParameters, - ) { - const Plugin = require(path.resolve(__dirname, '../plugins/taskEnv')) - const plugin = Plugin({ - env: { - uuid: uuid, - internalHttpServerPort: FakeBrowser.globalConfig.internalHttpServerPort, - }, - }) - - pptr.use(plugin) - } - - // @ts-ignore - private static patchUserActionLayer( - uuid: string, - pptr: PuppeteerExtra, - params: DriverParameters, - ) { - if (params.displayUserActionLayer) { - const Plugin = require(path.resolve(__dirname, '../plugins/user-action-layer')) - const plugin = Plugin() - pptr.use(plugin) + const opts: PptrExtraEvasionOpts = { + browserUUID: browserUUID, + internalHttpServerPort: FakeBrowser.globalConfig.internalHttpServerPort, + fakeDD: params.fakeDeviceDesc, + proxyExportIP: params.proxy && params.proxy.exportIP, + myRealExportIP: await helper.myRealExportIP(), + historyLength: helper.rd(2, 10), } - } - // @ts-ignore - private static pathChrome( - uuid: string, - pptr: PuppeteerExtra, - params: DriverParameters, - ) { - const availableEvasions = [ - 'chrome.app', - 'chrome.csi', - 'chrome.loadTimes', - 'chrome.runtime', - ] - - for (const evasion of availableEvasions) { - const Plugin = require(path.resolve(__dirname, `../plugins/evasions/${evasion}`)) - const plugin = Plugin() + // user action layer + await this.patchUserActionLayer(browserUUID, pptr, params, opts) + + // evasions + for (const evasionPath of params.evasionPaths) { + const Plugin = require(evasionPath) + const plugin = Plugin(opts) pptr.use(plugin) } - } - - // @ts-ignore - private static patchWindowHistoryLength( - uuid: string, - pptr: PuppeteerExtra, - params: DriverParameters, - ) { - const historyLength = helper.rd(2, 10) - - const Plugin = require(path.resolve(__dirname, '../plugins/evasions/window.history.length')) - const plugin = Plugin({historyLength}) - pptr.use(plugin) - - } - - // @ts-ignore - private static patchWindowMatchMedia( - uuid: string, - pptr: PuppeteerExtra, - params: DriverParameters, - ) { - const Plugin = require(path.resolve(__dirname, '../plugins/evasions/window.matchMedia')) - const plugin = Plugin() - pptr.use(plugin) - } - - // @ts-ignore - private static pathWebdriver( - uuid: string, - pptr: PuppeteerExtra, - params: DriverParameters, - ) { - const Plugin = require(path.resolve(__dirname, '../plugins/evasions/navigator.webdriver')) - const plugin = Plugin() - pptr.use(plugin) - } - - // @ts-ignore - private static pathSourceUrl( - uuid: string, - pptr: PuppeteerExtra, - params: DriverParameters, - ) { - const Plugin = require(path.resolve(__dirname, '../plugins/evasions/sourceurl')) - const plugin = Plugin() - pptr.use(plugin) - } - - // @ts-ignore - private static patchPluginsMineTypes( - uuid: string, - pptr: PuppeteerExtra, - params: DriverParameters, - ) { - assert(params.fakeDeviceDesc) - - // - const Plugin = require(path.resolve(__dirname, '../plugins/evasions/navigator.plugins-native')) - const plugin = Plugin({ - plugins: params.fakeDeviceDesc.plugins, - }) - - pptr.use(plugin) - } - - // @ts-ignore - private static patchWebGL( - uuid: string, - pptr: PuppeteerExtra, - params: DriverParameters, - ) { - assert(params.fakeDeviceDesc) - - const Plugin = require(path.resolve(__dirname, '../plugins/evasions/webgl')) - const plugin = Plugin({ - gpu: params.fakeDeviceDesc.gpu, - webgl: params.fakeDeviceDesc.webgl, - webgl2: params.fakeDeviceDesc.webgl2, - }) - - pptr.use(plugin) - } - - // @ts-ignore - private static patchMimeTypes( - uuid: string, - pptr: PuppeteerExtra, - params: DriverParameters, - ) { - assert(params.fakeDeviceDesc) - - const Plugin = require(path.resolve(__dirname, '../plugins/evasions/mimeTypes')) - const plugin = Plugin({ - data: params.fakeDeviceDesc.mimeTypes, - }) - - pptr.use(plugin) - } - - // @ts-ignore - private static patchMediaDevices( - uuid: string, - pptr: PuppeteerExtra, - params: DriverParameters, - ) { - assert(params.fakeDeviceDesc) - - const Plugin = require(path.resolve(__dirname, '../plugins/evasions/navigator.mediaDevices')) - const plugin = Plugin({ - data: params.fakeDeviceDesc.mediaDevices, - }) - - pptr.use(plugin) - } - - // @ts-ignore - private static patchBluetooth( - uuid: string, - pptr: PuppeteerExtra, - params: DriverParameters, - ) { - const Plugin = require(path.resolve(__dirname, '../plugins/evasions/bluetooth')) - const plugin = Plugin({}) - - pptr.use(plugin) - } - - // @ts-ignore - private static patchPermissions( - uuid: string, - pptr: PuppeteerExtra, - params: DriverParameters, - ) { - assert(params.fakeDeviceDesc) - - const Plugin = require(path.resolve(__dirname, '../plugins/evasions/navigator.permissions')) - const plugin = Plugin({ - permissions: params.fakeDeviceDesc.permissions, - }) - - pptr.use(plugin) - } - - // @ts-ignore - private static patchBatteryManager( - uuid: string, - pptr: PuppeteerExtra, - params: DriverParameters, - ) { - assert(params.fakeDeviceDesc) - const Plugin = require(path.resolve(__dirname, '../plugins/evasions/navigator.batteryManager')) - const plugin = Plugin({ - battery: params.fakeDeviceDesc.battery, - }) - - pptr.use(plugin) + // last, tidy up + await this.patchLast(browserUUID, pptr, params, opts) } - // @ts-ignore - private static async patchWebRtc( + private static patchUserActionLayer( uuid: string, pptr: PuppeteerExtra, params: DriverParameters, + opts: PptrExtraEvasionOpts, ) { - if (params.proxy && params.proxy.exportIP) { - const myRealExportIP = await helper.myRealExportIP() - const fakeIPs = [myRealExportIP] - - // if (driverParams.cluster && driverParams.cluster.length) { - // fakeIPs.push(driverParams.cluster) - // } - - // console.log( - // '!!! proxyExportIP: ' + driverParams.proxy.exportIP + - // ' fakeIPs:' + JSON.stringify(fakeIPs) - // ); - - const Plugin = require(path.resolve(__dirname, '../plugins/evasions/webrtc')) - const plugin = Plugin({ - proxyExportIP: params.proxy.exportIP, - fakeIPs, - }) - + if (params.displayUserActionLayer) { + const Plugin = require(path.resolve(__dirname, '../plugins/user-action-layer')) + const plugin = Plugin(opts) pptr.use(plugin) } } - // @ts-ignore - private static patchCanvas2DFingerprint( - uuid: string, - pptr: PuppeteerExtra, - params: DriverParameters, - ) { - assert(params.fakeDeviceDesc) - - const Plugin = require(path.resolve(__dirname, '../plugins/evasions/canvas.fingerprint')) - const plugin = Plugin({ - canvasSalt: params.fakeDeviceDesc.canvasSalt, - fontSalt: params.fakeDeviceDesc.fontSalt, - }) - - pptr.use(plugin) - } - - // @ts-ignore - private static patchFonts( - uuid: string, - pptr: PuppeteerExtra, - params: DriverParameters, - ) { - assert(params.fakeDeviceDesc) - - const Plugin = require(path.resolve(__dirname, '../plugins/evasions/font.fingerprint')) - const plugin = Plugin({ - fontSalt: params.fakeDeviceDesc.fontSalt, - }) - - pptr.use(plugin) - } - - // @ts-ignore - private static patchEmojis( - uuid: string, - pptr: PuppeteerExtra, - params: DriverParameters, - ) { - assert(params.fakeDeviceDesc) - - const Plugin = require(path.resolve(__dirname, '../plugins/evasions/emoji.fingerprint')) - const plugin = Plugin({ - fontSalt: params.fakeDeviceDesc.fontSalt, - }) - - pptr.use(plugin) - } - - // @ts-ignore - private static patchSpeechSynthesis( - uuid: string, - pptr: PuppeteerExtra, - params: DriverParameters, - ) { - assert(params.fakeDeviceDesc) - - const Plugin = require(path.resolve(__dirname, '../plugins/evasions/window.speechSynthesis')) - const plugin = Plugin({ - voices: params.fakeDeviceDesc.voices, - }) - - pptr.use(plugin) - } - - // @ts-ignore - private static patchWorkers( - uuid: string, - pptr: PuppeteerExtra, - params: DriverParameters, - ) { - const Plugin = require(path.resolve(__dirname, '../plugins/evasions/workers')) - const plugin = Plugin({ - env: { - uuid: uuid, - internalHttpServerPort: FakeBrowser.globalConfig.internalHttpServerPort, - }, - }) - - pptr.use(plugin) - } - - // @ts-ignore - private static patchKeyboard( - uuid: string, - pptr: PuppeteerExtra, - params: DriverParameters, - ) { - assert(params.fakeDeviceDesc) - - const Plugin = require(path.resolve(__dirname, '../plugins/evasions/keyboard')) - const plugin = Plugin({ - keyboard: params.fakeDeviceDesc.keyboard, - }) - - pptr.use(plugin) - } - - // @ts-ignore - private static patchUserAgent( - uuid: string, - pptr: PuppeteerExtra, - params: DriverParameters, - ) { - assert(params.fakeDeviceDesc) - - const Plugin = require(path.resolve(__dirname, '../plugins/evasions/user-agent-override')) - const plugin = Plugin({ - userAgent: params.fakeDeviceDesc.navigator.userAgent, - // Network.setUserAgentOverride acceptLanguage with qvalue - // https://github.com/berstend/puppeteer-extra/issues/132 - locale: DeviceDescriptorHelper.buildAcceptLanguage(params.fakeDeviceDesc), - maskLinux: true, - }) - - pptr.use(plugin) - } - - // @ts-ignore - private static patchIFrame( - uuid: string, - pptr: PuppeteerExtra, - params: DriverParameters, - ) { - let Plugin = require(path.resolve(__dirname, '../plugins/evasions/iframe.contentWindow')) - let plugin = Plugin() - pptr.use(plugin) - - Plugin = require(path.resolve(__dirname, '../plugins/evasions/iframe.src')) - plugin = Plugin() - pptr.use(plugin) - } - - // @ts-ignore - private static patchPropertiesGetters( - uuid: string, - pptr: PuppeteerExtra, - params: DriverParameters, - ) { - assert(params.fakeDeviceDesc) - - let Plugin = require(path.resolve(__dirname, '../plugins/evasions/properties.getter')) - let plugin = Plugin({ - navigator: params.fakeDeviceDesc.navigator, - window: params.fakeDeviceDesc.window, - document: params.fakeDeviceDesc.document, - screen: params.fakeDeviceDesc.screen, - }) - - pptr.use(plugin) - } - - // @ts-ignore private static patchLast( uuid: string, pptr: PuppeteerExtra, params: DriverParameters, + opts: PptrExtraEvasionOpts, ) { let Plugin = require(path.resolve(__dirname, '../plugins/evasions/zzzzzzzz.last')) - let plugin = Plugin() + let plugin = Plugin(opts) pptr.use(plugin) } diff --git a/src/plugins/evasions/_utils/index.js b/src/plugins/evasions/_utils/index.js index f44bd2c..c435ad3 100644 --- a/src/plugins/evasions/_utils/index.js +++ b/src/plugins/evasions/_utils/index.js @@ -1,4 +1,4 @@ -// noinspection JSUnusedLocalSymbols,JSUnusedGlobalSymbols,JSUnresolvedVariable +// noinspection JSUnusedLocalSymbols,JSUnusedGlobalSymbols,JSUnresolvedVariable,JSNonASCIINames,NonAsciiCharacters /** * A set of shared utility functions specifically for the purpose of modifying native browser APIs without leaving traces. @@ -145,6 +145,7 @@ utils._preloadGlobalVariables = () => { toStringPatchObjs: [], toStringRedirectObjs: [], renderingContextWithOperators: [], + taskData: {}, }; }; diff --git a/src/plugins/evasions/audio.fingerprint/index.js b/src/plugins/evasions/audio.fingerprint/index.js index b97cceb..04917e8 100644 --- a/src/plugins/evasions/audio.fingerprint/index.js +++ b/src/plugins/evasions/audio.fingerprint/index.js @@ -16,17 +16,11 @@ class Plugin extends PuppeteerExtraPlugin { } async onPageCreated(page) { - await withUtils(this, page).evaluateOnNewDocument( - this.mainFunction, - this.opts, - ); + await withUtils(this, page).evaluateOnNewDocument(this.mainFunction); } onServiceWorkerContent(jsContent) { - return withWorkerUtils(this, jsContent).evaluate( - this.mainFunction, - this.opts, - ); + return withWorkerUtils(this, jsContent).evaluate(this.mainFunction); } mainFunction = (utils, opts) => { diff --git a/src/plugins/evasions/barcodeDetector/index.js b/src/plugins/evasions/barcodeDetector/index.js index 7bfe7bd..02ad54f 100644 --- a/src/plugins/evasions/barcodeDetector/index.js +++ b/src/plugins/evasions/barcodeDetector/index.js @@ -18,17 +18,11 @@ class Plugin extends PuppeteerExtraPlugin { } async onPageCreated(page) { - await withUtils(this, page).evaluateOnNewDocument( - this.mainFunction, - this.opts, - ); + await withUtils(this, page).evaluateOnNewDocument(this.mainFunction); } onServiceWorkerContent(jsContent) { - return withWorkerUtils(this, jsContent).evaluate( - this.mainFunction, - this.opts, - ); + return withWorkerUtils(this, jsContent).evaluate(this.mainFunction); } mainFunction = (utils, opts) => { diff --git a/src/plugins/evasions/bluetooth/index.js b/src/plugins/evasions/bluetooth/index.js index 094dfd8..b695490 100644 --- a/src/plugins/evasions/bluetooth/index.js +++ b/src/plugins/evasions/bluetooth/index.js @@ -38,20 +38,14 @@ class Plugin extends PuppeteerExtraPlugin { } async onPageCreated(page) { - await withUtils(this, page).evaluateOnNewDocument( - this.mainFunction, - this.opts, - ); + await withUtils(this, page).evaluateOnNewDocument(this.mainFunction); } // onServiceWorkerContent(jsContent) { - // return withWorkerUtils(this, jsContent).evaluate( - // this.mainFunction, - // this.opts, - // ); + // return withWorkerUtils(this, jsContent).evaluate(this.mainFunction); // } - mainFunction = (utils, opts) => { + mainFunction = (utils) => { if ('undefined' !== typeof window.Bluetooth) { return; } diff --git a/src/plugins/evasions/canvas.fingerprint/index.js b/src/plugins/evasions/canvas.fingerprint/index.js index 67001d7..fc6eda2 100644 --- a/src/plugins/evasions/canvas.fingerprint/index.js +++ b/src/plugins/evasions/canvas.fingerprint/index.js @@ -66,7 +66,7 @@ class Plugin extends PuppeteerExtraPlugin { return 'evasions/canvas.fingerprint'; } - mainFunction = (utils, opts) => { + mainFunction = (utils, canvasSalt) => { const _Object = utils.cache.Object; const _Reflect = utils.cache.Reflect; @@ -306,11 +306,11 @@ class Plugin extends PuppeteerExtraPlugin { // The surrounding 4 pixels are not the same color before adding the noise. if (p00 !== p01 || p00 !== p10 || p00 !== p11) { - const salt = opts.canvasSalt[saltIndex]; + const salt = canvasSalt[saltIndex]; imageUint8Data[pos] += salt; ++saltIndex; - if (saltIndex >= opts.canvasSalt.length) { + if (saltIndex >= canvasSalt.length) { saltIndex = 0; } } @@ -396,11 +396,11 @@ class Plugin extends PuppeteerExtraPlugin { }; async onPageCreated(page) { - await withUtils(this, page).evaluateOnNewDocument(this.mainFunction, this.opts); + await withUtils(this, page).evaluateOnNewDocument(this.mainFunction, this.opts.fakeDD.canvasSalt); } onServiceWorkerContent(jsContent) { - return withWorkerUtils(this, jsContent).evaluate(this.mainFunction, this.opts); + return withWorkerUtils(this, jsContent).evaluate(this.mainFunction, this.opts.fakeDD.canvasSalt); } } diff --git a/src/plugins/evasions/chrome.loadTimes/index.js b/src/plugins/evasions/chrome.loadTimes/index.js index 8f88aed..87ac00a 100644 --- a/src/plugins/evasions/chrome.loadTimes/index.js +++ b/src/plugins/evasions/chrome.loadTimes/index.js @@ -1,3 +1,5 @@ +// noinspection JSUnusedLocalSymbols + 'use strict'; const {PuppeteerExtraPlugin} = require('puppeteer-extra-plugin'); @@ -30,10 +32,10 @@ class Plugin extends PuppeteerExtraPlugin { } async onPageCreated(page) { - await withUtils(this, page).evaluateOnNewDocument(this.mainFunction, {opts: this.opts}); + await withUtils(this, page).evaluateOnNewDocument(this.mainFunction); } - mainFunction = (utils, {opts}) => { + mainFunction = (utils) => { if (!window.chrome) { // Use the exact property descriptor found in headful Chrome // fetch it via `Object.getOwnPropertyDescriptor(window, 'chrome')` diff --git a/src/plugins/evasions/emoji.fingerprint/index.js b/src/plugins/evasions/emoji.fingerprint/index.js index 3f82f87..fbe0cfc 100644 --- a/src/plugins/evasions/emoji.fingerprint/index.js +++ b/src/plugins/evasions/emoji.fingerprint/index.js @@ -21,21 +21,17 @@ class Plugin extends PuppeteerExtraPlugin { } async onPageCreated(page) { - await withUtils(this, page).evaluateOnNewDocument( - this.mainFunction, - this.opts, - ); + await withUtils(this, page).evaluateOnNewDocument(this.mainFunction); } onServiceWorkerContent(jsContent) { return withWorkerUtils(this, jsContent).evaluate( this.mainFunction, - this.opts, ); } - mainFunction = (utils, opts) => { - const _Reflect = utils.cache.Reflect + mainFunction = (utils) => { + const _Reflect = utils.cache.Reflect; utils.replaceWithProxy(String, 'fromCodePoint', { apply(target, thisArg, args) { diff --git a/src/plugins/evasions/font.fingerprint/index.js b/src/plugins/evasions/font.fingerprint/index.js index 7e6235e..aab0a71 100644 --- a/src/plugins/evasions/font.fingerprint/index.js +++ b/src/plugins/evasions/font.fingerprint/index.js @@ -21,20 +21,14 @@ class Plugin extends PuppeteerExtraPlugin { } async onPageCreated(page) { - await withUtils(this, page).evaluateOnNewDocument( - this.mainFunction, - this.opts, - ); + await withUtils(this, page).evaluateOnNewDocument(this.mainFunction, this.opts.fakeDD.fontSalt); } onServiceWorkerContent(jsContent) { - return withWorkerUtils(this, jsContent).evaluate( - this.mainFunction, - this.opts, - ); + return withWorkerUtils(this, jsContent).evaluate(this.mainFunction, this.opts.fakeDD.fontSalt); } - mainFunction = (utils, opts) => { + mainFunction = (utils, fontSalt) => { // Thanks to: https://github.com/dy/css-font function parseFont(value) { if (typeof value !== 'string') throw new Error('Font argument must be a string.'); @@ -454,10 +448,10 @@ class Plugin extends PuppeteerExtraPlugin { // }; // Get out the font that exists - const allFonts = _Object.keys(opts.fontSalt) + const allFonts = _Object.keys(fontSalt) .map(e => e.toLowerCase()); - const existFonts = _Object.entries(opts.fontSalt) + const existFonts = _Object.entries(fontSalt) .filter(e => e[1].exists) .map(e => e[0].toLowerCase()); @@ -470,7 +464,7 @@ class Plugin extends PuppeteerExtraPlugin { // All lowercase const fontSaltWithLowerCaseName = _Object.fromEntries( - _Object.entries(opts.fontSalt).map(e => { + _Object.entries(fontSalt).map(e => { e[0] = e[0].toLowerCase(); return e; })); @@ -823,7 +817,7 @@ class Plugin extends PuppeteerExtraPlugin { // Return the new proxy const proxy = utils.newProxyInstance( styleDeclaration, - utils.stripProxyFromErrors(handler) + utils.stripProxyFromErrors(handler), ); utils.redirectToString(proxy, styleDeclaration); diff --git a/src/plugins/evasions/iframe.contentWindow/index.js b/src/plugins/evasions/iframe.contentWindow/index.js index d32b455..3b7810d 100644 --- a/src/plugins/evasions/iframe.contentWindow/index.js +++ b/src/plugins/evasions/iframe.contentWindow/index.js @@ -1,9 +1,10 @@ +// noinspection JSUnusedLocalSymbols + 'use strict'; const {PuppeteerExtraPlugin} = require('puppeteer-extra-plugin'); const withUtils = require('../_utils/withUtils'); - const withWorkerUtils = require('../_utils/withWorkerUtils'); /** @@ -27,12 +28,10 @@ class Plugin extends PuppeteerExtraPlugin { } async onPageCreated(page) { - await withUtils(this, page).evaluateOnNewDocument(this.mainFunction, { - opts: this.opts, - }); + await withUtils(this, page).evaluateOnNewDocument(this.mainFunction); } - mainFunction = (utils, {opts}) => { + mainFunction = (utils) => { const _Object = utils.cache.Object; const _Reflect = utils.cache.Reflect; diff --git a/src/plugins/evasions/iframe.src/index.js b/src/plugins/evasions/iframe.src/index.js index 024d64e..baaba6a 100644 --- a/src/plugins/evasions/iframe.src/index.js +++ b/src/plugins/evasions/iframe.src/index.js @@ -1,3 +1,5 @@ +// noinspection JSUnusedLocalSymbols + 'use strict'; const {PuppeteerExtraPlugin} = require('puppeteer-extra-plugin'); diff --git a/src/plugins/evasions/keyboard/index.js b/src/plugins/evasions/keyboard/index.js index 84d9b59..0776c72 100644 --- a/src/plugins/evasions/keyboard/index.js +++ b/src/plugins/evasions/keyboard/index.js @@ -16,30 +16,24 @@ class Plugin extends PuppeteerExtraPlugin { } async onPageCreated(page) { - await withUtils(this, page).evaluateOnNewDocument( - this.mainFunction, - this.opts, - ); + await withUtils(this, page).evaluateOnNewDocument(this.mainFunction, this.opts.fakeDD.keyboard); } onServiceWorkerContent(jsContent) { - return withWorkerUtils(this, jsContent).evaluate( - this.mainFunction, - this.opts, - ); + return withWorkerUtils(this, jsContent).evaluate(this.mainFunction, this.opts.fakeDD.keyboard); } - mainFunction = (utils, opts) => { + mainFunction = (utils, fakeKeyboard) => { const _Reflect = utils.cache.Reflect; if ( - opts.keyboard + fakeKeyboard && 'undefined' !== typeof KeyboardLayoutMap ) { utils.replaceWithProxy(KeyboardLayoutMap.prototype, 'get', { apply(target, thisArg, args) { if (args && args.length) { - return opts.keyboard[args[0]]; + return fakeKeyboard[args[0]]; } return _Reflect.apply(target, thisArg, args); diff --git a/src/plugins/evasions/mimeTypes/index.js b/src/plugins/evasions/mimeTypes/index.js index 4d22c7d..daecebc 100644 --- a/src/plugins/evasions/mimeTypes/index.js +++ b/src/plugins/evasions/mimeTypes/index.js @@ -1,3 +1,5 @@ +// noinspection JSUnusedLocalSymbols + 'use strict'; const {PuppeteerExtraPlugin} = require('puppeteer-extra-plugin'); @@ -15,10 +17,11 @@ class Plugin extends PuppeteerExtraPlugin { } async onPageCreated(page) { - await withUtils(this, page).evaluateOnNewDocument(this.mainFunction, this.opts); + await withUtils(this, page).evaluateOnNewDocument(this.mainFunction, this.opts.fakeDD.mimeTypes); } - mainFunction = (utils, opts) => { + mainFunction = (utils, fakeMimeTypes) => { + const _Object = utils.cache.Object; const _Reflect = utils.cache.Reflect; utils.replaceWithProxy( @@ -48,7 +51,7 @@ class Plugin extends PuppeteerExtraPlugin { return orgResult; } - const mimeType = opts.data.find(e => e.mimeType === trimmedType); + const mimeType = fakeMimeTypes.find(e => e.mimeType === trimmedType); if (mimeType) { if (thisArg instanceof HTMLVideoElement) { return mimeType.videoPlayType; @@ -79,7 +82,7 @@ class Plugin extends PuppeteerExtraPlugin { trimmedType = trimmedType.substr(0, trimmedType.length - 1); } - const mimeType = opts.data.find(e => e.mimeType === trimmedType); + const mimeType = fakeMimeTypes.find(e => e.mimeType === trimmedType); if (mimeType) { return mimeType.mediaSource; } else { @@ -107,7 +110,7 @@ class Plugin extends PuppeteerExtraPlugin { trimmedType = trimmedType.substr(0, trimmedType.length - 1); } - const mimeType = opts.data.find(e => e.mimeType === trimmedType); + const mimeType = fakeMimeTypes.find(e => e.mimeType === trimmedType); if (mimeType) { return mimeType.mediaRecorder; } else { diff --git a/src/plugins/evasions/navigator.batteryManager/index.js b/src/plugins/evasions/navigator.batteryManager/index.js index 78e2ff2..452f931 100644 --- a/src/plugins/evasions/navigator.batteryManager/index.js +++ b/src/plugins/evasions/navigator.batteryManager/index.js @@ -14,39 +14,39 @@ class Plugin extends PuppeteerExtraPlugin { } async onPageCreated(page) { - await withUtils(this, page).evaluateOnNewDocument(this.mainFunction, this.opts.battery); + await withUtils(this, page).evaluateOnNewDocument(this.mainFunction, this.opts.fakeDD.battery); } onServiceWorkerContent(jsContent) { - return withWorkerUtils(this, jsContent).evaluate(this.mainFunction, this.opts.battery); + return withWorkerUtils(this, jsContent).evaluate(this.mainFunction, this.opts.fakeDD.battery); } - mainFunction = (utils, battery) => { + mainFunction = (utils, fakeBattery) => { // TODO: If it is a charging state, the user's power should keep increasing to a certain time full. // It also needs to simulate the situation that the user has unplugged the power. if ('undefined' != typeof BatteryManager) { utils.replaceGetterWithProxy( BatteryManager.prototype, 'charging', - utils.makeHandler().getterValue(battery.charging), + utils.makeHandler().getterValue(fakeBattery.charging), ); utils.replaceGetterWithProxy( BatteryManager.prototype, 'chargingTime', - utils.makeHandler().getterValue(!battery.chargingTime ? Infinity : battery.chargingTime), + utils.makeHandler().getterValue(!fakeBattery.chargingTime ? Infinity : fakeBattery.chargingTime), ); utils.replaceGetterWithProxy( BatteryManager.prototype, 'dischargingTime', - utils.makeHandler().getterValue(!battery.dischargingTime ? Infinity : battery.dischargingTime), + utils.makeHandler().getterValue(!fakeBattery.dischargingTime ? Infinity : fakeBattery.dischargingTime), ); utils.replaceGetterWithProxy( BatteryManager.prototype, 'level', - utils.makeHandler().getterValue(battery.level), + utils.makeHandler().getterValue(fakeBattery.level), ); } }; diff --git a/src/plugins/evasions/navigator.mediaDevices/index.js b/src/plugins/evasions/navigator.mediaDevices/index.js index fee28da..b79bdcc 100644 --- a/src/plugins/evasions/navigator.mediaDevices/index.js +++ b/src/plugins/evasions/navigator.mediaDevices/index.js @@ -16,22 +16,14 @@ class Plugin extends PuppeteerExtraPlugin { } async onPageCreated(page) { - await withUtils(this, page).evaluateOnNewDocument( - this.mainFunction, - { - mediaDevices: this.opts.data, - }, - ); + await withUtils(this, page).evaluateOnNewDocument(this.mainFunction, this.opts.fakeDD.mediaDevices); } onServiceWorkerContent(jsContent) { - return withWorkerUtils(this, jsContent).evaluate(this.mainFunction, - { - mediaDevices: this.opts.data, - }); + return withWorkerUtils(this, jsContent).evaluate(this.mainFunction, this.opts.fakeDD.mediaDevices); } - mainFunction = (utils, {mediaDevices}) => { + mainFunction = (utils, fakeMediaDevices) => { const _Object = utils.cache.Object; const _Reflect = utils.cache.Reflect; @@ -42,7 +34,7 @@ class Plugin extends PuppeteerExtraPlugin { const index = 4 + Math.floor(Math.random() * 32); const tempMediaDeviceObjs = []; - for (let mediaDevice of mediaDevices) { + for (let mediaDevice of fakeMediaDevices) { const json = JSON.stringify(mediaDevice); mediaDevice.groupId = mediaDevice.groupId.substr(0, index) + to + mediaDevice.groupId.substr(index + 1); @@ -126,7 +118,6 @@ class Plugin extends PuppeteerExtraPlugin { }); } }; - } module.exports = function (pluginConfig) { diff --git a/src/plugins/evasions/navigator.permissions/index.js b/src/plugins/evasions/navigator.permissions/index.js index 2bd79e2..e583e9c 100644 --- a/src/plugins/evasions/navigator.permissions/index.js +++ b/src/plugins/evasions/navigator.permissions/index.js @@ -30,29 +30,29 @@ class Plugin extends PuppeteerExtraPlugin { // "msg"?: string, // }> - await withUtils(this, page).evaluateOnNewDocument(this.mainFunction, this.opts); - - // invoke CDP setPermission - const permissions = this.opts.permissions; - for (const name in permissions) { - const permission = permissions[name]; - if (permission.state) { - try { - await page['_client'].send('Browser.setPermission', { - permission: {name}, - setting: permission.state, - }); - } catch (ignore) { - } - } - } + await withUtils(this, page).evaluateOnNewDocument(this.mainFunction, this.opts.fakeDD.permissions); + + // // invoke CDP setPermission + // const permissions = this.opts.permissions; + // for (const name in permissions) { + // const permission = permissions[name]; + // if (permission.state) { + // try { + // await page['_client'].send('Browser.setPermission', { + // permission: {name}, + // setting: permission.state, + // }); + // } catch (ignore) { + // } + // } + // } } onServiceWorkerContent(jsContent) { - return withWorkerUtils(this, jsContent).evaluate(this.mainFunction, this.opts); + return withWorkerUtils(this, jsContent).evaluate(this.mainFunction, this.opts.fakeDD.permissions); } - mainFunction = (utils, opts) => { + mainFunction = (utils, fakePermissions) => { const _Object = utils.cache.Object; const _Reflect = utils.cache.Reflect; @@ -72,8 +72,7 @@ class Plugin extends PuppeteerExtraPlugin { const paramName = param && param.name; return new utils.cache.Promise((resolve, reject) => { - const permissions = opts.permissions; - const permission = permissions[paramName]; + const permission = fakePermissions[paramName]; if (permission) { let exType = permission.exType; diff --git a/src/plugins/evasions/navigator.plugins-native/index.js b/src/plugins/evasions/navigator.plugins-native/index.js index dea7cef..95458d2 100644 --- a/src/plugins/evasions/navigator.plugins-native/index.js +++ b/src/plugins/evasions/navigator.plugins-native/index.js @@ -7,111 +7,6 @@ const {PuppeteerExtraPlugin} = require('puppeteer-extra-plugin'); const utils = require('../_utils'); const withUtils = require('../_utils/withUtils'); -const kPluginsLessThen93 = { - 'mimeTypes': [ - { - 'type': 'application/pdf', - 'suffixes': 'pdf', - 'description': '', - '__pluginName': 'Chrome PDF Viewer', - }, - { - 'type': 'application/x-google-chrome-pdf', - 'suffixes': 'pdf', - 'description': 'Portable Document Format', - '__pluginName': 'Chrome PDF Plugin', - }, - { - 'type': 'application/x-nacl', - 'suffixes': '', - 'description': 'Native Client Executable', - '__pluginName': 'Native Client', - }, - { - 'type': 'application/x-pnacl', - 'suffixes': '', - 'description': 'Portable Native Client Executable', - '__pluginName': 'Native Client', - }, - ], - 'plugins': [ - { - 'name': 'Chrome PDF Plugin', - 'filename': 'internal-pdf-viewer', - 'description': 'Portable Document Format', - '__mimeTypes': [ - 'application/x-google-chrome-pdf', - ], - }, - { - 'name': 'Chrome PDF Viewer', - 'filename': 'mhjfbmdgcfjbbpaeojofohoefgiehjai', - 'description': '', - '__mimeTypes': [ - 'application/pdf', - ], - }, - { - 'name': 'Native Client', - 'filename': 'internal-nacl-plugin', - 'description': '', - '__mimeTypes': [ - 'application/x-nacl', - 'application/x-pnacl', - ], - }, - ], -}; - -const kPluginsGreaterThen93 = { - mimeTypes: [ - { - type: 'application/pdf', - suffixes: 'pdf', - description: 'Portable Document Format', - __pluginName: 'PDF Viewer', - }, - { - type: 'text/pdf', - suffixes: 'pdf', - description: 'Portable Document Format', - __pluginName: 'PDF Viewer', - }, - ], - plugins: [ - { - name: 'PDF Viewer', - filename: 'internal-pdf-viewer', - description: 'Portable Document Format', - __mimeTypes: ['application/pdf', 'text/pdf'], - }, - { - name: 'Chrome PDF Viewer', - filename: 'internal-pdf-viewer', - description: 'Portable Document Format', - __mimeTypes: ['application/pdf', 'text/pdf'], - }, - { - name: 'Chromium PDF Viewer', - filename: 'internal-pdf-viewer', - description: 'Portable Document Format', - __mimeTypes: ['application/pdf', 'text/pdf'], - }, - { - name: 'Microsoft Edge PDF Viewer', - filename: 'internal-pdf-viewer', - description: 'Portable Document Format', - __mimeTypes: ['application/pdf', 'text/pdf'], - }, - { - name: 'WebKit built-in PDF', - filename: 'internal-pdf-viewer', - description: 'Portable Document Format', - __mimeTypes: ['application/pdf', 'text/pdf'], - }, - ], -}; - /** * @see https://developer.mozilla.org/en-US/docs/Web/API/NavigatorPlugins/mimeTypes * @see https://developer.mozilla.org/en-US/docs/Web/API/MimeTypeArray @@ -119,8 +14,7 @@ const kPluginsGreaterThen93 = { * @see https://developer.mozilla.org/en-US/docs/Web/API/PluginArray */ class Plugin extends PuppeteerExtraPlugin { - - constructor(opts = {plugins: kPluginsLessThen93}) { + constructor(opts = {}) { super(opts); } @@ -141,19 +35,130 @@ class Plugin extends PuppeteerExtraPlugin { const orgUA = await browser.userAgent(); // https://www.chromestatus.com/feature/5741884322349056#details - if (chromeMajorVersion(orgUA) > 93) { - this.opts.plugins = kPluginsGreaterThen93; - } + this.opts.chromeMajorVersion = chromeMajorVersion(orgUA); } async onPageCreated(page) { await withUtils(this, page).evaluateOnNewDocument( this.mainFunction, - this.opts.plugins, + { + chromeMajorVersion: this.opts.chromeMajorVersion, + fakePlugins: this.opts.fakeDD.plugins, + }, ); } - mainFunction = (utils, pluginsData) => { + mainFunction = (utils, {chromeMajorVersion, fakePlugins}) => { + const kPluginsLessThen93 = { + 'mimeTypes': [ + { + 'type': 'application/pdf', + 'suffixes': 'pdf', + 'description': '', + '__pluginName': 'Chrome PDF Viewer', + }, + { + 'type': 'application/x-google-chrome-pdf', + 'suffixes': 'pdf', + 'description': 'Portable Document Format', + '__pluginName': 'Chrome PDF Plugin', + }, + { + 'type': 'application/x-nacl', + 'suffixes': '', + 'description': 'Native Client Executable', + '__pluginName': 'Native Client', + }, + { + 'type': 'application/x-pnacl', + 'suffixes': '', + 'description': 'Portable Native Client Executable', + '__pluginName': 'Native Client', + }, + ], + 'plugins': [ + { + 'name': 'Chrome PDF Plugin', + 'filename': 'internal-pdf-viewer', + 'description': 'Portable Document Format', + '__mimeTypes': [ + 'application/x-google-chrome-pdf', + ], + }, + { + 'name': 'Chrome PDF Viewer', + 'filename': 'mhjfbmdgcfjbbpaeojofohoefgiehjai', + 'description': '', + '__mimeTypes': [ + 'application/pdf', + ], + }, + { + 'name': 'Native Client', + 'filename': 'internal-nacl-plugin', + 'description': '', + '__mimeTypes': [ + 'application/x-nacl', + 'application/x-pnacl', + ], + }, + ], + }; + + const kPluginsGreaterThen93 = { + mimeTypes: [ + { + type: 'application/pdf', + suffixes: 'pdf', + description: 'Portable Document Format', + __pluginName: 'PDF Viewer', + }, + { + type: 'text/pdf', + suffixes: 'pdf', + description: 'Portable Document Format', + __pluginName: 'PDF Viewer', + }, + ], + plugins: [ + { + name: 'PDF Viewer', + filename: 'internal-pdf-viewer', + description: 'Portable Document Format', + __mimeTypes: ['application/pdf', 'text/pdf'], + }, + { + name: 'Chrome PDF Viewer', + filename: 'internal-pdf-viewer', + description: 'Portable Document Format', + __mimeTypes: ['application/pdf', 'text/pdf'], + }, + { + name: 'Chromium PDF Viewer', + filename: 'internal-pdf-viewer', + description: 'Portable Document Format', + __mimeTypes: ['application/pdf', 'text/pdf'], + }, + { + name: 'Microsoft Edge PDF Viewer', + filename: 'internal-pdf-viewer', + description: 'Portable Document Format', + __mimeTypes: ['application/pdf', 'text/pdf'], + }, + { + name: 'WebKit built-in PDF', + filename: 'internal-pdf-viewer', + description: 'Portable Document Format', + __mimeTypes: ['application/pdf', 'text/pdf'], + }, + ], + }; + + const pluginsData = + chromeMajorVersion > 93 + ? kPluginsGreaterThen93 + : fakePlugins; + const _Object = utils.cache.Object; const _Reflect = utils.cache.Reflect; const _origPlugins = utils.cache.window.navigator.plugins; diff --git a/src/plugins/evasions/properties.getter/index.js b/src/plugins/evasions/properties.getter/index.js index a452864..dfed639 100644 --- a/src/plugins/evasions/properties.getter/index.js +++ b/src/plugins/evasions/properties.getter/index.js @@ -17,44 +17,61 @@ class Plugin extends PuppeteerExtraPlugin { } async onBrowser(browser, opts) { - if ( - this.opts.navigator - && this.opts.navigator.userAgent - ) { - const orgUA = await browser.userAgent(); - - function chromeVersion(userAgent) { - const chromeVersionPart = userAgent.match(/Chrome\/(.*?) /); - if (chromeVersionPart) { - return chromeVersionPart[1]; - } - - return null; - } - - const orgVersion = chromeVersion(orgUA); - const fakeVersion = chromeVersion(this.opts.navigator.userAgent); - - this.opts.navigator.userAgent = this.opts.navigator.userAgent.replace(fakeVersion, orgVersion); - - if (this.opts.navigator.appVersion) { - this.opts.navigator.appVersion = this.opts.navigator.appVersion.replace(fakeVersion, orgVersion); - } - } + this.opts.realUA = await browser.userAgent(); } async onPageCreated(page) { - await withUtils(this, page).evaluateOnNewDocument(this.mainFunction, this.opts); + await withUtils(this, page).evaluateOnNewDocument(this.mainFunction, { + realUA: this.opts.realUA, + fakeNavigator: this.opts.fakeDD.navigator, + fakeWindow: this.opts.fakeDD.window, + fakeDocument: this.opts.fakeDD.document, + fakeScreen: this.opts.fakeDD.screen, + fakeBody: this.opts.fakeDD.body, + }); } onServiceWorkerContent(jsContent) { - return withWorkerUtils(this, jsContent).evaluate(this.mainFunction, this.opts); + return withWorkerUtils(this, jsContent).evaluate(this.mainFunction, { + realUA: this.opts.realUA, + fakeNavigator: this.opts.fakeDD.navigator, + fakeWindow: this.opts.fakeDD.window, + fakeDocument: this.opts.fakeDD.document, + fakeScreen: this.opts.fakeDD.screen, + fakeBody: this.opts.fakeDD.body, + }); } - mainFunction = (utils, opts) => { + mainFunction = (utils, { + realUA, + fakeNavigator, + fakeWindow, + fakeDocument, + fakeScreen, + fakeBody, + }) => { const _Object = utils.cache.Object; const _Reflect = utils.cache.Reflect; + // replace Chrome version of fakeDDs' userAgent and appVersion with real version + function chromeVersion(userAgent) { + const chromeVersionPart = userAgent.match(/Chrome\/(.*?) /); + if (chromeVersionPart) { + return chromeVersionPart[1]; + } + + return null; + } + + const realVersion = chromeVersion(realUA); + const fakeVersion = chromeVersion(fakeNavigator.userAgent); + + fakeNavigator.userAgent = fakeNavigator.userAgent.replace(fakeVersion, realVersion); + + if (fakeNavigator.appVersion) { + fakeNavigator.appVersion = fakeNavigator.appVersion.replace(fakeVersion, realVersion); + } + /* Define variables */ const kObjPlaceHolder = '_$obj!_//+_'; const kObjUndefinedPlaceHolder = '_$obj!_undefined_//+_'; @@ -123,27 +140,27 @@ class Plugin extends PuppeteerExtraPlugin { }; if ('undefined' !== typeof WorkerNavigator) { - overwriteObjectProperties(WorkerNavigator.prototype, opts.navigator); + overwriteObjectProperties(WorkerNavigator.prototype, fakeNavigator); } if ('undefined' !== typeof Navigator) { - overwriteObjectProperties(Navigator.prototype, opts.navigator); + overwriteObjectProperties(Navigator.prototype, fakeNavigator); } if ('undefined' !== typeof window) { - overwriteObjectProperties(window, opts.window, ['pageXOffset', 'pageYOffset']); + overwriteObjectProperties(window, fakeWindow, ['pageXOffset', 'pageYOffset']); } if ('undefined' !== typeof Document) { - overwriteObjectProperties(Document.prototype, opts.document); + overwriteObjectProperties(Document.prototype, fakeDocument); } if ('undefined' !== typeof HTMLBodyElement) { - overwriteObjectProperties(HTMLBodyElement.prototype, opts.body, ['clientWidth', 'clientHeight']); + overwriteObjectProperties(HTMLBodyElement.prototype, fakeBody, ['clientWidth', 'clientHeight']); } if ('undefined' !== typeof Screen) { - overwriteObjectProperties(Screen.prototype, opts.screen); + overwriteObjectProperties(Screen.prototype, fakeScreen); } }; diff --git a/src/plugins/evasions/sourceurl/index.js b/src/plugins/evasions/sourceurl/index.js index 9669918..5b2c432 100644 --- a/src/plugins/evasions/sourceurl/index.js +++ b/src/plugins/evasions/sourceurl/index.js @@ -50,6 +50,7 @@ class Plugin extends PuppeteerExtraPlugin { 'Runtime.evaluate': 'expression', 'Runtime.callFunctionOn': 'functionDeclaration', }; + const SOURCE_URL_SUFFIX = '//# sourceURL=__puppeteer_evaluation_script__'; diff --git a/src/plugins/evasions/user-agent-override/index.js b/src/plugins/evasions/user-agent-override/index.js index eb1856b..470c36e 100644 --- a/src/plugins/evasions/user-agent-override/index.js +++ b/src/plugins/evasions/user-agent-override/index.js @@ -9,15 +9,13 @@ const withWorkerUtils = require('../_utils/withWorkerUtils'); class Plugin extends PuppeteerExtraPlugin { - _override = null; - constructor(opts = {}) { super(opts); this._headless = false; } async onBrowser(browser, opts) { - this._override = await this.getOverride(browser); + this.opts.override = await this.getOverride(browser); } getOverride = async (browser) => { @@ -182,7 +180,7 @@ class Plugin extends PuppeteerExtraPlugin { } async onPageCreated(page) { - const override = this._override; + const override = this.opts.override; await withUtils(this, page).evaluateOnNewDocument(this.mainFunction, override); try { @@ -193,7 +191,7 @@ class Plugin extends PuppeteerExtraPlugin { } onServiceWorkerContent(jsContent) { - const override = this._override; + const override = this.opts.override; if (override) { return withWorkerUtils(this, jsContent).evaluate(this.mainFunction, override); diff --git a/src/plugins/evasions/webgl/index.js b/src/plugins/evasions/webgl/index.js index 6bdfecc..f0c0319 100644 --- a/src/plugins/evasions/webgl/index.js +++ b/src/plugins/evasions/webgl/index.js @@ -1,3 +1,5 @@ +// noinspection JSUnusedLocalSymbols + 'use strict'; const {PuppeteerExtraPlugin} = require('puppeteer-extra-plugin'); @@ -16,14 +18,22 @@ class Plugin extends PuppeteerExtraPlugin { /* global WebGLRenderingContext WebGL2RenderingContext */ async onPageCreated(page) { - await withUtils(this, page).evaluateOnNewDocument(this.mainFunction, this.opts); + await withUtils(this, page).evaluateOnNewDocument(this.mainFunction, { + gpu: this.opts.fakeDD.gpu, + webgl: this.opts.fakeDD.webgl, + webgl2: this.opts.fakeDD.webgl2, + }); } onServiceWorkerContent(jsContent) { - return withWorkerUtils(this, jsContent).evaluate(this.mainFunction, this.opts); + return withWorkerUtils(this, jsContent).evaluate(this.mainFunction, { + gpu: this.opts.fakeDD.gpu, + webgl: this.opts.fakeDD.webgl, + webgl2: this.opts.fakeDD.webgl2, + }); } - mainFunction = (utils, data) => { + mainFunction = (utils, fakeDD) => { const _Object = utils.cache.Object; const _Reflect = utils.cache.Reflect; @@ -40,7 +50,7 @@ class Plugin extends PuppeteerExtraPlugin { const WebGLShaderPrecisionFormat_prototype_rangeMax_get = utils.cache.Descriptor.WebGLShaderPrecisionFormat.prototype.rangeMax.get; const WebGLShaderPrecisionFormat_prototype_precision_get = utils.cache.Descriptor.WebGLShaderPrecisionFormat.prototype.precision.get; - const bindContext = (_WebGLRenderingContext, propName) => { + const bindContext = (_WebGLRenderingContext, fakeDDPropName) => { // getParameter utils.replaceWithProxy(_WebGLRenderingContext.prototype, 'getParameter', { apply(target, thisArg, args) { @@ -51,15 +61,15 @@ class Plugin extends PuppeteerExtraPlugin { switch (type) { case 37445: /* renderer.UNMASKED_VENDOR_WEBGL */ - result = data.gpu.vendor; + result = fakeDD.gpu.vendor; break; case 37446: /* renderer.UNMASKED_RENDERER_WEBGL */ - result = data.gpu.renderer; + result = fakeDD.gpu.renderer; break; default: - const param = data[propName].params[type]; + const param = fakeDD[fakeDDPropName].params[type]; if (param) { const paramValue = param.value; @@ -88,7 +98,7 @@ class Plugin extends PuppeteerExtraPlugin { utils.replaceWithProxy(_WebGLRenderingContext.prototype, 'getSupportedExtensions', { apply(target, thisArg, args) { _Reflect.apply(target, thisArg, args); - return data[propName].supportedExtensions; + return fakeDD[fakeDDPropName].supportedExtensions; }, }); @@ -97,16 +107,16 @@ class Plugin extends PuppeteerExtraPlugin { apply(target, thisArg, args) { const result = _Reflect.apply(target, thisArg, args); - result.alpha = data[propName].contextAttributes.alpha; - result.antialias = data[propName].contextAttributes.antialias; - result.depth = data[propName].contextAttributes.depth; - result.desynchronized = data[propName].contextAttributes.desynchronized; - result.failIfMajorPerformanceCaveat = data[propName].contextAttributes.failIfMajorPerformanceCaveat; - result.powerPreference = data[propName].contextAttributes.powerPreference; - result.premultipliedAlpha = data[propName].contextAttributes.premultipliedAlpha; - result.preserveDrawingBuffer = data[propName].contextAttributes.preserveDrawingBuffer; - result.stencil = data[propName].contextAttributes.stencil; - result.xrCompatible = data[propName].contextAttributes.xrCompatible; + result.alpha = fakeDD[fakeDDPropName].contextAttributes.alpha; + result.antialias = fakeDD[fakeDDPropName].contextAttributes.antialias; + result.depth = fakeDD[fakeDDPropName].contextAttributes.depth; + result.desynchronized = fakeDD[fakeDDPropName].contextAttributes.desynchronized; + result.failIfMajorPerformanceCaveat = fakeDD[fakeDDPropName].contextAttributes.failIfMajorPerformanceCaveat; + result.powerPreference = fakeDD[fakeDDPropName].contextAttributes.powerPreference; + result.premultipliedAlpha = fakeDD[fakeDDPropName].contextAttributes.premultipliedAlpha; + result.preserveDrawingBuffer = fakeDD[fakeDDPropName].contextAttributes.preserveDrawingBuffer; + result.stencil = fakeDD[fakeDDPropName].contextAttributes.stencil; + result.xrCompatible = fakeDD[fakeDDPropName].contextAttributes.xrCompatible; return result; }, @@ -119,7 +129,7 @@ class Plugin extends PuppeteerExtraPlugin { shaderPrecisionFormats.push({ shaderPrecisionFormat, - webglPropName: propName, + webglPropName: fakeDDPropName, shaderType: args[0], precisionType: args[1], rangeMin: WebGLShaderPrecisionFormat_prototype_rangeMin_get.call(shaderPrecisionFormat), @@ -161,7 +171,7 @@ class Plugin extends PuppeteerExtraPlugin { precision, } = r; - const fake_r = data[webglPropName].shaderPrecisionFormats.find( + const fake_r = fakeDD[webglPropName].shaderPrecisionFormats.find( e => e.shaderType === shaderType && e.precisionType === precisionType, ); @@ -189,7 +199,7 @@ class Plugin extends PuppeteerExtraPlugin { precision, } = r; - const fake_r = data[webglPropName].shaderPrecisionFormats.find( + const fake_r = fakeDD[webglPropName].shaderPrecisionFormats.find( e => e.shaderType === shaderType && e.precisionType === precisionType, ); @@ -217,7 +227,7 @@ class Plugin extends PuppeteerExtraPlugin { precision, } = r; - const fake_r = data[webglPropName].shaderPrecisionFormats.find( + const fake_r = fakeDD[webglPropName].shaderPrecisionFormats.find( e => e.shaderType === shaderType && e.precisionType === precisionType, ); diff --git a/src/plugins/evasions/webrtc/index.js b/src/plugins/evasions/webrtc/index.js index 1638837..5d74b4f 100644 --- a/src/plugins/evasions/webrtc/index.js +++ b/src/plugins/evasions/webrtc/index.js @@ -16,38 +16,32 @@ class Plugin extends PuppeteerExtraPlugin { } async onPageCreated(page) { - await withUtils(this, page).evaluateOnNewDocument( - this.mainFunction, - { - opts: this.opts, - }, - ); + await withUtils(this, page).evaluateOnNewDocument(this.mainFunction, { + proxyExportIP: this.opts.proxyExportIP, + myRealExportIP: this.opts.myRealExportIP, + }); } // SW does not support RTC // onServiceWorkerContent(jsContent) { - // return withWorkerUtils(this, jsContent).evaluate( - // this.mainFunction, - // { - // opts: this.opts, - // }, - // ); + // return withWorkerUtils(this, jsContent).evaluate(this.mainFunction); // } - mainFunction = (utils, {opts}) => { + mainFunction = (utils, {proxyExportIP, myRealExportIP}) => { // RTCIceCandidate.prototype.candidate get function // RTCIceCandidate.prototype.address get function // RTCIceCandidate.prototype.toJSON value function // RTCSessionDescription.prototype.sdp get function // RTCSessionDescription.prototype.toJSON value function - const _Reflect = utils.cache.Reflect; - const {proxyExportIP, fakeIPs} = opts; + const fakeIPs = [myRealExportIP]; if (!proxyExportIP || !fakeIPs || !fakeIPs.length) { return; } + const _Reflect = utils.cache.Reflect; + const replaceIps = (str) => { if (fakeIPs && proxyExportIP) { for (let fakeIP of fakeIPs) { diff --git a/src/plugins/evasions/window.history.length/index.js b/src/plugins/evasions/window.history.length/index.js index 48567c4..0bcfca9 100644 --- a/src/plugins/evasions/window.history.length/index.js +++ b/src/plugins/evasions/window.history.length/index.js @@ -14,7 +14,7 @@ class Plugin extends PuppeteerExtraPlugin { async onPageCreated(page) { await withUtils(this, page).evaluateOnNewDocument( - (utils, {historyLength}) => { + (utils, historyLength) => { for (let n = 0; n < historyLength; ++n) { if (window.history.length >= historyLength) { break; @@ -23,9 +23,7 @@ class Plugin extends PuppeteerExtraPlugin { window.history.pushState(null, ''); } }, - { - historyLength: this.opts.historyLength, - }, + this.opts.historyLength, ); } } diff --git a/src/plugins/evasions/window.speechSynthesis/index.js b/src/plugins/evasions/window.speechSynthesis/index.js index 01e32f8..c44875e 100644 --- a/src/plugins/evasions/window.speechSynthesis/index.js +++ b/src/plugins/evasions/window.speechSynthesis/index.js @@ -15,10 +15,10 @@ class Plugin extends PuppeteerExtraPlugin { } async onPageCreated(page) { - await withUtils(this, page).evaluateOnNewDocument(this.mainFunction, this.opts); + await withUtils(this, page).evaluateOnNewDocument(this.mainFunction, this.opts.fakeDD.voices); } - mainFunction = (utils, opts) => { + mainFunction = (utils, fakeVoices) => { const _Object = utils.cache.Object; const _Reflect = utils.cache.Reflect; @@ -48,7 +48,7 @@ class Plugin extends PuppeteerExtraPlugin { const voiceObjs = []; // With the configuration, construct voices object and then we hook the properties with Proxy - for (const voice of opts.voices) { + for (const voice of fakeVoices) { const voiceObj = new SpeechSynthesisVoice(); voiceObjs.push(voiceObj); @@ -101,7 +101,7 @@ class Plugin extends PuppeteerExtraPlugin { } } - return opts.voices[voiceObjs.indexOf(thisArg)][prop]; + return fakeVoices[voiceObjs.indexOf(thisArg)][prop]; }, }, ); diff --git a/src/plugins/evasions/workers/index.js b/src/plugins/evasions/workers/index.js index b696c73..a096c25 100644 --- a/src/plugins/evasions/workers/index.js +++ b/src/plugins/evasions/workers/index.js @@ -1,3 +1,5 @@ +// noinspection HttpUrlsUsage + 'use strict'; const {PuppeteerExtraPlugin} = require('puppeteer-extra-plugin'); @@ -14,90 +16,93 @@ class Plugin extends PuppeteerExtraPlugin { } async onPageCreated(page) { - await withUtils(this, page).evaluateOnNewDocument( - (utils, env) => { - if ('undefined' !== typeof Worker) { - // noinspection UnnecessaryLocalVariableJS - const _Worker = Worker; - const workerConstructor = Object.getOwnPropertyDescriptor( - _Worker.prototype, 'constructor', - ); - - utils.replaceWithProxy(utils.cache.global, 'Worker', { - construct: function (target, args) { - // console.log(`worker is registered in the browser, ${args[0]}`); - const relUrl = window.location.href; - const workerUrl = args[0]; - - // fix: The worker's relative path is relative to the current page path. - // reference: https://github.com/shehua/Alice/blob/master/w3c/html5-web-workers.md - - // noinspection LoopStatementThatDoesntLoopJS - for (; ;) { - if (!workerUrl) { - break; - } - - if ( - workerUrl.includes('://') && - !( - workerUrl.startsWith('http://') - || workerUrl.startsWith('https://') - ) - ) { - break; - } - - args[0] = `http://127.0.0.1:${env.internalHttpServerPort}/patchWorker?type=worker&uuid=${env.uuid}&relUrl=${encodeURIComponent(relUrl)}&workerUrl=${encodeURIComponent(workerUrl)}`; - - break; - } - - return new workerConstructor.value(...args); - }, - }); - } - - if ('undefined' !== typeof SharedWorker) { - const _SharedWorker = SharedWorker; - const sharedWorkerConstructor = Object.getOwnPropertyDescriptor( - _SharedWorker.prototype, 'constructor', - ); - - utils.replaceWithProxy(utils.cache.global, 'SharedWorker', { - construct: function (target, args) { - // console.log(`sharedWorker is registered in the browser, ${args[0]}`); - const relUrl = window.location.href; - const workerUrl = args[0]; - - // noinspection LoopStatementThatDoesntLoopJS - for (; ;) { - if (!workerUrl) { - break; - } - - if ( - workerUrl.includes('://') && - !( - workerUrl.startsWith('http://') - || workerUrl.startsWith('https://') - ) - ) { - break; - } - - args[0] = `http://127.0.0.1:${env.internalHttpServerPort}/patchWorker?type=sharedWorker&uuid=${env.uuid}&relUrl=${encodeURIComponent(relUrl)}&workerUrl=${encodeURIComponent(workerUrl)}`; - - break; - } - - return new sharedWorkerConstructor.value(...args); - }, - }); - } - }, this.opts.env, - ); + await withUtils(this, page).evaluateOnNewDocument(this.mainFunction, { + internalHttpServerPort: this.opts.internalHttpServerPort, + browserUUID: this.opts.browserUUID, + }); } + + mainFunction = (utils, {internalHttpServerPort, browserUUID}) => { + if ('undefined' !== typeof Worker) { + // noinspection UnnecessaryLocalVariableJS + const _Worker = Worker; + const workerConstructor = Object.getOwnPropertyDescriptor( + _Worker.prototype, 'constructor', + ); + + utils.replaceWithProxy(utils.cache.global, 'Worker', { + construct: function (target, args) { + // console.log(`worker is registered in the browser, ${args[0]}`); + const relUrl = window.location.href; + const workerUrl = args[0]; + + // fix: The worker's relative path is relative to the current page path. + // reference: https://github.com/shehua/Alice/blob/master/w3c/html5-web-workers.md + + // noinspection LoopStatementThatDoesntLoopJS + for (; ;) { + if (!workerUrl) { + break; + } + + if ( + workerUrl.includes('://') && + !( + workerUrl.startsWith('http://') + || workerUrl.startsWith('https://') + ) + ) { + break; + } + + args[0] = `http://127.0.0.1:${internalHttpServerPort}/patchWorker?type=worker&uuid=${browserUUID}&relUrl=${encodeURIComponent(relUrl)}&workerUrl=${encodeURIComponent(workerUrl)}`; + + break; + } + + return new workerConstructor.value(...args); + }, + }); + } + + if ('undefined' !== typeof SharedWorker) { + const _SharedWorker = SharedWorker; + const sharedWorkerConstructor = Object.getOwnPropertyDescriptor( + _SharedWorker.prototype, 'constructor', + ); + + utils.replaceWithProxy(utils.cache.global, 'SharedWorker', { + construct: function (target, args) { + // console.log(`sharedWorker is registered in the browser, ${args[0]}`); + const relUrl = window.location.href; + const workerUrl = args[0]; + + // noinspection LoopStatementThatDoesntLoopJS + for (; ;) { + if (!workerUrl) { + break; + } + + if ( + workerUrl.includes('://') && + !( + workerUrl.startsWith('http://') + || workerUrl.startsWith('https://') + ) + ) { + break; + } + + args[0] = `http://127.0.0.1:${internalHttpServerPort}/patchWorker?type=sharedWorker&uuid=${browserUUID}&relUrl=${encodeURIComponent(relUrl)}&workerUrl=${encodeURIComponent(workerUrl)}`; + + break; + } + + return new sharedWorkerConstructor.value(...args); + }, + }); + } + }; } module.exports = function (pluginConfig) { diff --git a/src/plugins/taskEnv/index.js b/src/plugins/taskEnv/index.js deleted file mode 100644 index a0de21b..0000000 --- a/src/plugins/taskEnv/index.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -const {PuppeteerExtraPlugin} = require('puppeteer-extra-plugin'); -const withUtils = require('../evasions/_utils/withUtils'); -const withWorkerUtils = require('../evasions/_utils/withWorkerUtils'); - -class Plugin extends PuppeteerExtraPlugin { - constructor(opts = {}) { - super(opts); - } - - get name() { - return 'evasions/taskEnv'; - } - - async onPageCreated(page) { - await withUtils(this, page).evaluateOnNewDocument(this.mainFunction, this.opts.env); - } - - onServiceWorkerContent(jsContent) { - return withWorkerUtils(this, jsContent).evaluate(this.mainFunction, this.opts.env); - } - - mainFunction = (utils, env) => { - utils.variables.env = env; - }; -} - -module.exports = function (pluginConfig) { - return new Plugin(pluginConfig); -}; diff --git a/src/plugins/taskEnv/package.json b/src/plugins/taskEnv/package.json deleted file mode 100644 index 18c15ea..0000000 --- a/src/plugins/taskEnv/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "private": true, - "main": "index.js" -} diff --git a/test/__tests__/test-keyboard.js b/test/__tests__/test-keyboard.js new file mode 100644 index 0000000..c00494c --- /dev/null +++ b/test/__tests__/test-keyboard.js @@ -0,0 +1,41 @@ +const timeout = 30000; + +describe( + '/ (properties.getter)', + () => { + let page; + beforeAll(async () => { + page = await global.vanillaBrowser.newPage(); + await page.goto('https://google.com'); + }, timeout); + + afterAll(async () => { + await page.close(); + }); + + it('plugins matched with dd', async () => { + const fakeValue = await page.evaluate(async () => { + const dumpKeyboard = async () => { + if (!('keyboard' in navigator && navigator.keyboard)) { + return []; + } + + const keys = ['Sleep', 'WakeUp', 'KeyA', 'KeyB', 'KeyC', 'KeyD', 'KeyE', 'KeyF', 'KeyG', 'KeyH', 'KeyI', 'KeyJ', 'KeyK', 'KeyL', 'KeyM', 'KeyN', 'KeyO', 'KeyP', 'KeyQ', 'KeyR', 'KeyS', 'KeyT', 'KeyU', 'KeyV', 'KeyW', 'KeyX', 'KeyY', 'KeyZ', 'Digit1', 'Digit2', 'Digit3', 'Digit4', 'Digit5', 'Digit6', 'Digit7', 'Digit8', 'Digit9', 'Digit0', 'Enter', 'Escape', 'Backspace', 'Tab', 'Space', 'Minus', 'Equal', 'BracketLeft', 'BracketRight', 'Backslash', 'Semicolon', 'Quote', 'Backquote', 'Comma', 'Period', 'Slash', 'CapsLock', 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12', 'F13', 'F14', 'F15', 'F16', 'F17', 'F18', 'F19', 'F20', 'F21', 'F22', 'F23', 'F24', 'PrintScreen', 'ScrollLock', 'Pause', 'Insert', 'Home', 'PageUp', 'Delete', 'End', 'PageDown', 'ArrowRight', 'ArrowLeft', 'ArrowDown', 'ArrowUp', 'NumLock', 'Numpad1', 'Numpad2', 'Numpad3', 'Numpad4', 'Numpad5', 'Numpad6', 'Numpad7', 'Numpad8', 'Numpad9', 'Numpad0', 'NumpadDivide', 'NumpadMultiply', 'NumpadSubtract', 'NumpadAdd', 'NumpadEnter', 'NumpadDecimal', 'NumpadEqual', 'NumpadParenLeft', 'NumpadParenRight', 'IntlBackslash', 'ContextMenu', 'Power', 'Help', 'Undo', 'Cut', 'Copy', 'Paste', 'AudioVolumeMute', 'AudioVolumeUp', 'AudioVolumeDown', 'NumpadComma', 'IntlRo', 'KanaMode', 'IntlYen', 'Convert', 'NonConvert', 'Lang1', 'Lang2', 'Lang3', 'Lang4', 'ControlLeft', 'ShiftLeft', 'AltLeft', 'MetaLeft', 'ControlRight', 'ShiftRight', 'AltRight', 'MetaRight', 'MediaTrackNext', 'MediaTrackPrevious', 'MediaStop', 'Eject', 'MediaPlayPause', 'MediaSelect', 'LaunchMail', 'LaunchApp2', 'LaunchApp1', 'BrowserSearch', 'BrowserHome', 'BrowserBack', 'BrowserForward', 'BrowserStop', 'BrowserRefresh', 'BrowserFavorites']; + const keyboardLayoutMap = await navigator.keyboard.getLayoutMap(); + return keys + .reduce((acc, key) => { + acc[key] = keyboardLayoutMap.get(key); + return acc; + }, {}); + }; + + return JSON.stringify(await dumpKeyboard()); + }); + + const origValue = JSON.stringify(global.fakeDeviceDesc['keyboard']); + + expect(fakeValue).toBe(origValue); + }, timeout); + }, + timeout, +); diff --git a/test/__tests__/test-window.speechSynthesis.js b/test/__tests__/test-window.speechSynthesis.js new file mode 100644 index 0000000..231e93b --- /dev/null +++ b/test/__tests__/test-window.speechSynthesis.js @@ -0,0 +1,80 @@ +const timeout = 30000; + +describe( + '/ (properties.getter)', + () => { + let page; + beforeAll(async () => { + page = await global.vanillaBrowser.newPage(); + await page.goto('https://google.com'); + }, timeout); + + afterAll(async () => { + await page.close(); + }); + + it('plugins matched with dd', async () => { + const fakeValue = await page.evaluate(async () => { + const dumpVoices = () => { + return new Promise(async resolve => { + try { + const win = window; + const supported = 'speechSynthesis' in win; + supported && speechSynthesis.getVoices(); // warm up + + // noinspection JSCheckFunctionSignatures + await new Promise(setTimeout).catch(e => e); + + if (!supported) { + return resolve([]); + } + + // inspired by https://github.com/abrahamjuliot/creepjs/blob/master/creep.js + let success = false; + const getVoices = () => { + const data = win.speechSynthesis.getVoices(); + if (!data || !data.length) { + return; + } + + success = true; + + const voices = data.map(e => ({ + default: e.default, + lang: e.lang, + localService: e.localService, + name: e.name, + voiceURI: e.voiceURI, + })); + + return resolve(voices); + }; + + getVoices(); + win.speechSynthesis.onvoiceschanged = getVoices; // Chrome support + + // handle pending resolve + const wait = 1000; + setTimeout(() => { + if (success) { + return; + } + + return resolve([]); + }, wait); + } catch (error) { + return resolve([]); + } + }); + }; + + return JSON.stringify(await dumpVoices()); + }); + + const origValue = JSON.stringify(global.fakeDeviceDesc['voices']); + + expect(fakeValue).toBe(origValue); + }, timeout); + }, + timeout, +);