@browserless/goto 9.12.4 → 10.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +0 -0
- package/package.json +11 -14
- package/src/index.js +3 -24
- package/src/evasions/chrome-runtime.js +0 -69
- package/src/evasions/index.js +0 -13
- package/src/evasions/media-codecs.js +0 -76
- package/src/evasions/navigator-permissions.js +0 -43
- package/src/evasions/navigator-plugins.js +0 -199
- package/src/evasions/navigator-vendor.js +0 -25
- package/src/evasions/randomize-user-agent.js +0 -8
- package/src/evasions/stack-traces.js +0 -51
- package/src/evasions/webgl-vendor.js +0 -50
- package/src/evasions/window-frame.js +0 -9
package/LICENSE.md
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@browserless/goto",
|
|
3
3
|
"description": "Go to a page aborting unnecessary requests",
|
|
4
4
|
"homepage": "https://browserless.js.org/#/?id=gotopage-options",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "10.0.0",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"author": {
|
|
8
8
|
"email": "hello@microlink.io",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"puppeteer"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@browserless/devices": "^
|
|
32
|
+
"@browserless/devices": "^10.0.0",
|
|
33
33
|
"@cliqz/adblocker-puppeteer": "~1.26.6",
|
|
34
34
|
"debug-logfmt": "~1.0.4",
|
|
35
35
|
"got": "~11.8.6",
|
|
@@ -39,15 +39,12 @@
|
|
|
39
39
|
"pretty-ms": "~7.0.1",
|
|
40
40
|
"shallow-equal": "~3.1.0",
|
|
41
41
|
"time-span": "~4.0.0",
|
|
42
|
-
"
|
|
43
|
-
"tough-cookie": "~4.1.3",
|
|
44
|
-
"unique-random-array": "~2.0.0"
|
|
42
|
+
"tough-cookie": "~4.1.3"
|
|
45
43
|
},
|
|
46
44
|
"devDependencies": {
|
|
47
|
-
"@browserless/test": "^
|
|
45
|
+
"@browserless/test": "^10.0.0",
|
|
48
46
|
"ava": "latest",
|
|
49
|
-
"
|
|
50
|
-
"fpscanner": "latest",
|
|
47
|
+
"is-ci": "latest",
|
|
51
48
|
"p-wait-for": "3"
|
|
52
49
|
},
|
|
53
50
|
"engines": {
|
|
@@ -57,10 +54,6 @@
|
|
|
57
54
|
"scripts",
|
|
58
55
|
"src"
|
|
59
56
|
],
|
|
60
|
-
"scripts": {
|
|
61
|
-
"postinstall": "node scripts/postinstall",
|
|
62
|
-
"test": "ava"
|
|
63
|
-
},
|
|
64
57
|
"license": "MIT",
|
|
65
58
|
"ava": {
|
|
66
59
|
"files": [
|
|
@@ -70,5 +63,9 @@
|
|
|
70
63
|
"timeout": "30s",
|
|
71
64
|
"workerThreads": false
|
|
72
65
|
},
|
|
73
|
-
"gitHead": "
|
|
74
|
-
|
|
66
|
+
"gitHead": "9085ee165094687aaf15c2d9a429d1c6acb9fe83",
|
|
67
|
+
"scripts": {
|
|
68
|
+
"postinstall": "node scripts/postinstall",
|
|
69
|
+
"test": "ava"
|
|
70
|
+
}
|
|
71
|
+
}
|
package/src/index.js
CHANGED
|
@@ -19,10 +19,6 @@ debug.adblock = require('debug-logfmt')('browserless:goto:adblock')
|
|
|
19
19
|
|
|
20
20
|
const truncate = (str, n = 80) => (str.length > n ? str.substr(0, n - 1) + '…' : str)
|
|
21
21
|
|
|
22
|
-
const EVASIONS = require('./evasions')
|
|
23
|
-
|
|
24
|
-
const ALL_EVASIONS_KEYS = Object.keys(EVASIONS)
|
|
25
|
-
|
|
26
22
|
const engine = PuppeteerBlocker.deserialize(
|
|
27
23
|
new Uint8Array(fs.readFileSync(path.resolve(__dirname, './engine.bin')))
|
|
28
24
|
)
|
|
@@ -155,12 +151,7 @@ const inject = async (page, { timeout, mediaType, animations, modules, scripts,
|
|
|
155
151
|
return Promise.all(postPromises)
|
|
156
152
|
}
|
|
157
153
|
|
|
158
|
-
module.exports = ({
|
|
159
|
-
evasions = ALL_EVASIONS_KEYS,
|
|
160
|
-
defaultDevice = 'Macbook Pro 13',
|
|
161
|
-
timeout: globalTimeout,
|
|
162
|
-
...deviceOpts
|
|
163
|
-
}) => {
|
|
154
|
+
module.exports = ({ defaultDevice = 'Macbook Pro 13', timeout: globalTimeout, ...deviceOpts }) => {
|
|
164
155
|
const getDevice = createDevices(deviceOpts)
|
|
165
156
|
const { viewport: defaultViewport } = getDevice.findDevice(defaultDevice)
|
|
166
157
|
|
|
@@ -249,7 +240,8 @@ module.exports = ({
|
|
|
249
240
|
}
|
|
250
241
|
|
|
251
242
|
const enableInterception =
|
|
252
|
-
(onPageRequest || abortTypes.length > 0) &&
|
|
243
|
+
(onPageRequest || abortTypes.length > 0) &&
|
|
244
|
+
run({ fn: page.setRequestInterception(true), debug: 'enableInterception' })
|
|
253
245
|
|
|
254
246
|
if (onPageRequest) {
|
|
255
247
|
Promise.resolve(enableInterception).then(() => page.on('request', onPageRequest))
|
|
@@ -363,18 +355,6 @@ module.exports = ({
|
|
|
363
355
|
)
|
|
364
356
|
}
|
|
365
357
|
|
|
366
|
-
const applyEvasions = castArray(evasions)
|
|
367
|
-
.filter(Boolean)
|
|
368
|
-
.map(key =>
|
|
369
|
-
run({
|
|
370
|
-
fn: EVASIONS[key](page),
|
|
371
|
-
timeout: actionTimeout,
|
|
372
|
-
debug: key
|
|
373
|
-
})
|
|
374
|
-
)
|
|
375
|
-
|
|
376
|
-
await Promise.all(prePromises.concat(applyEvasions))
|
|
377
|
-
|
|
378
358
|
const { value: response, reason: error } = await run({
|
|
379
359
|
fn: html
|
|
380
360
|
? page.setContent(html, { waitUntil, ...args })
|
|
@@ -442,4 +422,3 @@ module.exports = ({
|
|
|
442
422
|
|
|
443
423
|
module.exports.parseCookies = parseCookies
|
|
444
424
|
module.exports.inject = inject
|
|
445
|
-
module.exports.evasions = ALL_EVASIONS_KEYS
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Pass the Chrome Test.
|
|
5
|
-
*
|
|
6
|
-
* This will work for iframes as well, except for `srcdoc` iframes:
|
|
7
|
-
* https://github.com/puppeteer/puppeteer/issues/1106
|
|
8
|
-
*
|
|
9
|
-
* Could be mocked further.
|
|
10
|
-
*/
|
|
11
|
-
module.exports = page =>
|
|
12
|
-
page.evaluateOnNewDocument(() => {
|
|
13
|
-
window.chrome = {
|
|
14
|
-
app: {
|
|
15
|
-
isInstalled: false,
|
|
16
|
-
InstallState: {
|
|
17
|
-
DISABLED: 'disabled',
|
|
18
|
-
INSTALLED: 'installed',
|
|
19
|
-
NOT_INSTALLED: 'not_installed'
|
|
20
|
-
},
|
|
21
|
-
RunningState: {
|
|
22
|
-
CANNOT_RUN: 'cannot_run',
|
|
23
|
-
READY_TO_RUN: 'ready_to_run',
|
|
24
|
-
RUNNING: 'running'
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
runtime: {
|
|
28
|
-
OnInstalledReason: {
|
|
29
|
-
CHROME_UPDATE: 'chrome_update',
|
|
30
|
-
INSTALL: 'install',
|
|
31
|
-
SHARED_MODULE_UPDATE: 'shared_module_update',
|
|
32
|
-
UPDATE: 'update'
|
|
33
|
-
},
|
|
34
|
-
OnRestartRequiredReason: {
|
|
35
|
-
APP_UPDATE: 'app_update',
|
|
36
|
-
OS_UPDATE: 'os_update',
|
|
37
|
-
PERIODIC: 'periodic'
|
|
38
|
-
},
|
|
39
|
-
PlatformArch: {
|
|
40
|
-
ARM: 'arm',
|
|
41
|
-
ARM64: 'arm64',
|
|
42
|
-
MIPS: 'mips',
|
|
43
|
-
MIPS64: 'mips64',
|
|
44
|
-
X86_32: 'x86-32',
|
|
45
|
-
X86_64: 'x86-64'
|
|
46
|
-
},
|
|
47
|
-
PlatformNaclArch: {
|
|
48
|
-
ARM: 'arm',
|
|
49
|
-
MIPS: 'mips',
|
|
50
|
-
MIPS64: 'mips64',
|
|
51
|
-
X86_32: 'x86-32',
|
|
52
|
-
X86_64: 'x86-64'
|
|
53
|
-
},
|
|
54
|
-
PlatformOs: {
|
|
55
|
-
ANDROID: 'android',
|
|
56
|
-
CROS: 'cros',
|
|
57
|
-
LINUX: 'linux',
|
|
58
|
-
MAC: 'mac',
|
|
59
|
-
OPENBSD: 'openbsd',
|
|
60
|
-
WIN: 'win'
|
|
61
|
-
},
|
|
62
|
-
RequestUpdateCheckStatus: {
|
|
63
|
-
NO_UPDATE: 'no_update',
|
|
64
|
-
THROTTLED: 'throttled',
|
|
65
|
-
UPDATE_AVAILABLE: 'update_available'
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
})
|
package/src/evasions/index.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
module.exports = {
|
|
4
|
-
chromeRuntime: require('./chrome-runtime'),
|
|
5
|
-
mediaCodecs: require('./media-codecs'),
|
|
6
|
-
navigatorPermissions: require('./navigator-permissions'),
|
|
7
|
-
navigatorPlugins: require('./navigator-plugins'),
|
|
8
|
-
navigatorVendor: require('./navigator-vendor'),
|
|
9
|
-
randomizeUserAgent: require('./randomize-user-agent'),
|
|
10
|
-
stackTraces: require('./stack-traces'),
|
|
11
|
-
webglVendor: require('./webgl-vendor'),
|
|
12
|
-
windowFrame: require('./window-frame')
|
|
13
|
-
}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Fix Chromium not reporting "probably" to codecs like `videoEl.canPlayType('video/mp4; codecs="avc1.42E01E"')`.
|
|
5
|
-
* (Chromium doesn't support proprietary codecs, only Chrome does)
|
|
6
|
-
*/
|
|
7
|
-
module.exports = page =>
|
|
8
|
-
page.evaluateOnNewDocument(() => {
|
|
9
|
-
try {
|
|
10
|
-
/**
|
|
11
|
-
* Input might look funky, we need to normalize it so e.g. whitespace isn't an issue for our spoofing.
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* video/webm; codecs="vp8, vorbis"
|
|
15
|
-
* video/mp4; codecs="avc1.42E01E"
|
|
16
|
-
* audio/x-m4a;
|
|
17
|
-
* audio/ogg; codecs="vorbis"
|
|
18
|
-
* @param {String} arg
|
|
19
|
-
*/
|
|
20
|
-
const parseInput = arg => {
|
|
21
|
-
const [mime, codecStr] = arg.trim().split(';')
|
|
22
|
-
let codecs = []
|
|
23
|
-
if (codecStr && codecStr.includes('codecs="')) {
|
|
24
|
-
codecs = codecStr
|
|
25
|
-
.trim()
|
|
26
|
-
.replace('codecs="', '')
|
|
27
|
-
.replace('"', '')
|
|
28
|
-
.trim()
|
|
29
|
-
.split(',')
|
|
30
|
-
.filter(x => !!x)
|
|
31
|
-
.map(x => x.trim())
|
|
32
|
-
}
|
|
33
|
-
return { mime, codecStr, codecs }
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/* global HTMLMediaElement */
|
|
37
|
-
const canPlayType = {
|
|
38
|
-
// Make toString() native
|
|
39
|
-
get (target, key) {
|
|
40
|
-
// Mitigate Chromium bug (#130)
|
|
41
|
-
if (typeof target[key] === 'function') {
|
|
42
|
-
return target[key].bind(target)
|
|
43
|
-
}
|
|
44
|
-
return Reflect.get(target, key)
|
|
45
|
-
},
|
|
46
|
-
// Intercept certain requests
|
|
47
|
-
apply: function (target, ctx, args) {
|
|
48
|
-
if (!args || !args.length) {
|
|
49
|
-
return target.apply(ctx, args)
|
|
50
|
-
}
|
|
51
|
-
const { mime, codecs } = parseInput(args[0])
|
|
52
|
-
// This specific mp4 codec is missing in Chromium
|
|
53
|
-
if (mime === 'video/mp4') {
|
|
54
|
-
if (codecs.includes('avc1.42E01E')) {
|
|
55
|
-
return 'probably'
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
// This mimetype is only supported if no codecs are specified
|
|
59
|
-
if (mime === 'audio/x-m4a' && !codecs.length) {
|
|
60
|
-
return 'maybe'
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// This mimetype is only supported if no codecs are specified
|
|
64
|
-
if (mime === 'audio/aac' && !codecs.length) {
|
|
65
|
-
return 'probably'
|
|
66
|
-
}
|
|
67
|
-
// Everything else as usual
|
|
68
|
-
return target.apply(ctx, args)
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
HTMLMediaElement.prototype.canPlayType = new Proxy(
|
|
72
|
-
HTMLMediaElement.prototype.canPlayType,
|
|
73
|
-
canPlayType
|
|
74
|
-
)
|
|
75
|
-
} catch (err) {}
|
|
76
|
-
})
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Pass the Permissions Test.
|
|
5
|
-
*/
|
|
6
|
-
module.exports = page =>
|
|
7
|
-
page.evaluateOnNewDocument(() => {
|
|
8
|
-
if (!window.Notification) {
|
|
9
|
-
window.Notification = {
|
|
10
|
-
permission: 'denied'
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const originalQuery = window.navigator.permissions.query
|
|
15
|
-
// eslint-disable-next-line
|
|
16
|
-
window.navigator.permissions.__proto__.query = parameters =>
|
|
17
|
-
parameters.name === 'notifications'
|
|
18
|
-
? Promise.resolve({ state: window.Notification.permission })
|
|
19
|
-
: originalQuery(parameters)
|
|
20
|
-
|
|
21
|
-
// Inspired by: https://github.com/ikarienator/phantomjs_hide_and_seek/blob/master/5.spoofFunctionBind.js
|
|
22
|
-
const oldCall = Function.prototype.call
|
|
23
|
-
function call () {
|
|
24
|
-
return oldCall.apply(this, arguments)
|
|
25
|
-
}
|
|
26
|
-
// eslint-disable-next-line
|
|
27
|
-
Function.prototype.call = call
|
|
28
|
-
|
|
29
|
-
const nativeToStringFunctionString = Error.toString().replace(/Error/g, 'toString')
|
|
30
|
-
const oldToString = Function.prototype.toString
|
|
31
|
-
|
|
32
|
-
function functionToString () {
|
|
33
|
-
if (this === window.navigator.permissions.query) {
|
|
34
|
-
return 'function query() { [native code] }'
|
|
35
|
-
}
|
|
36
|
-
if (this === functionToString) {
|
|
37
|
-
return nativeToStringFunctionString
|
|
38
|
-
}
|
|
39
|
-
return oldCall.call(oldToString, this)
|
|
40
|
-
}
|
|
41
|
-
// eslint-disable-next-line
|
|
42
|
-
Function.prototype.toString = functionToString
|
|
43
|
-
})
|
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* In headless mode `navigator.mimeTypes` and `navigator.plugins` are empty.
|
|
5
|
-
* This plugin quite emulates both of these to match regular headful Chrome.
|
|
6
|
-
* We even go so far as to mock functional methods, instance types and `.toString` properties. :D
|
|
7
|
-
*/
|
|
8
|
-
module.exports = page =>
|
|
9
|
-
page.evaluateOnNewDocument(() => {
|
|
10
|
-
function mockPluginsAndMimeTypes () {
|
|
11
|
-
/* global MimeType MimeTypeArray PluginArray */
|
|
12
|
-
|
|
13
|
-
// Disguise custom functions as being native
|
|
14
|
-
const makeFnsNative = (fns = []) => {
|
|
15
|
-
const oldCall = Function.prototype.call
|
|
16
|
-
function call () {
|
|
17
|
-
return oldCall.apply(this, arguments)
|
|
18
|
-
}
|
|
19
|
-
// eslint-disable-next-line
|
|
20
|
-
Function.prototype.call = call
|
|
21
|
-
|
|
22
|
-
const nativeToStringFunctionString = Error.toString().replace(/Error/g, 'toString')
|
|
23
|
-
const oldToString = Function.prototype.toString
|
|
24
|
-
|
|
25
|
-
function functionToString () {
|
|
26
|
-
for (const fn of fns) {
|
|
27
|
-
if (this === fn.ref) {
|
|
28
|
-
return `function ${fn.name}() { [native code] }`
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (this === functionToString) {
|
|
33
|
-
return nativeToStringFunctionString
|
|
34
|
-
}
|
|
35
|
-
return oldCall.call(oldToString, this)
|
|
36
|
-
}
|
|
37
|
-
// eslint-disable-next-line
|
|
38
|
-
Function.prototype.toString = functionToString
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const mockedFns = []
|
|
42
|
-
|
|
43
|
-
const fakeData = {
|
|
44
|
-
mimeTypes: [
|
|
45
|
-
{
|
|
46
|
-
type: 'application/pdf',
|
|
47
|
-
suffixes: 'pdf',
|
|
48
|
-
description: '',
|
|
49
|
-
__pluginName: 'Chrome PDF Viewer'
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
type: 'application/x-google-chrome-pdf',
|
|
53
|
-
suffixes: 'pdf',
|
|
54
|
-
description: 'Portable Document Format',
|
|
55
|
-
__pluginName: 'Chrome PDF Plugin'
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
type: 'application/x-nacl',
|
|
59
|
-
suffixes: '',
|
|
60
|
-
description: 'Native Client Executable',
|
|
61
|
-
// eslint-disable-next-line
|
|
62
|
-
enabledPlugin: Plugin,
|
|
63
|
-
__pluginName: 'Native Client'
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
type: 'application/x-pnacl',
|
|
67
|
-
suffixes: '',
|
|
68
|
-
description: 'Portable Native Client Executable',
|
|
69
|
-
__pluginName: 'Native Client'
|
|
70
|
-
}
|
|
71
|
-
],
|
|
72
|
-
plugins: [
|
|
73
|
-
{
|
|
74
|
-
name: 'Chrome PDF Plugin',
|
|
75
|
-
filename: 'internal-pdf-viewer',
|
|
76
|
-
description: 'Portable Document Format'
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
name: 'Chrome PDF Viewer',
|
|
80
|
-
filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai',
|
|
81
|
-
description: ''
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
name: 'Native Client',
|
|
85
|
-
filename: 'internal-nacl-plugin',
|
|
86
|
-
description: ''
|
|
87
|
-
}
|
|
88
|
-
],
|
|
89
|
-
fns: {
|
|
90
|
-
namedItem: instanceName => {
|
|
91
|
-
// Returns the Plugin/MimeType with the specified name.
|
|
92
|
-
const fn = function (name) {
|
|
93
|
-
if (!arguments.length) {
|
|
94
|
-
throw new TypeError(
|
|
95
|
-
`Failed to execute 'namedItem' on '${instanceName}': 1 argument required, but only 0 present.`
|
|
96
|
-
)
|
|
97
|
-
}
|
|
98
|
-
return this[name] || null
|
|
99
|
-
}
|
|
100
|
-
mockedFns.push({ ref: fn, name: 'namedItem' })
|
|
101
|
-
return fn
|
|
102
|
-
},
|
|
103
|
-
item: instanceName => {
|
|
104
|
-
// Returns the Plugin/MimeType at the specified index into the array.
|
|
105
|
-
const fn = function (index) {
|
|
106
|
-
if (!arguments.length) {
|
|
107
|
-
throw new TypeError(
|
|
108
|
-
`Failed to execute 'namedItem' on '${instanceName}': 1 argument required, but only 0 present.`
|
|
109
|
-
)
|
|
110
|
-
}
|
|
111
|
-
return this[index] || null
|
|
112
|
-
}
|
|
113
|
-
mockedFns.push({ ref: fn, name: 'item' })
|
|
114
|
-
return fn
|
|
115
|
-
},
|
|
116
|
-
refresh: instanceName => {
|
|
117
|
-
// Refreshes all plugins on the current page, optionally reloading documents.
|
|
118
|
-
const fn = function () {
|
|
119
|
-
return undefined
|
|
120
|
-
}
|
|
121
|
-
mockedFns.push({ ref: fn, name: 'refresh' })
|
|
122
|
-
return fn
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
// Poor mans _.pluck
|
|
127
|
-
const getSubset = (keys, obj) => keys.reduce((a, c) => ({ ...a, [c]: obj[c] }), {})
|
|
128
|
-
|
|
129
|
-
function generateMimeTypeArray () {
|
|
130
|
-
const arr = fakeData.mimeTypes
|
|
131
|
-
.map(obj => getSubset(['type', 'suffixes', 'description'], obj))
|
|
132
|
-
.map(obj => Object.setPrototypeOf(obj, MimeType.prototype))
|
|
133
|
-
arr.forEach(obj => {
|
|
134
|
-
arr[obj.type] = obj
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
// Mock functions
|
|
138
|
-
arr.namedItem = fakeData.fns.namedItem('MimeTypeArray')
|
|
139
|
-
arr.item = fakeData.fns.item('MimeTypeArray')
|
|
140
|
-
|
|
141
|
-
return Object.setPrototypeOf(arr, MimeTypeArray.prototype)
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const mimeTypeArray = generateMimeTypeArray()
|
|
145
|
-
Object.defineProperty(navigator, 'mimeTypes', {
|
|
146
|
-
get: () => mimeTypeArray
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
function generatePluginArray () {
|
|
150
|
-
const arr = fakeData.plugins
|
|
151
|
-
.map(obj => getSubset(['name', 'filename', 'description'], obj))
|
|
152
|
-
.map(obj => {
|
|
153
|
-
const mimes = fakeData.mimeTypes.filter(m => m.__pluginName === obj.name)
|
|
154
|
-
// Add mimetypes
|
|
155
|
-
mimes.forEach((mime, index) => {
|
|
156
|
-
navigator.mimeTypes[mime.type].enabledPlugin = obj
|
|
157
|
-
obj[mime.type] = navigator.mimeTypes[mime.type]
|
|
158
|
-
obj[index] = navigator.mimeTypes[mime.type]
|
|
159
|
-
})
|
|
160
|
-
obj.length = mimes.length
|
|
161
|
-
return obj
|
|
162
|
-
})
|
|
163
|
-
.map(obj => {
|
|
164
|
-
// Mock functions
|
|
165
|
-
obj.namedItem = fakeData.fns.namedItem('Plugin')
|
|
166
|
-
obj.item = fakeData.fns.item('Plugin')
|
|
167
|
-
return obj
|
|
168
|
-
})
|
|
169
|
-
// eslint-disable-next-line
|
|
170
|
-
.map(obj => Object.setPrototypeOf(obj, Plugin.prototype))
|
|
171
|
-
arr.forEach(obj => {
|
|
172
|
-
arr[obj.name] = obj
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
// Mock functions
|
|
176
|
-
arr.namedItem = fakeData.fns.namedItem('PluginArray')
|
|
177
|
-
arr.item = fakeData.fns.item('PluginArray')
|
|
178
|
-
arr.refresh = fakeData.fns.refresh('PluginArray')
|
|
179
|
-
|
|
180
|
-
return Object.setPrototypeOf(arr, PluginArray.prototype)
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const pluginArray = generatePluginArray()
|
|
184
|
-
Object.defineProperty(navigator, 'plugins', {
|
|
185
|
-
get: () => pluginArray
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
// Make mockedFns toString() representation resemble a native function
|
|
189
|
-
makeFnsNative(mockedFns)
|
|
190
|
-
}
|
|
191
|
-
try {
|
|
192
|
-
const isPluginArray = navigator.plugins instanceof PluginArray
|
|
193
|
-
const hasPlugins = isPluginArray && navigator.plugins.length > 0
|
|
194
|
-
if (isPluginArray && hasPlugins) {
|
|
195
|
-
return // nothing to do here
|
|
196
|
-
}
|
|
197
|
-
mockPluginsAndMimeTypes()
|
|
198
|
-
} catch (err) {}
|
|
199
|
-
})
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
module.exports = page =>
|
|
2
|
-
page.evaluateOnNewDocument(async () => {
|
|
3
|
-
const getUserAgentVendor = userAgent => {
|
|
4
|
-
let vendor
|
|
5
|
-
|
|
6
|
-
if (userAgent.includes('Firefox')) {
|
|
7
|
-
vendor = 'firefox'
|
|
8
|
-
} else if (userAgent.includes('Chrome')) {
|
|
9
|
-
vendor = 'chrome'
|
|
10
|
-
} else {
|
|
11
|
-
vendor = 'safari'
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
return vendor
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const userAgent = navigator.userAgent
|
|
18
|
-
const userAgentVendor = getUserAgentVendor(userAgent)
|
|
19
|
-
|
|
20
|
-
if (userAgentVendor === 'chrome') return
|
|
21
|
-
|
|
22
|
-
Object.defineProperty(navigator, 'vendor', {
|
|
23
|
-
value: userAgentVendor === 'safari' ? 'Apple Computer, Inc.' : ''
|
|
24
|
-
})
|
|
25
|
-
})
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
*
|
|
5
|
-
* Prevent detect Puppeteer via variable name
|
|
6
|
-
*
|
|
7
|
-
* References:
|
|
8
|
-
* - https://github.com/digitalhurricane-io/puppeteer-detection-100-percent
|
|
9
|
-
* - https://github.com/berstend/puppeteer-extra/issues/209
|
|
10
|
-
*/
|
|
11
|
-
module.exports = page =>
|
|
12
|
-
page.evaluateOnNewDocument(() => {
|
|
13
|
-
const errors = {
|
|
14
|
-
Error,
|
|
15
|
-
EvalError,
|
|
16
|
-
RangeError,
|
|
17
|
-
ReferenceError,
|
|
18
|
-
SyntaxError,
|
|
19
|
-
TypeError,
|
|
20
|
-
URIError
|
|
21
|
-
}
|
|
22
|
-
for (const name in errors) {
|
|
23
|
-
// eslint-disable-next-line
|
|
24
|
-
globalThis[name] = (function (NativeError) {
|
|
25
|
-
return function (message) {
|
|
26
|
-
const err = new NativeError(message)
|
|
27
|
-
const stub = {
|
|
28
|
-
message: err.message,
|
|
29
|
-
name: err.name,
|
|
30
|
-
toString: () => err.toString(),
|
|
31
|
-
get stack () {
|
|
32
|
-
const lines = err.stack.split('\n')
|
|
33
|
-
lines.splice(1, 1) // remove anonymous function above
|
|
34
|
-
lines.pop() // remove puppeteer line
|
|
35
|
-
return lines.join('\n')
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
// eslint-disable-next-line
|
|
39
|
-
if (this === globalThis) {
|
|
40
|
-
// called as function, not constructor
|
|
41
|
-
// eslint-disable-next-line
|
|
42
|
-
stub.__proto__ = NativeError
|
|
43
|
-
return stub
|
|
44
|
-
}
|
|
45
|
-
Object.assign(this, stub)
|
|
46
|
-
// eslint-disable-next-line
|
|
47
|
-
this.__proto__ = NativeError
|
|
48
|
-
}
|
|
49
|
-
})(errors[name])
|
|
50
|
-
}
|
|
51
|
-
})
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Fix WebGL Vendor/Renderer being set to Google in headless mode
|
|
3
|
-
*/
|
|
4
|
-
module.exports = page =>
|
|
5
|
-
page.evaluateOnNewDocument(() => {
|
|
6
|
-
// Remove traces of our Proxy ;-)
|
|
7
|
-
const stripErrorStack = stack =>
|
|
8
|
-
stack
|
|
9
|
-
.split('\n')
|
|
10
|
-
.filter(line => !line.includes('at Object.apply'))
|
|
11
|
-
.filter(line => !line.includes('at Object.get'))
|
|
12
|
-
.join('\n')
|
|
13
|
-
|
|
14
|
-
const getParameterProxyHandler = {
|
|
15
|
-
get (target, key) {
|
|
16
|
-
try {
|
|
17
|
-
// Mitigate Chromium bug (#130)
|
|
18
|
-
if (typeof target[key] === 'function') {
|
|
19
|
-
return target[key].bind(target)
|
|
20
|
-
}
|
|
21
|
-
return Reflect.get(target, key)
|
|
22
|
-
} catch (err) {
|
|
23
|
-
err.stack = stripErrorStack(err.stack)
|
|
24
|
-
throw err
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
apply: function (target, thisArg, args) {
|
|
28
|
-
const param = (args || [])[0]
|
|
29
|
-
// UNMASKED_VENDOR_WEBGL
|
|
30
|
-
if (param === 37445) return 'Intel Inc.'
|
|
31
|
-
// UNMASKED_RENDERER_WEBGL
|
|
32
|
-
if (param === 37446) return 'Intel(R) Iris(TM) Plus Graphics 640'
|
|
33
|
-
try {
|
|
34
|
-
return Reflect.apply(target, thisArg, args)
|
|
35
|
-
} catch (err) {
|
|
36
|
-
err.stack = stripErrorStack(err.stack)
|
|
37
|
-
throw err
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
;['WebGLRenderingContext', 'WebGL2RenderingContext'].forEach(function (ctx) {
|
|
43
|
-
Object.defineProperty(window[ctx].prototype, 'getParameter', {
|
|
44
|
-
configurable: true,
|
|
45
|
-
enumerable: false,
|
|
46
|
-
writable: false,
|
|
47
|
-
value: new Proxy(window[ctx].prototype.getParameter, getParameterProxyHandler)
|
|
48
|
-
})
|
|
49
|
-
})
|
|
50
|
-
})
|