@zorilla/puppeteer-extra-plugin-stealth 1.0.2 → 1.0.3
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/CHANGELOG.md +12 -0
- package/README.md +4 -4
- package/dist/evasions/sourceurl/index.js +1 -1
- package/dist/evasions/sourceurl/index.js.map +1 -1
- package/dist/evasions/sourceurl/index.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/package.json +8 -7
- package/src/evasions/sourceurl/index.ts +1 -1
- package/src/index.ts +1 -1
- package/test/ambient.d.ts +85 -0
- package/test/cat-and-mouse.test.ts +21 -20
- package/test/evasions/_utils/index.test.ts +1 -1
- package/test/evasions/chrome.app/index.test.ts +36 -17
- package/test/evasions/chrome.csi/index.test.ts +10 -7
- package/test/evasions/chrome.loadTimes/index.test.ts +9 -7
- package/test/evasions/chrome.runtime/index.test.ts +1 -1
- package/test/evasions/defaultArgs/index.test.ts +9 -9
- package/test/evasions/iframe.contentWindow/{index.test.js → index.test.ts} +2 -1
- package/test/evasions/media.codecs/{index.test.js → index.test.ts} +1 -1
- package/test/evasions/navigator.hardwareConcurrency/{index.test.js → index.test.ts} +1 -1
- package/test/evasions/navigator.languages/{index.test.js → index.test.ts} +1 -1
- package/test/evasions/navigator.permissions/{index.test.js → index.test.ts} +1 -1
- package/test/evasions/navigator.plugins/{index.test.js → index.test.ts} +14 -3
- package/test/evasions/navigator.plugins/{mimeTypes.test.js → mimeTypes.test.ts} +2 -6
- package/test/evasions/navigator.plugins/{plugins.test.js → plugins.test.ts} +1 -5
- package/test/evasions/navigator.vendor/{index.test.js → index.test.ts} +1 -5
- package/test/evasions/navigator.webdriver/{index.test.js → index.test.ts} +1 -1
- package/test/evasions/sourceurl/{index.test.js → index.test.ts} +1 -5
- package/test/evasions/user-agent-override/{index.test.js → index.test.ts} +1 -5
- package/test/evasions/webgl.vendor/{index.test.js → index.test.ts} +9 -4
- package/test/fpscanner.test.ts +112 -45
- package/test/index.test.ts +13 -5
- package/test/service-worker.test.ts +40 -19
- package/test/{util.js → util.ts} +45 -15
- package/tsconfig.json +1 -1
- package/tsconfig.test.json +23 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/vitest.config.ts +8 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @zorilla/puppeteer-extra-plugin-stealth
|
|
2
2
|
|
|
3
|
+
## 1.0.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#56](https://github.com/zorillajs/zorilla/pull/56) [`a414e33`](https://github.com/zorillajs/zorilla/commit/a414e33dd236db995fd8cb40b0955851953eaddb) Thanks [@JustinBeckwith](https://github.com/JustinBeckwith)! - Convert the remaining automated stealth JS tests to TypeScript and add CI coverage for stealth test typechecking.
|
|
8
|
+
|
|
9
|
+
- [#31](https://github.com/zorillajs/zorilla/pull/31) [`4c37634`](https://github.com/zorillajs/zorilla/commit/4c37634704c8f412e97d90a54d810cbd6aa38c42) Thanks [@JustinBeckwith](https://github.com/JustinBeckwith)! - Update documentation across all packages to use correct `@zorilla` scoped package names in installation instructions, titles, and npm badges. Also fix GitHub workflow status badges to point to the `main` branch instead of `master`.
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [[`4c37634`](https://github.com/zorillajs/zorilla/commit/4c37634704c8f412e97d90a54d810cbd6aa38c42)]:
|
|
12
|
+
- @zorilla/puppeteer-extra-plugin@1.0.2
|
|
13
|
+
- @zorilla/puppeteer-extra-plugin-user-preferences@1.0.2
|
|
14
|
+
|
|
3
15
|
## 1.0.2
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# puppeteer-extra-plugin-stealth [ [](https://www.npmjs.com/package/@zorilla/puppeteer-extra-plugin-stealth)
|
|
2
2
|
|
|
3
|
-
> A plugin for [`puppeteer-extra`](https://github.com/zorillajs/zorilla/tree/
|
|
3
|
+
> A plugin for [`puppeteer-extra`](https://github.com/zorillajs/zorilla/tree/main/packages/puppeteer-extra) and [`playwright-extra`](https://github.com/zorillajs/zorilla/tree/main/packages/playwright-extra) to prevent detection.
|
|
4
4
|
|
|
5
5
|
<p align="center"><img src="https://i.imgur.com/q2xBjqH.png" /></p>
|
|
6
6
|
|
|
@@ -39,7 +39,7 @@ puppeteer.launch({ headless: true }).then(async browser => {
|
|
|
39
39
|
})
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
> Please check out the [puppeteer-extra package](https://github.com/zorillajs/zorilla/tree/
|
|
42
|
+
> Please check out the [puppeteer-extra package](https://github.com/zorillajs/zorilla/tree/main/packages/puppeteer-extra) to learn more about `puppeteer-extra` (Firefox usage, other Plugins, etc).
|
|
43
43
|
|
|
44
44
|
## Status
|
|
45
45
|
|
|
@@ -98,7 +98,7 @@ Using stealth also seems to help with maintaining a normal [reCAPTCHA v3 score](
|
|
|
98
98
|
|
|
99
99
|
Note: The [official test](https://recaptcha-demo.appspot.com/recaptcha-v3-request-scores.php) is to be taken with a grain of salt, as the score is calculated individually per site and multiple other factors (past behaviour, IP address, etc). Based on anecdotal observations it still seems to work as a rough indicator.
|
|
100
100
|
|
|
101
|
-
_**Tip:** Have a look at the [recaptcha plugin](https://github.com/zorillajs/zorilla/tree/
|
|
101
|
+
_**Tip:** Have a look at the [recaptcha plugin](https://github.com/zorillajs/zorilla/tree/main/packages/puppeteer-extra-plugin-recaptcha) if you have issues with reCAPTCHAs._
|
|
102
102
|
|
|
103
103
|
## API
|
|
104
104
|
|
|
@@ -27,7 +27,7 @@ class Plugin extends PuppeteerExtraPlugin {
|
|
|
27
27
|
cdpSession.send = async function (method, paramArgs) {
|
|
28
28
|
const next = async () => {
|
|
29
29
|
try {
|
|
30
|
-
return await originalSend(method, paramArgs);
|
|
30
|
+
return (await originalSend(method, paramArgs));
|
|
31
31
|
}
|
|
32
32
|
catch (error) {
|
|
33
33
|
// This seems to happen sometimes when redirects cause other outstanding requests to be cut short
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/evasions/sourceurl/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAcvE;;;GAGG;AACH,MAAM,MAAO,SAAQ,oBAAoB;IACvC,YAAY,OAAgC,EAAE;QAC5C,KAAK,CAAC,IAAI,CAAC,CAAC;IACd,CAAC;IAED,IAAa,IAAI;QACf,OAAO,4BAA4B,CAAC;IACtC,CAAC;IAEQ,KAAK,CAAC,aAAa,CAAC,IAAU;QACrC,MAAM,cAAc,GAAG,IAAsB,CAAC;QAC9C,MAAM,MAAM,GACV,cAAc,IAAI,OAAO,cAAc,CAAC,OAAO,KAAK,UAAU;YAC5D,CAAC,CAAC,cAAc,CAAC,OAAO,EAAE;YAC1B,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC;QAC7B,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,IAAI,CAAC,KAAK,CAAC,+CAA+C,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;YACtE,OAAO;QACT,CAAC;QAED,yEAAyE;QACzE,wHAAwH;QACxH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,UAAU,GAAG,MAAoB,CAAC;QACxC,MAAM,YAAY,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAEtD,UAAU,CAAC,IAAI,GAAG,KAAK,WAErB,MAAc,EACd,SAAmC;YAEnC,MAAM,IAAI,GAAG,KAAK,IAAgB,EAAE;gBAClC,IAAI,CAAC;oBACH,OAAO,MAAM,YAAY,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/evasions/sourceurl/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAcvE;;;GAGG;AACH,MAAM,MAAO,SAAQ,oBAAoB;IACvC,YAAY,OAAgC,EAAE;QAC5C,KAAK,CAAC,IAAI,CAAC,CAAC;IACd,CAAC;IAED,IAAa,IAAI;QACf,OAAO,4BAA4B,CAAC;IACtC,CAAC;IAEQ,KAAK,CAAC,aAAa,CAAC,IAAU;QACrC,MAAM,cAAc,GAAG,IAAsB,CAAC;QAC9C,MAAM,MAAM,GACV,cAAc,IAAI,OAAO,cAAc,CAAC,OAAO,KAAK,UAAU;YAC5D,CAAC,CAAC,cAAc,CAAC,OAAO,EAAE;YAC1B,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC;QAC7B,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,IAAI,CAAC,KAAK,CAAC,+CAA+C,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;YACtE,OAAO;QACT,CAAC;QAED,yEAAyE;QACzE,wHAAwH;QACxH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,UAAU,GAAG,MAAoB,CAAC;QACxC,MAAM,YAAY,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAEtD,UAAU,CAAC,IAAI,GAAG,KAAK,WAErB,MAAc,EACd,SAAmC;YAEnC,MAAM,IAAI,GAAG,KAAK,IAAgB,EAAE;gBAClC,IAAI,CAAC;oBACH,OAAO,CAAC,MAAM,YAAY,CAAC,MAAM,EAAE,SAAS,CAAC,CAAM,CAAC;gBACtD,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,iGAAiG;oBACjG,IACE,KAAK,YAAY,KAAK;wBACtB,KAAK,CAAC,OAAO,CAAC,QAAQ,CACpB,mFAAmF,CACpF,EACD,CAAC;wBACD,KAAK,CACH,+DAA+D,EAC/D,EAAE,KAAK,EAAE,CACV,CAAC;wBACF,OAAO,SAAc,CAAC;oBACxB,CAAC;yBAAM,CAAC;wBACN,MAAM,KAAK,CAAC;oBACd,CAAC;gBACH,CAAC;YACH,CAAC,CAAC;YAEF,IAAI,CAAC,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;gBAC1B,OAAO,IAAI,EAAE,CAAC;YAChB,CAAC;YAED,sEAAsE;YACtE,uFAAuF;YACvF,MAAM,cAAc,GAA2B;gBAC7C,kBAAkB,EAAE,YAAY;gBAChC,wBAAwB,EAAE,qBAAqB;aAChD,CAAC;YACF,MAAM,iBAAiB,GAAG,+CAA+C,CAAC;YAE1E,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;gBAClE,OAAO,IAAI,EAAE,CAAC;YAChB,CAAC;YAED,KAAK,CAAC,qBAAqB,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;YACzC,MAAM,QAAQ,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;YACxC,IAAI,OAAO,SAAS,CAAC,QAAQ,CAAC,KAAK,QAAQ,EAAE,CAAC;gBAC5C,SAAS,CAAC,QAAQ,CAAC,GAAI,SAAS,CAAC,QAAQ,CAAY,CAAC,OAAO,CAC3D,iBAAiB,EACjB,EAAE,CACH,CAAC;YACJ,CAAC;YAED,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC,CAAC;IACJ,CAAC;CACF;AAED,MAAM,CAAC,OAAO,WAAW,YAAsC;IAC7D,OAAO,IAAI,MAAM,CAAC,YAAY,CAAC,CAAC;AAClC,CAAC"}
|
|
@@ -49,7 +49,7 @@ class Plugin extends PuppeteerExtraPlugin {
|
|
|
49
49
|
): Promise<T> {
|
|
50
50
|
const next = async (): Promise<T> => {
|
|
51
51
|
try {
|
|
52
|
-
return await originalSend
|
|
52
|
+
return (await originalSend(method, paramArgs)) as T;
|
|
53
53
|
} catch (error) {
|
|
54
54
|
// This seems to happen sometimes when redirects cause other outstanding requests to be cut short
|
|
55
55
|
if (
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PuppeteerExtraPlugin } from '@zorilla/puppeteer-extra-plugin';
|
|
2
|
-
import type * as Puppeteer from '@zorilla/puppeteer-extra-plugin/
|
|
2
|
+
import type * as Puppeteer from '@zorilla/puppeteer-extra-plugin/puppeteer';
|
|
3
3
|
interface PluginOptions {
|
|
4
4
|
enabledEvasions?: Set<string>;
|
|
5
5
|
availableEvasions?: Set<string>;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,KAAK,SAAS,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,KAAK,SAAS,MAAM,2CAA2C,CAAC;AAE5E,UAAU,aAAa;IACrB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,iBAAiB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAChC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,UAAU,cAAc;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AACH,cAAM,aAAc,SAAQ,oBAAoB;gBAClC,IAAI,GAAE,aAAkB;IAIpC,IAAa,IAAI,IAAI,MAAM,CAE1B;IAED,IAAa,QAAQ,IAAI,QAAQ,CAAC,aAAa,CAAC,CAyB/C;IAED;;;;OAIG;IACH,IAAa,YAAY,IAAI,GAAG,CAAC,MAAM,CAAC,CAGvC;IAED;;;;;;;;;;;;;OAaG;IACH,IAAI,iBAAiB,IAAI,GAAG,CAAC,MAAM,CAAC,CAEnC;IAED;;;;;;;;;;;;;;OAcG;IACH,IAAI,eAAe,IAAI,GAAG,CAAC,MAAM,CAAC,CAEjC;IAED;;OAEG;IACH,IAAI,eAAe,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,EAExC;IAEc,SAAS,CACtB,OAAO,EAAE,SAAS,CAAC,OAAO,EAC1B,KAAK,EAAE,cAAc,GACpB,OAAO,CAAC,IAAI,CAAC;CAUjB;AAED;;;;;GAKG;yBACa,OAAO,aAAa,KAAG,aAAa;AAApD,wBAAgF;AAGhF,OAAO,EAAE,aAAa,EAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zorilla/puppeteer-extra-plugin-stealth",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Stealth mode: Applies various techniques to make detection of headless puppeteer harder.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -37,16 +37,16 @@
|
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "^24.10.9",
|
|
39
39
|
"fpcollect": "^1.0.5",
|
|
40
|
-
"fpscanner": "^0.
|
|
40
|
+
"fpscanner": "^1.0.2",
|
|
41
41
|
"loop": "^3.3.6",
|
|
42
|
-
"puppeteer": "^24.
|
|
43
|
-
"vitest": "^4.
|
|
44
|
-
"@zorilla/puppeteer-extra": "1.0.
|
|
42
|
+
"puppeteer": "^24.40.0",
|
|
43
|
+
"vitest": "^4.1.2",
|
|
44
|
+
"@zorilla/puppeteer-extra": "1.0.2"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"debug": "^4.4.3",
|
|
48
|
-
"@zorilla/puppeteer-extra-plugin": "1.0.
|
|
49
|
-
"@zorilla/puppeteer-extra-plugin-user-preferences": "1.0.
|
|
48
|
+
"@zorilla/puppeteer-extra-plugin": "1.0.2",
|
|
49
|
+
"@zorilla/puppeteer-extra-plugin-user-preferences": "1.0.2"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"playwright-extra": "*",
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
},
|
|
63
63
|
"scripts": {
|
|
64
64
|
"build": "tsc && node scripts/fix-imports.js",
|
|
65
|
+
"typecheck": "tsc --project tsconfig.test.json --noEmit",
|
|
65
66
|
"test": "vitest run",
|
|
66
67
|
"test:watch": "vitest",
|
|
67
68
|
"lint": "eslint --ext .js,.ts ."
|
|
@@ -49,7 +49,7 @@ class Plugin extends PuppeteerExtraPlugin {
|
|
|
49
49
|
): Promise<T> {
|
|
50
50
|
const next = async (): Promise<T> => {
|
|
51
51
|
try {
|
|
52
|
-
return await originalSend
|
|
52
|
+
return (await originalSend(method, paramArgs)) as T;
|
|
53
53
|
} catch (error) {
|
|
54
54
|
// This seems to happen sometimes when redirects cause other outstanding requests to be cut short
|
|
55
55
|
if (
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PuppeteerExtraPlugin } from '@zorilla/puppeteer-extra-plugin';
|
|
2
|
-
import type * as Puppeteer from '@zorilla/puppeteer-extra-plugin/
|
|
2
|
+
import type * as Puppeteer from '@zorilla/puppeteer-extra-plugin/puppeteer';
|
|
3
3
|
|
|
4
4
|
interface PluginOptions {
|
|
5
5
|
enabledEvasions?: Set<string>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import 'puppeteer';
|
|
2
|
+
|
|
3
|
+
declare global {
|
|
4
|
+
const fpCollect: {
|
|
5
|
+
generateFingerprint<
|
|
6
|
+
TFingerprint extends Record<string, unknown>,
|
|
7
|
+
>(): TFingerprint;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const chrome: Record<string, unknown>;
|
|
11
|
+
|
|
12
|
+
interface Navigator {
|
|
13
|
+
__proto__?: unknown;
|
|
14
|
+
languages: string[];
|
|
15
|
+
userAgentData?: {
|
|
16
|
+
getHighEntropyValues(hints: string[]): Promise<Record<string, unknown>>;
|
|
17
|
+
[key: string]: unknown;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface Window {
|
|
22
|
+
chrome?: Record<string, unknown>;
|
|
23
|
+
hcaptcha?: {
|
|
24
|
+
render(...args: unknown[]): unknown;
|
|
25
|
+
execute(...args: unknown[]): unknown;
|
|
26
|
+
};
|
|
27
|
+
HTMLMediaElement: typeof HTMLMediaElement;
|
|
28
|
+
Function: FunctionConstructor;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface MimeType {
|
|
32
|
+
__proto__?: any;
|
|
33
|
+
enabledPlugins?: unknown;
|
|
34
|
+
length?: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface MimeTypeArray {
|
|
38
|
+
__proto__?: any;
|
|
39
|
+
[key: string]: any;
|
|
40
|
+
item(index: number | string | null): any;
|
|
41
|
+
namedItem(name: string): any;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface Plugin {
|
|
45
|
+
__proto__?: any;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface PluginArray {
|
|
49
|
+
__proto__?: any;
|
|
50
|
+
[key: string]: any;
|
|
51
|
+
item(index: number | string | null): any;
|
|
52
|
+
namedItem(name: string): any;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface Element {
|
|
56
|
+
innerText: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface RenderingContext {
|
|
60
|
+
getExtension(name: string): any;
|
|
61
|
+
getParameter(parameter?: any): any;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
declare module 'puppeteer' {
|
|
66
|
+
interface Page {
|
|
67
|
+
_client?:
|
|
68
|
+
| (() => {
|
|
69
|
+
send(
|
|
70
|
+
method: string,
|
|
71
|
+
params?: Record<string, unknown>
|
|
72
|
+
): Promise<unknown>;
|
|
73
|
+
})
|
|
74
|
+
| {
|
|
75
|
+
send(
|
|
76
|
+
method: string,
|
|
77
|
+
params?: Record<string, unknown>
|
|
78
|
+
): Promise<unknown>;
|
|
79
|
+
};
|
|
80
|
+
waitFor?(ms: number): Promise<void>;
|
|
81
|
+
waitForTimeout?(ms: number): Promise<void>;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export {};
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
compareLooseVersionStrings,
|
|
6
6
|
getDefaultLaunchArgs,
|
|
7
7
|
vanillaPuppeteer,
|
|
8
|
-
} from './util
|
|
8
|
+
} from './util';
|
|
9
9
|
|
|
10
10
|
// Fix CI issues with old versions
|
|
11
11
|
const isOldPuppeteerVersion = () => {
|
|
@@ -41,15 +41,15 @@ test.skip('stealth: will pass Paul Irish (requires user-preferences)', async ()
|
|
|
41
41
|
});
|
|
42
42
|
|
|
43
43
|
async function detectHeadless() {
|
|
44
|
-
const results = {};
|
|
44
|
+
const results: Record<string, boolean> = {};
|
|
45
45
|
|
|
46
|
-
async function test(name, fn) {
|
|
46
|
+
async function test(name: string, fn: () => boolean | Promise<boolean>) {
|
|
47
47
|
const detectionPassed = await fn();
|
|
48
48
|
if (detectionPassed) console.log(`Chrome headless detected via ${name}`);
|
|
49
49
|
results[name] = detectionPassed;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
await test('userAgent',
|
|
52
|
+
await test('userAgent', () => {
|
|
53
53
|
return /HeadlessChrome/.test(window.navigator.userAgent);
|
|
54
54
|
});
|
|
55
55
|
|
|
@@ -57,29 +57,30 @@ async function detectHeadless() {
|
|
|
57
57
|
if (
|
|
58
58
|
(await compareLooseVersionStrings(navigator.userAgent, '89.0.4339.0')) >= 0
|
|
59
59
|
) {
|
|
60
|
-
await test('navigator.webdriver is not false',
|
|
60
|
+
await test('navigator.webdriver is not false', () => {
|
|
61
61
|
return navigator.webdriver !== false;
|
|
62
62
|
});
|
|
63
63
|
} else {
|
|
64
64
|
// Detects the --enable-automation || --headless flags
|
|
65
65
|
// Will return true in headful if --enable-automation is provided
|
|
66
|
-
await test('navigator.webdriver present',
|
|
66
|
+
await test('navigator.webdriver present', () => {
|
|
67
67
|
return 'webdriver' in navigator;
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
-
await test('navigator.webdriver not undefined',
|
|
70
|
+
await test('navigator.webdriver not undefined', () => {
|
|
71
71
|
return navigator.webdriver !== undefined;
|
|
72
72
|
});
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
await test('navigator.webdriver property overridden', _ => {
|
|
74
|
+
await test('navigator.webdriver property overridden', () => {
|
|
76
75
|
return (
|
|
77
|
-
Object.getOwnPropertyDescriptor(
|
|
78
|
-
|
|
76
|
+
Object.getOwnPropertyDescriptor(
|
|
77
|
+
Object.getPrototypeOf(navigator),
|
|
78
|
+
'webdriver'
|
|
79
|
+
) !== undefined
|
|
79
80
|
);
|
|
80
81
|
});
|
|
81
82
|
|
|
82
|
-
await test('navigator.webdriver prop detected',
|
|
83
|
+
await test('navigator.webdriver prop detected', () => {
|
|
83
84
|
for (const prop in navigator) {
|
|
84
85
|
if (prop === 'webdriver') {
|
|
85
86
|
return true;
|
|
@@ -89,11 +90,11 @@ async function detectHeadless() {
|
|
|
89
90
|
});
|
|
90
91
|
}
|
|
91
92
|
|
|
92
|
-
await test('window.chrome missing',
|
|
93
|
+
await test('window.chrome missing', () => {
|
|
93
94
|
return /Chrome/.test(window.navigator.userAgent) && !window.chrome;
|
|
94
95
|
});
|
|
95
96
|
|
|
96
|
-
await test('permissions API', async
|
|
97
|
+
await test('permissions API', async () => {
|
|
97
98
|
const permissionStatus = await navigator.permissions.query({
|
|
98
99
|
name: 'notifications',
|
|
99
100
|
});
|
|
@@ -103,7 +104,7 @@ async function detectHeadless() {
|
|
|
103
104
|
);
|
|
104
105
|
});
|
|
105
106
|
|
|
106
|
-
await test('permissions API overriden',
|
|
107
|
+
await test('permissions API overriden', () => {
|
|
107
108
|
const permissions = window.navigator.permissions;
|
|
108
109
|
if (permissions.query.toString() !== 'function query() { [native code] }')
|
|
109
110
|
return true;
|
|
@@ -121,15 +122,15 @@ async function detectHeadless() {
|
|
|
121
122
|
if (Object.hasOwn(permissions, 'query')) return true; // eslint-disable-line
|
|
122
123
|
});
|
|
123
124
|
|
|
124
|
-
await test('navigator.plugins empty',
|
|
125
|
+
await test('navigator.plugins empty', () => {
|
|
125
126
|
return navigator.plugins.length === 0;
|
|
126
127
|
});
|
|
127
128
|
|
|
128
|
-
await test('navigator.languages blank',
|
|
129
|
-
return navigator.languages ===
|
|
129
|
+
await test('navigator.languages blank', () => {
|
|
130
|
+
return navigator.languages.length === 0;
|
|
130
131
|
});
|
|
131
132
|
|
|
132
|
-
await test('iFrame for fresh window object',
|
|
133
|
+
await test('iFrame for fresh window object', () => {
|
|
133
134
|
// evaluateOnNewDocument scripts don't apply within [srcdoc] (or [sandbox]) iframes
|
|
134
135
|
// https://github.com/GoogleChrome/puppeteer/issues/1106#issuecomment-359313898
|
|
135
136
|
const iframe = document.createElement('iframe');
|
|
@@ -156,7 +157,7 @@ async function detectHeadless() {
|
|
|
156
157
|
|
|
157
158
|
// This detects that a devtools protocol agent is attached.
|
|
158
159
|
// So it will also pass true in headful Chrome if the devtools window is attached
|
|
159
|
-
await test('toString',
|
|
160
|
+
await test('toString', () => {
|
|
160
161
|
let gotYou = 0;
|
|
161
162
|
const spooky = /./;
|
|
162
163
|
spooky.toString = () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { expect, test } from 'vitest';
|
|
2
2
|
import utils from '../../../src/evasions/_utils/index.js';
|
|
3
3
|
import withUtils from '../../../src/evasions/_utils/withUtils.js';
|
|
4
|
-
import { getDefaultLaunchArgs, vanillaPuppeteer } from '../../util
|
|
4
|
+
import { getDefaultLaunchArgs, vanillaPuppeteer } from '../../util';
|
|
5
5
|
|
|
6
6
|
/* global HTMLMediaElement WebGLRenderingContext */
|
|
7
7
|
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import { expect, test } from 'vitest';
|
|
2
2
|
import Plugin from '../../../src/evasions/chrome.app/index.js';
|
|
3
|
-
import {
|
|
4
|
-
addExtra,
|
|
5
|
-
getDefaultLaunchArgs,
|
|
6
|
-
vanillaPuppeteer,
|
|
7
|
-
} from '../../util.js';
|
|
3
|
+
import { addExtra, getDefaultLaunchArgs, vanillaPuppeteer } from '../../util';
|
|
8
4
|
|
|
9
5
|
/* global chrome */
|
|
10
6
|
|
|
@@ -17,30 +13,53 @@ test('stealth: will add convincing chrome.app object', async () => {
|
|
|
17
13
|
const page = await browser.newPage();
|
|
18
14
|
|
|
19
15
|
const results = await page.evaluate(() => {
|
|
20
|
-
|
|
16
|
+
type ChromeApp = {
|
|
17
|
+
getDetails: () => null;
|
|
18
|
+
getIsInstalled: () => boolean;
|
|
19
|
+
runningState: () => string;
|
|
20
|
+
InstallState: Record<string, string>;
|
|
21
|
+
RunningState: Record<string, string>;
|
|
22
|
+
toString: () => string;
|
|
23
|
+
};
|
|
24
|
+
type ErrorResult = {
|
|
25
|
+
name: string;
|
|
26
|
+
message: string;
|
|
27
|
+
stack: string;
|
|
28
|
+
};
|
|
29
|
+
const chromeApp = (window.chrome as { app: ChromeApp }).app;
|
|
30
|
+
|
|
31
|
+
const catchErr = (
|
|
32
|
+
fn: (...args: unknown[]) => unknown,
|
|
33
|
+
...args: unknown[]
|
|
34
|
+
): ErrorResult => {
|
|
21
35
|
try {
|
|
22
|
-
|
|
36
|
+
fn(...args);
|
|
37
|
+
return { name: '', message: '', stack: '' };
|
|
23
38
|
} catch ({ name, message, stack }) {
|
|
24
|
-
return {
|
|
39
|
+
return {
|
|
40
|
+
name: String(name),
|
|
41
|
+
message: String(message),
|
|
42
|
+
stack: String(stack),
|
|
43
|
+
};
|
|
25
44
|
}
|
|
26
45
|
};
|
|
27
46
|
|
|
28
47
|
return {
|
|
29
48
|
app: {
|
|
30
49
|
exists: window.chrome && 'app' in window.chrome,
|
|
31
|
-
toString:
|
|
32
|
-
deepToString:
|
|
50
|
+
toString: chromeApp.toString(),
|
|
51
|
+
deepToString: chromeApp.getDetails.toString(),
|
|
33
52
|
},
|
|
34
53
|
data: {
|
|
35
|
-
getIsInstalled:
|
|
36
|
-
runningState:
|
|
37
|
-
getDetails:
|
|
38
|
-
InstallState:
|
|
39
|
-
RunningState:
|
|
54
|
+
getIsInstalled: chromeApp.getIsInstalled(),
|
|
55
|
+
runningState: chromeApp.runningState(),
|
|
56
|
+
getDetails: chromeApp.getDetails(),
|
|
57
|
+
InstallState: chromeApp.InstallState,
|
|
58
|
+
RunningState: chromeApp.RunningState,
|
|
40
59
|
},
|
|
41
60
|
errors: {
|
|
42
|
-
getIsInstalled: catchErr(
|
|
43
|
-
stackOK: !catchErr(
|
|
61
|
+
getIsInstalled: catchErr(chromeApp.getIsInstalled, 'foo').message,
|
|
62
|
+
stackOK: !catchErr(chromeApp.getIsInstalled, 'foo').stack.includes(
|
|
44
63
|
'at getIsInstalled'
|
|
45
64
|
),
|
|
46
65
|
},
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import { expect, test } from 'vitest';
|
|
2
2
|
import Plugin from '../../../src/evasions/chrome.csi/index.js';
|
|
3
|
-
import {
|
|
4
|
-
addExtra,
|
|
5
|
-
getDefaultLaunchArgs,
|
|
6
|
-
vanillaPuppeteer,
|
|
7
|
-
} from '../../util.js';
|
|
3
|
+
import { addExtra, getDefaultLaunchArgs, vanillaPuppeteer } from '../../util';
|
|
8
4
|
|
|
9
5
|
/* global chrome */
|
|
10
6
|
|
|
@@ -21,13 +17,20 @@ test('stealth: will add functional chrome.csi function mock', async () => {
|
|
|
21
17
|
const page = await browser.newPage();
|
|
22
18
|
|
|
23
19
|
const results = await page.evaluate(() => {
|
|
20
|
+
type ChromeCsi = {
|
|
21
|
+
onloadT: number;
|
|
22
|
+
startE: number;
|
|
23
|
+
pageT: number;
|
|
24
|
+
tran: number;
|
|
25
|
+
};
|
|
26
|
+
const chromeCsi = (window.chrome as { csi: () => ChromeCsi }).csi;
|
|
24
27
|
const { timing } = window.performance;
|
|
25
|
-
const csi =
|
|
28
|
+
const csi = chromeCsi();
|
|
26
29
|
|
|
27
30
|
return {
|
|
28
31
|
csi: {
|
|
29
32
|
exists: window.chrome && 'csi' in window.chrome,
|
|
30
|
-
toString:
|
|
33
|
+
toString: chromeCsi.toString(),
|
|
31
34
|
},
|
|
32
35
|
dataOK: {
|
|
33
36
|
onloadT: csi.onloadT === timing.domContentLoadedEventEnd,
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import { expect, test } from 'vitest';
|
|
2
2
|
import Plugin from '../../../src/evasions/chrome.loadTimes/index.js';
|
|
3
|
-
import {
|
|
4
|
-
addExtra,
|
|
5
|
-
getDefaultLaunchArgs,
|
|
6
|
-
vanillaPuppeteer,
|
|
7
|
-
} from '../../util.js';
|
|
3
|
+
import { addExtra, getDefaultLaunchArgs, vanillaPuppeteer } from '../../util';
|
|
8
4
|
|
|
9
5
|
/* global chrome */
|
|
10
6
|
|
|
@@ -17,12 +13,18 @@ test('stealth: will add functional chrome.loadTimes function mock', async () =>
|
|
|
17
13
|
const page = await browser.newPage();
|
|
18
14
|
|
|
19
15
|
const results = await page.evaluate(() => {
|
|
20
|
-
|
|
16
|
+
type ChromeLoadTimes = Record<string, unknown>;
|
|
17
|
+
const chromeLoadTimes = (
|
|
18
|
+
window.chrome as {
|
|
19
|
+
loadTimes: () => ChromeLoadTimes;
|
|
20
|
+
}
|
|
21
|
+
).loadTimes;
|
|
22
|
+
const loadTimes = chromeLoadTimes();
|
|
21
23
|
|
|
22
24
|
return {
|
|
23
25
|
loadTimes: {
|
|
24
26
|
exists: window.chrome && 'loadTimes' in window.chrome,
|
|
25
|
-
toString:
|
|
27
|
+
toString: chromeLoadTimes.toString(),
|
|
26
28
|
},
|
|
27
29
|
dataOK: {
|
|
28
30
|
connectionInfo: 'connectionInfo' in loadTimes,
|
|
@@ -2,11 +2,11 @@ import { expect, test } from 'vitest';
|
|
|
2
2
|
import Plugin, {
|
|
3
3
|
argsToIgnore,
|
|
4
4
|
} from '../../../src/evasions/defaultArgs/index.js';
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
5
|
+
import { addExtra, getDefaultLaunchArgs, vanillaPuppeteer } from '../../util';
|
|
6
|
+
|
|
7
|
+
type BrowserCommandLineResponse = {
|
|
8
|
+
arguments: string[];
|
|
9
|
+
};
|
|
10
10
|
|
|
11
11
|
test('vanilla: uses args to ignore', async () => {
|
|
12
12
|
const browser = await vanillaPuppeteer.launch({
|
|
@@ -16,9 +16,9 @@ test('vanilla: uses args to ignore', async () => {
|
|
|
16
16
|
const page = await browser.newPage();
|
|
17
17
|
const client =
|
|
18
18
|
typeof page._client === 'function' ? page._client() : page._client;
|
|
19
|
-
const { arguments: launchArgs } = await client.send(
|
|
19
|
+
const { arguments: launchArgs } = (await client.send(
|
|
20
20
|
'Browser.getBrowserCommandLine'
|
|
21
|
-
);
|
|
21
|
+
)) as BrowserCommandLineResponse;
|
|
22
22
|
const ok = argsToIgnore.every(arg => launchArgs.includes(arg));
|
|
23
23
|
if (!ok) {
|
|
24
24
|
console.log({ argsToIgnore, launchArgs });
|
|
@@ -35,9 +35,9 @@ test('stealth: does not use args to ignore', async () => {
|
|
|
35
35
|
const page = await browser.newPage();
|
|
36
36
|
const client =
|
|
37
37
|
typeof page._client === 'function' ? page._client() : page._client;
|
|
38
|
-
const { arguments: launchArgs } = await client.send(
|
|
38
|
+
const { arguments: launchArgs } = (await client.send(
|
|
39
39
|
'Browser.getBrowserCommandLine'
|
|
40
|
-
);
|
|
40
|
+
)) as BrowserCommandLineResponse;
|
|
41
41
|
const ok = argsToIgnore.every(arg => !launchArgs.includes(arg));
|
|
42
42
|
if (!ok) {
|
|
43
43
|
console.log({ argsToIgnore, launchArgs });
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
1
2
|
// import Plugin from '../../../src/evasions/iframe.contentWindow/index.js'
|
|
2
3
|
// NOTE: We're using the full plugin for testing here as `iframe.contentWindow` uses data set by `chrome.runtime`
|
|
3
4
|
import Plugin from '@zorilla/puppeteer-extra-plugin-stealth';
|
|
@@ -9,7 +10,7 @@ import {
|
|
|
9
10
|
getStealthFingerPrint,
|
|
10
11
|
getVanillaFingerPrint,
|
|
11
12
|
vanillaPuppeteer,
|
|
12
|
-
} from '../../util
|
|
13
|
+
} from '../../util';
|
|
13
14
|
|
|
14
15
|
// Fix CI issues with old versions
|
|
15
16
|
const isOldPuppeteerVersion = () => {
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
getStealthFingerPrint,
|
|
7
7
|
getVanillaFingerPrint,
|
|
8
8
|
vanillaPuppeteer,
|
|
9
|
-
} from '../../util
|
|
9
|
+
} from '../../util';
|
|
10
10
|
|
|
11
11
|
test.skip('vanilla: doesnt support proprietary codecs (requires fpcollect)', async () => {
|
|
12
12
|
const { videoCodecs, audioCodecs } = await getVanillaFingerPrint();
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
getStealthFingerPrint,
|
|
7
7
|
getVanillaFingerPrint,
|
|
8
8
|
vanillaPuppeteer,
|
|
9
|
-
} from '../../util
|
|
9
|
+
} from '../../util';
|
|
10
10
|
|
|
11
11
|
// TODO: Vanilla seems fine, evasion obsolete?
|
|
12
12
|
// Note: We keep it around for now, as we will need this method in a fingerprinting plugin later anyway
|
|
@@ -6,10 +6,20 @@ import {
|
|
|
6
6
|
getStealthFingerPrint,
|
|
7
7
|
getVanillaFingerPrint,
|
|
8
8
|
vanillaPuppeteer,
|
|
9
|
-
} from '../../util
|
|
9
|
+
} from '../../util';
|
|
10
|
+
|
|
11
|
+
type FingerPrintWithPlugins = {
|
|
12
|
+
plugins: {
|
|
13
|
+
length: number;
|
|
14
|
+
};
|
|
15
|
+
mimeTypes: {
|
|
16
|
+
length: number;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
10
19
|
|
|
11
20
|
test.skip('vanilla: empty plugins, empty mimetypes (requires fpcollect)', async () => {
|
|
12
|
-
const { plugins, mimeTypes } =
|
|
21
|
+
const { plugins, mimeTypes } =
|
|
22
|
+
await getVanillaFingerPrint<FingerPrintWithPlugins>();
|
|
13
23
|
expect(plugins.length).toBe(0);
|
|
14
24
|
expect(mimeTypes.length).toBe(0);
|
|
15
25
|
});
|
|
@@ -35,7 +45,8 @@ test('vanilla: will not have modifications', async () => {
|
|
|
35
45
|
});
|
|
36
46
|
|
|
37
47
|
test.skip('stealth: has plugin, has mimetypes (requires fpcollect)', async () => {
|
|
38
|
-
const { plugins, mimeTypes } =
|
|
48
|
+
const { plugins, mimeTypes } =
|
|
49
|
+
await getStealthFingerPrint<FingerPrintWithPlugins>(Plugin);
|
|
39
50
|
expect(plugins.length).toBe(3);
|
|
40
51
|
expect(mimeTypes.length).toBe(4);
|
|
41
52
|
});
|