@zorilla/puppeteer-extra-plugin-stealth 1.0.2 → 1.0.4

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +4 -4
  3. package/dist/evasions/defaultArgs/README.md +1 -1
  4. package/dist/evasions/media.codecs/README.md +2 -2
  5. package/dist/evasions/media.codecs/index.js +1 -1
  6. package/dist/evasions/media.codecs/index.js.map +1 -1
  7. package/dist/evasions/media.codecs/index.ts +1 -1
  8. package/dist/evasions/sourceurl/index.js +1 -1
  9. package/dist/evasions/sourceurl/index.js.map +1 -1
  10. package/dist/evasions/sourceurl/index.ts +1 -1
  11. package/dist/index.d.ts +1 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/package.json +8 -7
  14. package/src/evasions/defaultArgs/README.md +1 -1
  15. package/src/evasions/media.codecs/README.md +2 -2
  16. package/src/evasions/media.codecs/index.ts +1 -1
  17. package/src/evasions/sourceurl/index.ts +1 -1
  18. package/src/index.ts +1 -1
  19. package/test/ambient.d.ts +85 -0
  20. package/test/cat-and-mouse.test.ts +21 -20
  21. package/test/evasions/_utils/index.test.ts +1 -1
  22. package/test/evasions/chrome.app/index.test.ts +36 -17
  23. package/test/evasions/chrome.csi/index.test.ts +10 -7
  24. package/test/evasions/chrome.loadTimes/index.test.ts +9 -7
  25. package/test/evasions/chrome.runtime/index.test.ts +1 -1
  26. package/test/evasions/defaultArgs/index.test.ts +9 -9
  27. package/test/evasions/iframe.contentWindow/{index.test.js → index.test.ts} +2 -1
  28. package/test/evasions/media.codecs/{index.test.js → index.test.ts} +1 -1
  29. package/test/evasions/navigator.hardwareConcurrency/{index.test.js → index.test.ts} +1 -1
  30. package/test/evasions/navigator.languages/{index.test.js → index.test.ts} +1 -1
  31. package/test/evasions/navigator.permissions/{index.test.js → index.test.ts} +1 -1
  32. package/test/evasions/navigator.plugins/{index.test.js → index.test.ts} +14 -3
  33. package/test/evasions/navigator.plugins/{mimeTypes.test.js → mimeTypes.test.ts} +2 -6
  34. package/test/evasions/navigator.plugins/{plugins.test.js → plugins.test.ts} +1 -5
  35. package/test/evasions/navigator.vendor/{index.test.js → index.test.ts} +1 -5
  36. package/test/evasions/navigator.webdriver/{index.test.js → index.test.ts} +1 -1
  37. package/test/evasions/sourceurl/{index.test.js → index.test.ts} +1 -5
  38. package/test/evasions/user-agent-override/{index.test.js → index.test.ts} +1 -5
  39. package/test/evasions/webgl.vendor/{index.test.js → index.test.ts} +9 -4
  40. package/test/fpscanner.test.ts +114 -45
  41. package/test/index.test.ts +13 -5
  42. package/test/service-worker.test.ts +40 -19
  43. package/test/{util.js → util.ts} +45 -15
  44. package/tsconfig.json +1 -1
  45. package/tsconfig.test.json +23 -0
  46. package/tsconfig.tsbuildinfo +1 -1
  47. package/vitest.config.ts +8 -1
@@ -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
- addExtra,
7
- getDefaultLaunchArgs,
8
- vanillaPuppeteer,
9
- } from '../../util.js';
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.js';
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.js';
9
+ } from '../../util';
10
10
 
11
11
  test.skip('vanilla: doesnt support proprietary codecs (requires fpcollect)', async () => {
12
12
  const { videoCodecs, audioCodecs } = await getVanillaFingerPrint();
@@ -7,7 +7,7 @@ import {
7
7
  getStealthFingerPrint,
8
8
  getVanillaFingerPrint,
9
9
  vanillaPuppeteer,
10
- } from '../../util.js';
10
+ } from '../../util';
11
11
 
12
12
  const fingerprintFn = page => page.evaluate('navigator.hardwareConcurrency');
13
13
 
@@ -6,7 +6,7 @@ import {
6
6
  getStealthFingerPrint,
7
7
  getVanillaFingerPrint,
8
8
  vanillaPuppeteer,
9
- } from '../../util.js';
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
@@ -7,7 +7,7 @@ import {
7
7
  getStealthFingerPrint,
8
8
  getVanillaFingerPrint,
9
9
  vanillaPuppeteer,
10
- } from '../../util.js';
10
+ } from '../../util';
11
11
 
12
12
  test.skip('vanilla: is prompt (requires fpcollect)', async () => {
13
13
  const { permissions } = await getVanillaFingerPrint();
@@ -6,10 +6,20 @@ import {
6
6
  getStealthFingerPrint,
7
7
  getVanillaFingerPrint,
8
8
  vanillaPuppeteer,
9
- } from '../../util.js';
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 } = await getVanillaFingerPrint();
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 } = await getStealthFingerPrint(Plugin);
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
  });
@@ -1,10 +1,6 @@
1
1
  import { expect, test } from 'vitest';
2
2
  import Plugin from '../../../src/evasions/navigator.plugins/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
  test('stealth: will have convincing mimeTypes', async () => {
10
6
  const puppeteer = addExtra(vanillaPuppeteer).use(Plugin());
@@ -55,7 +51,7 @@ test('stealth: will have convincing mimeTypes', async () => {
55
51
  })(),
56
52
  loopResult: (() => {
57
53
  let res = '';
58
- for (var bK = 0; bK < window.navigator.mimeTypes.length; bK++)
54
+ for (let bK = 0; bK < window.navigator.mimeTypes.length; bK++)
59
55
  bK === window.navigator.mimeTypes.length - 1
60
56
  ? (res += window.navigator.mimeTypes[bK].type)
61
57
  : (res += window.navigator.mimeTypes[bK].type + ',');
@@ -1,10 +1,6 @@
1
1
  import { expect, test } from 'vitest';
2
2
  import Plugin from '../../../src/evasions/navigator.plugins/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
  test('stealth: will have convincing plugins', async () => {
10
6
  const puppeteer = addExtra(vanillaPuppeteer).use(Plugin());
@@ -1,10 +1,6 @@
1
1
  import { expect, test } from 'vitest';
2
2
  import Plugin from '../../../src/evasions/navigator.vendor/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
  test('vanilla: navigator.vendor is always Google Inc.', async () => {
10
6
  const browser = await vanillaPuppeteer.launch({
@@ -5,7 +5,7 @@ import {
5
5
  compareLooseVersionStrings,
6
6
  getDefaultLaunchArgs,
7
7
  vanillaPuppeteer,
8
- } from '../../util.js';
8
+ } from '../../util';
9
9
 
10
10
  function getExpectedValue(looseVersionString) {
11
11
  if (compareLooseVersionStrings(looseVersionString, '89.0.4339.0') >= 0) {
@@ -2,11 +2,7 @@ import path from 'node:path';
2
2
  import { fileURLToPath } from 'node:url';
3
3
  import { expect, test } from 'vitest';
4
4
  import Plugin from '../../../src/evasions/sourceurl/index.js';
5
- import {
6
- addExtra,
7
- getDefaultLaunchArgs,
8
- vanillaPuppeteer,
9
- } from '../../util.js';
5
+ import { addExtra, getDefaultLaunchArgs, vanillaPuppeteer } from '../../util';
10
6
 
11
7
  const __filename = fileURLToPath(import.meta.url);
12
8
  const __dirname = path.dirname(__filename);
@@ -1,10 +1,6 @@
1
1
  import { expect, test } from 'vitest';
2
2
  import Plugin from '../../../src/evasions/user-agent-override/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
  // Fixed since 2.1.1?
10
6
  // test('vanilla: Accept-Language header is missing', async () => {
@@ -6,7 +6,7 @@ import {
6
6
  getStealthFingerPrint,
7
7
  getVanillaFingerPrint,
8
8
  vanillaPuppeteer,
9
- } from '../../util.js';
9
+ } from '../../util';
10
10
 
11
11
  // FIXME: This changed in more recent chrome versions
12
12
  // test('vanilla: videoCard is Google Inc', async () => {
@@ -64,7 +64,7 @@ async function extendedTests() {
64
64
  // Make sure we not reveal our proxy through errors
65
65
  await test('errorOK', _ => {
66
66
  try {
67
- return context.getParameter();
67
+ return (context as WebGLRenderingContext).getParameter(undefined as any);
68
68
  } catch (err) {
69
69
  return !err.stack.includes(`at Object.apply`);
70
70
  }
@@ -118,8 +118,13 @@ test.skip('stealth: webgl is native (requires fpcollect)', async () => {
118
118
  * @see https://stackoverflow.com/questions/49267764/how-to-get-the-video-card-driver-name-using-javascript-browser-side
119
119
  * @returns {Object}
120
120
  */
121
- function getVideoCardInfo(context = 'webgl') {
122
- const gl = document.createElement('canvas').getContext(context);
121
+ function getVideoCardInfo(
122
+ context: 'webgl' | 'webgl2' | 'experimental-webgl' = 'webgl'
123
+ ) {
124
+ const gl = document.createElement('canvas').getContext(context) as
125
+ | WebGLRenderingContext
126
+ | WebGL2RenderingContext
127
+ | null;
123
128
  if (!gl) {
124
129
  return {
125
130
  error: 'no webgl',
@@ -1,54 +1,123 @@
1
- import fpscanner from 'fpscanner';
1
+ import { readFileSync } from 'node:fs';
2
+ import { createRequire } from 'node:module';
3
+ import type { Browser } from 'puppeteer';
2
4
  import { expect, test } from 'vitest';
3
5
  import Plugin from '../dist/index.js';
4
6
  import {
5
- compareLooseVersionStrings,
6
- getStealthFingerPrint,
7
- getVanillaFingerPrint,
8
- } from './util.js';
9
-
10
- // Fix CI issues with old versions
11
- const isOldPuppeteerVersion = () => {
12
- const version = process.env.PUPPETEER_VERSION;
13
- if (!version) {
14
- return false;
15
- }
16
- if (version === '1.9.0' || version === '1.6.2') {
17
- return true;
18
- }
19
- return false;
7
+ addExtra,
8
+ dummyHTMLPath,
9
+ getDefaultLaunchArgs,
10
+ vanillaPuppeteer,
11
+ } from './util';
12
+
13
+ const require = createRequire(import.meta.url);
14
+
15
+ // fpscanner v1.0.0 is a browser-side library — no longer a Node.js analyser.
16
+ // We inject the ES bundle into the page and expose the class as a window global,
17
+ // then call collectFingerprint() in the browser context to both collect and analyse.
18
+ const getFpScannerBrowserCode = (): string => {
19
+ const fpScannerPath = require.resolve(
20
+ 'fpscanner/dist/fpScanner.es.js'
21
+ ) as string;
22
+ const code = readFileSync(fpScannerPath, 'utf8');
23
+ // Replace `export { <name> as default };` with a window global assignment so
24
+ // the bundle can be injected as a regular (non-module) script tag.
25
+ return code.replace(/export \{([^}]+)\};?\s*$/, (_match, inner: string) => {
26
+ const name = inner.match(/(\w+)\s+as\s+default/)?.[1];
27
+ if (!name)
28
+ throw new Error('Could not find default export name in fpscanner bundle');
29
+ return `window.FingerprintScanner = ${name};`;
30
+ });
20
31
  };
21
32
 
22
- test.skip('vanilla: will fail multiple fpscanner tests (requires fpcollect build)', async () => {
23
- const fingerPrint = await getVanillaFingerPrint();
24
- const testedFingerPrints = fpscanner.analyseFingerprint(fingerPrint);
25
- const failedChecks = Object.values(testedFingerPrints).filter(
26
- val => val.consistent < 3
27
- );
28
-
29
- if (isOldPuppeteerVersion()) {
30
- expect(failedChecks.length).toBe(8);
31
- } else {
32
- expect(failedChecks.length).toBe(7);
33
- }
34
- });
33
+ const fpScannerCode = getFpScannerBrowserCode();
34
+ // GitHub's Windows runners can take over a minute to launch Chromium, inject
35
+ // fpscanner, and finish the vanilla baseline collection.
36
+ const fpscannerTestTimeout = process.platform === 'win32' ? 90000 : 30000;
35
37
 
36
- test.skip('stealth: will not fail a single fpscanner test (requires fpcollect build)', async () => {
37
- const fingerPrint = await getStealthFingerPrint(Plugin);
38
- const testedFingerPrints = fpscanner.analyseFingerprint(fingerPrint);
39
- const failedChecks = Object.values(testedFingerPrints).filter(
40
- val => val.consistent < 3
41
- );
38
+ type FastBotDetectionDetails = Record<
39
+ string,
40
+ { detected: boolean; severity: string }
41
+ >;
42
42
 
43
- if (failedChecks.length) {
44
- console.warn('The following fingerprints failed:', failedChecks);
45
- }
43
+ interface FpScannerResult {
44
+ fastBotDetection: boolean;
45
+ fastBotDetectionDetails: FastBotDetectionDetails;
46
+ }
47
+
48
+ interface LaunchablePuppeteer {
49
+ launch(options?: { headless?: boolean; args?: string[] }): Promise<Browser>;
50
+ }
46
51
 
47
- if (compareLooseVersionStrings(fingerPrint.userAgent, '89.0.4339.0') >= 0) {
48
- // Updated navigator.webdriver behavior breaks the fpscanner tests.
49
- expect(failedChecks.length).toBe(1);
50
- expect(failedChecks[0].name).toBe('WEBDRIVER');
51
- } else {
52
- expect(failedChecks.length).toBe(0);
52
+ const getFpScannerResult = async (
53
+ puppeteer: LaunchablePuppeteer
54
+ ): Promise<FpScannerResult> => {
55
+ const browser = await puppeteer.launch({
56
+ headless: true,
57
+ args: getDefaultLaunchArgs(),
58
+ });
59
+ try {
60
+ const page = await browser.newPage();
61
+ await page.goto(`file://${dummyHTMLPath}`);
62
+ await page.addScriptTag({ content: fpScannerCode });
63
+ const result = await page.evaluate(async () => {
64
+ // FingerprintScanner is injected as a browser global via addScriptTag above
65
+ const FP = (window as Record<string, unknown>)
66
+ .FingerprintScanner as new () => {
67
+ collectFingerprint(opts: { encrypt: boolean }): Promise<unknown>;
68
+ };
69
+ const scanner = new FP();
70
+ return scanner.collectFingerprint({ encrypt: false });
71
+ });
72
+ return result as FpScannerResult;
73
+ } finally {
74
+ await browser.close();
53
75
  }
54
- });
76
+ };
77
+
78
+ test(
79
+ 'vanilla: will fail multiple fpscanner bot detection checks',
80
+ async () => {
81
+ const result = await getFpScannerResult(vanillaPuppeteer);
82
+ const failedChecks = Object.entries(result.fastBotDetectionDetails)
83
+ .filter(([_name, val]) => val.detected)
84
+ .map(([name]) => name);
85
+
86
+ console.log('Vanilla failed checks:', failedChecks);
87
+ expect(result.fastBotDetection).toBe(true);
88
+ expect(failedChecks.length).toBeGreaterThan(0);
89
+ },
90
+ fpscannerTestTimeout
91
+ );
92
+
93
+ test(
94
+ 'stealth: will pass core fpscanner automation checks',
95
+ async () => {
96
+ // PuppeteerExtra has a compatible launch() interface — assertion is safe
97
+ const stealthPuppeteer = addExtra(vanillaPuppeteer).use(
98
+ Plugin()
99
+ ) as unknown as LaunchablePuppeteer;
100
+ const result = await getFpScannerResult(stealthPuppeteer);
101
+ const failedChecks = Object.entries(result.fastBotDetectionDetails)
102
+ .filter(([_name, val]) => val.detected)
103
+ .map(([name]) => name);
104
+
105
+ if (failedChecks.length) {
106
+ console.warn('The following checks failed:', failedChecks);
107
+ }
108
+
109
+ // These are the automation signals that the stealth plugin explicitly addresses.
110
+ // Stealth overrides navigator.webdriver → hasWebdriver should not fire.
111
+ expect(result.fastBotDetectionDetails.hasWebdriver.detected).toBe(false);
112
+ // Stealth injects chrome.* objects → hasMissingChromeObject should not fire.
113
+ expect(result.fastBotDetectionDetails.hasMissingChromeObject.detected).toBe(
114
+ false
115
+ );
116
+ // Neither Selenium nor Playwright markers are present in a Puppeteer session.
117
+ expect(result.fastBotDetectionDetails.hasSeleniumProperty.detected).toBe(
118
+ false
119
+ );
120
+ expect(result.fastBotDetectionDetails.hasPlaywright.detected).toBe(false);
121
+ },
122
+ fpscannerTestTimeout
123
+ );
@@ -4,18 +4,26 @@ import { expect, test } from 'vitest';
4
4
 
5
5
  import Plugin from '../dist/index.js';
6
6
 
7
+ type StealthPluginInstance = ReturnType<typeof Plugin> & {
8
+ opts: {
9
+ enabledEvasions: Set<string>;
10
+ };
11
+ availableEvasions: Set<string>;
12
+ dependencies: Set<string>;
13
+ };
14
+
7
15
  test('is a function', async () => {
8
16
  expect(typeof Plugin).toBe('function');
9
17
  });
10
18
 
11
19
  test('should have the basic class members', async () => {
12
- const instance = Plugin();
20
+ const instance = Plugin() as StealthPluginInstance;
13
21
  expect(instance.name).toBe(PLUGIN_NAME);
14
22
  expect(instance._isPuppeteerExtraPlugin).toBe(true);
15
23
  });
16
24
 
17
25
  test('should have the public child class members', async () => {
18
- const instance = Plugin();
26
+ const instance = Plugin() as StealthPluginInstance;
19
27
  const prototype = Object.getPrototypeOf(instance);
20
28
  const childClassMembers = Object.getOwnPropertyNames(prototype);
21
29
 
@@ -29,12 +37,12 @@ test('should have the public child class members', async () => {
29
37
  });
30
38
 
31
39
  test('should have opts with default values', async () => {
32
- const instance = Plugin();
40
+ const instance = Plugin() as StealthPluginInstance;
33
41
  expect(instance.opts.enabledEvasions).toEqual(instance.availableEvasions);
34
42
  });
35
43
 
36
44
  test('should add all dependencies dynamically', async () => {
37
- const instance = Plugin();
45
+ const instance = Plugin() as StealthPluginInstance;
38
46
  const deps = new Set(
39
47
  [...instance.opts.enabledEvasions].map(e => `${PLUGIN_NAME}/evasions/${e}`)
40
48
  );
@@ -42,7 +50,7 @@ test('should add all dependencies dynamically', async () => {
42
50
  });
43
51
 
44
52
  test('should add all dependencies dynamically including changes', async () => {
45
- const instance = Plugin();
53
+ const instance = Plugin() as StealthPluginInstance;
46
54
  const fakeDep = 'foobar';
47
55
  instance.enabledEvasions = new Set([fakeDep]);
48
56
  expect(instance.dependencies).toEqual(
@@ -1,10 +1,12 @@
1
1
  import fs from 'node:fs';
2
2
  import http from 'node:http';
3
+ import type { AddressInfo } from 'node:net';
3
4
  import path from 'node:path';
4
5
  import { fileURLToPath } from 'node:url';
6
+ import type { Browser, Page, Target, WebWorker } from 'puppeteer';
5
7
  import { afterAll, beforeAll, expect, test } from 'vitest';
6
8
  import Plugin from '../dist/index.js';
7
- import { addExtra, getDefaultLaunchArgs, vanillaPuppeteer } from './util.js';
9
+ import { addExtra, getDefaultLaunchArgs, vanillaPuppeteer } from './util';
8
10
 
9
11
  const __filename = fileURLToPath(import.meta.url);
10
12
  const __dirname = path.dirname(__filename);
@@ -31,10 +33,13 @@ const httpServer = async () => {
31
33
  })
32
34
  .listen(0); // random free port
33
35
 
34
- return `http://127.0.0.1:${server.address().port}/`;
36
+ const address = server.address() as AddressInfo;
37
+ return `http://127.0.0.1:${address.port}/`;
35
38
  };
36
39
 
37
- let browser, page, worker;
40
+ let browser: Browser;
41
+ let page: Page;
42
+ let worker: WebWorker;
38
43
 
39
44
  beforeAll(async () => {
40
45
  const address = await httpServer();
@@ -45,16 +50,21 @@ beforeAll(async () => {
45
50
  .launch({ headless: true, args: getDefaultLaunchArgs() });
46
51
  page = await browser.newPage();
47
52
 
48
- worker = new Promise(resolve => {
49
- browser.on('targetcreated', async target => {
53
+ const workerPromise = new Promise<WebWorker>((resolve, reject) => {
54
+ browser.on('targetcreated', async (target: Target) => {
50
55
  if (target.type() === 'service_worker') {
51
- resolve(target.worker());
56
+ const serviceWorker = await target.worker();
57
+ if (serviceWorker) {
58
+ resolve(serviceWorker);
59
+ } else {
60
+ reject(new Error('Target did not expose a service worker'));
61
+ }
52
62
  }
53
63
  });
54
64
  });
55
65
 
56
66
  await page.goto(address);
57
- worker = await worker;
67
+ worker = await workerPromise;
58
68
  });
59
69
 
60
70
  afterAll(async () => {
@@ -82,9 +92,16 @@ test.skip('stealth: creepjs has good trust score', async () => {
82
92
 
83
93
  /* global OffscreenCanvas */
84
94
  function detectFingerprint() {
85
- const results = {};
86
-
87
- const props = [
95
+ const results: Record<string, string> = {};
96
+
97
+ const props: Array<
98
+ | 'userAgent'
99
+ | 'language'
100
+ | 'hardwareConcurrency'
101
+ | 'deviceMemory'
102
+ | 'languages'
103
+ | 'platform'
104
+ > = [
88
105
  'userAgent',
89
106
  'language',
90
107
  'hardwareConcurrency',
@@ -92,19 +109,23 @@ function detectFingerprint() {
92
109
  'languages',
93
110
  'platform',
94
111
  ];
95
- props.forEach(el => {
96
- results[el] = navigator[el].toString();
112
+ props.forEach(prop => {
113
+ results[prop] = String(navigator[prop]);
97
114
  });
98
115
 
99
116
  const canvasOffscreenWebgl = new OffscreenCanvas(256, 256);
100
117
  const contextWebgl = canvasOffscreenWebgl.getContext('webgl');
101
- const rendererInfo = contextWebgl.getExtension('WEBGL_debug_renderer_info');
102
- results.webglVendor = contextWebgl.getParameter(
103
- rendererInfo.UNMASKED_VENDOR_WEBGL
104
- );
105
- results.webglRenderer = contextWebgl.getParameter(
106
- rendererInfo.UNMASKED_RENDERER_WEBGL
107
- );
118
+ if (contextWebgl) {
119
+ const rendererInfo = contextWebgl.getExtension('WEBGL_debug_renderer_info');
120
+ if (rendererInfo) {
121
+ results.webglVendor = String(
122
+ contextWebgl.getParameter(rendererInfo.UNMASKED_VENDOR_WEBGL)
123
+ );
124
+ results.webglRenderer = String(
125
+ contextWebgl.getParameter(rendererInfo.UNMASKED_RENDERER_WEBGL)
126
+ );
127
+ }
128
+ }
108
129
 
109
130
  results.timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
110
131