@zorilla/puppeteer-extra-plugin-stealth 1.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/.claude/settings.local.json +21 -0
- package/LICENSE +21 -0
- package/README.md +324 -0
- package/dist/evasions/README.md +13 -0
- package/dist/evasions/_template/README.md +18 -0
- package/dist/evasions/_template/index.d.ts +13 -0
- package/dist/evasions/_template/index.d.ts.map +1 -0
- package/dist/evasions/_template/index.js +26 -0
- package/dist/evasions/_template/index.js.map +1 -0
- package/dist/evasions/_template/package.json +5 -0
- package/dist/evasions/_utils/README.md +287 -0
- package/dist/evasions/_utils/index.d.ts +238 -0
- package/dist/evasions/_utils/index.d.ts.map +1 -0
- package/dist/evasions/_utils/index.js +588 -0
- package/dist/evasions/_utils/index.js.map +1 -0
- package/dist/evasions/_utils/withUtils.d.ts +12 -0
- package/dist/evasions/_utils/withUtils.d.ts.map +1 -0
- package/dist/evasions/_utils/withUtils.js +47 -0
- package/dist/evasions/_utils/withUtils.js.map +1 -0
- package/dist/evasions/chrome.app/README.md +16 -0
- package/dist/evasions/chrome.app/index.d.ts +11 -0
- package/dist/evasions/chrome.app/index.d.ts.map +1 -0
- package/dist/evasions/chrome.app/index.js +97 -0
- package/dist/evasions/chrome.app/index.js.map +1 -0
- package/dist/evasions/chrome.app/package.json +5 -0
- package/dist/evasions/chrome.csi/README.md +29 -0
- package/dist/evasions/chrome.csi/index.d.ts +25 -0
- package/dist/evasions/chrome.csi/index.d.ts.map +1 -0
- package/dist/evasions/chrome.csi/index.js +69 -0
- package/dist/evasions/chrome.csi/index.js.map +1 -0
- package/dist/evasions/chrome.csi/package.json +5 -0
- package/dist/evasions/chrome.loadTimes/README.md +27 -0
- package/dist/evasions/chrome.loadTimes/index.d.ts +23 -0
- package/dist/evasions/chrome.loadTimes/index.d.ts.map +1 -0
- package/dist/evasions/chrome.loadTimes/index.js +163 -0
- package/dist/evasions/chrome.loadTimes/index.js.map +1 -0
- package/dist/evasions/chrome.loadTimes/package.json +5 -0
- package/dist/evasions/chrome.runtime/README.md +32 -0
- package/dist/evasions/chrome.runtime/index.d.ts +14 -0
- package/dist/evasions/chrome.runtime/index.d.ts.map +1 -0
- package/dist/evasions/chrome.runtime/index.js +252 -0
- package/dist/evasions/chrome.runtime/index.js.map +1 -0
- package/dist/evasions/chrome.runtime/package.json +5 -0
- package/dist/evasions/chrome.runtime/staticData.json +41 -0
- package/dist/evasions/defaultArgs/README.md +17 -0
- package/dist/evasions/defaultArgs/index.d.ts +2 -0
- package/dist/evasions/defaultArgs/index.d.ts.map +1 -0
- package/dist/evasions/defaultArgs/index.js +43 -0
- package/dist/evasions/defaultArgs/index.js.map +1 -0
- package/dist/evasions/defaultArgs/package.json +5 -0
- package/dist/evasions/iframe.contentWindow/README.md +19 -0
- package/dist/evasions/iframe.contentWindow/index.d.ts +15 -0
- package/dist/evasions/iframe.contentWindow/index.d.ts.map +1 -0
- package/dist/evasions/iframe.contentWindow/index.js +132 -0
- package/dist/evasions/iframe.contentWindow/index.js.map +1 -0
- package/dist/evasions/iframe.contentWindow/package.json +5 -0
- package/dist/evasions/media.codecs/README.md +38 -0
- package/dist/evasions/media.codecs/index.d.ts +12 -0
- package/dist/evasions/media.codecs/index.d.ts.map +1 -0
- package/dist/evasions/media.codecs/index.js +89 -0
- package/dist/evasions/media.codecs/index.js.map +1 -0
- package/dist/evasions/media.codecs/package.json +5 -0
- package/dist/evasions/navigator.hardwareConcurrency/README.md +19 -0
- package/dist/evasions/navigator.hardwareConcurrency/index.d.ts +2 -0
- package/dist/evasions/navigator.hardwareConcurrency/index.d.ts.map +1 -0
- package/dist/evasions/navigator.hardwareConcurrency/index.js +45 -0
- package/dist/evasions/navigator.hardwareConcurrency/index.js.map +1 -0
- package/dist/evasions/navigator.hardwareConcurrency/package.json +5 -0
- package/dist/evasions/navigator.languages/README.md +17 -0
- package/dist/evasions/navigator.languages/index.d.ts +2 -0
- package/dist/evasions/navigator.languages/index.d.ts.map +1 -0
- package/dist/evasions/navigator.languages/index.js +44 -0
- package/dist/evasions/navigator.languages/index.js.map +1 -0
- package/dist/evasions/navigator.languages/package.json +5 -0
- package/dist/evasions/navigator.permissions/README.md +16 -0
- package/dist/evasions/navigator.permissions/index.d.ts +2 -0
- package/dist/evasions/navigator.permissions/index.d.ts.map +1 -0
- package/dist/evasions/navigator.permissions/index.js +66 -0
- package/dist/evasions/navigator.permissions/index.js.map +1 -0
- package/dist/evasions/navigator.permissions/package.json +5 -0
- package/dist/evasions/navigator.plugins/README.md +24 -0
- package/dist/evasions/navigator.plugins/data.json +48 -0
- package/dist/evasions/navigator.plugins/functionMocks.d.ts +9 -0
- package/dist/evasions/navigator.plugins/functionMocks.d.ts.map +1 -0
- package/dist/evasions/navigator.plugins/functionMocks.js +47 -0
- package/dist/evasions/navigator.plugins/functionMocks.js.map +1 -0
- package/dist/evasions/navigator.plugins/index.d.ts +19 -0
- package/dist/evasions/navigator.plugins/index.d.ts.map +1 -0
- package/dist/evasions/navigator.plugins/index.js +98 -0
- package/dist/evasions/navigator.plugins/index.js.map +1 -0
- package/dist/evasions/navigator.plugins/magicArray.d.ts +2 -0
- package/dist/evasions/navigator.plugins/magicArray.d.ts.map +1 -0
- package/dist/evasions/navigator.plugins/magicArray.js +145 -0
- package/dist/evasions/navigator.plugins/magicArray.js.map +1 -0
- package/dist/evasions/navigator.plugins/mimeTypes.d.ts +2 -0
- package/dist/evasions/navigator.plugins/mimeTypes.d.ts.map +1 -0
- package/dist/evasions/navigator.plugins/mimeTypes.js +18 -0
- package/dist/evasions/navigator.plugins/mimeTypes.js.map +1 -0
- package/dist/evasions/navigator.plugins/package.json +5 -0
- package/dist/evasions/navigator.plugins/plugins.d.ts +2 -0
- package/dist/evasions/navigator.plugins/plugins.d.ts.map +1 -0
- package/dist/evasions/navigator.plugins/plugins.js +18 -0
- package/dist/evasions/navigator.plugins/plugins.js.map +1 -0
- package/dist/evasions/navigator.vendor/README.md +36 -0
- package/dist/evasions/navigator.vendor/index.d.ts +2 -0
- package/dist/evasions/navigator.vendor/index.d.ts.map +1 -0
- package/dist/evasions/navigator.vendor/index.js +64 -0
- package/dist/evasions/navigator.vendor/index.js.map +1 -0
- package/dist/evasions/navigator.vendor/package.json +5 -0
- package/dist/evasions/navigator.webdriver/README.md +17 -0
- package/dist/evasions/navigator.webdriver/index.d.ts +13 -0
- package/dist/evasions/navigator.webdriver/index.d.ts.map +1 -0
- package/dist/evasions/navigator.webdriver/index.js +48 -0
- package/dist/evasions/navigator.webdriver/index.js.map +1 -0
- package/dist/evasions/navigator.webdriver/package.json +5 -0
- package/dist/evasions/sourceurl/README.md +17 -0
- package/dist/evasions/sourceurl/_fixtures/test.html +35 -0
- package/dist/evasions/sourceurl/index.d.ts +12 -0
- package/dist/evasions/sourceurl/index.d.ts.map +1 -0
- package/dist/evasions/sourceurl/index.js +82 -0
- package/dist/evasions/sourceurl/index.js.map +1 -0
- package/dist/evasions/sourceurl/package.json +5 -0
- package/dist/evasions/user-agent-override/README.md +53 -0
- package/dist/evasions/user-agent-override/index.d.ts +2 -0
- package/dist/evasions/user-agent-override/index.d.ts.map +1 -0
- package/dist/evasions/user-agent-override/index.js +206 -0
- package/dist/evasions/user-agent-override/index.js.map +1 -0
- package/dist/evasions/user-agent-override/package.json +5 -0
- package/dist/evasions/webgl.vendor/README.md +20 -0
- package/dist/evasions/webgl.vendor/index.d.ts +17 -0
- package/dist/evasions/webgl.vendor/index.d.ts.map +1 -0
- package/dist/evasions/webgl.vendor/index.js +57 -0
- package/dist/evasions/webgl.vendor/index.js.map +1 -0
- package/dist/evasions/webgl.vendor/package.json +5 -0
- package/dist/evasions/window.outerdimensions/README.md +17 -0
- package/dist/evasions/window.outerdimensions/index.d.ts +13 -0
- package/dist/evasions/window.outerdimensions/index.d.ts.map +1 -0
- package/dist/evasions/window.outerdimensions/index.js +42 -0
- package/dist/evasions/window.outerdimensions/index.js.map +1 -0
- package/dist/evasions/window.outerdimensions/package.json +5 -0
- package/dist/index.d.ts +130 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +164 -0
- package/dist/index.js.map +1 -0
- package/examples/detect-headless.js +95 -0
- package/examples/test1.js +20 -0
- package/examples/test2.js +25 -0
- package/package.json +70 -0
- package/src/evasions/README.md +13 -0
- package/src/evasions/_template/README.md +18 -0
- package/src/evasions/_template/index.js +26 -0
- package/src/evasions/_template/package.json +5 -0
- package/src/evasions/_utils/README.md +287 -0
- package/src/evasions/_utils/index.js +588 -0
- package/src/evasions/_utils/withUtils.js +47 -0
- package/src/evasions/chrome.app/README.md +16 -0
- package/src/evasions/chrome.app/index.js +97 -0
- package/src/evasions/chrome.app/package.json +5 -0
- package/src/evasions/chrome.csi/README.md +29 -0
- package/src/evasions/chrome.csi/index.js +69 -0
- package/src/evasions/chrome.csi/package.json +5 -0
- package/src/evasions/chrome.loadTimes/README.md +27 -0
- package/src/evasions/chrome.loadTimes/index.js +163 -0
- package/src/evasions/chrome.loadTimes/package.json +5 -0
- package/src/evasions/chrome.runtime/README.md +32 -0
- package/src/evasions/chrome.runtime/index.js +252 -0
- package/src/evasions/chrome.runtime/package.json +5 -0
- package/src/evasions/chrome.runtime/staticData.json +41 -0
- package/src/evasions/defaultArgs/README.md +17 -0
- package/src/evasions/defaultArgs/index.js +43 -0
- package/src/evasions/defaultArgs/package.json +5 -0
- package/src/evasions/iframe.contentWindow/README.md +19 -0
- package/src/evasions/iframe.contentWindow/index.js +132 -0
- package/src/evasions/iframe.contentWindow/package.json +5 -0
- package/src/evasions/media.codecs/README.md +38 -0
- package/src/evasions/media.codecs/index.js +89 -0
- package/src/evasions/media.codecs/package.json +5 -0
- package/src/evasions/navigator.hardwareConcurrency/README.md +19 -0
- package/src/evasions/navigator.hardwareConcurrency/index.js +45 -0
- package/src/evasions/navigator.hardwareConcurrency/package.json +5 -0
- package/src/evasions/navigator.languages/README.md +17 -0
- package/src/evasions/navigator.languages/index.js +44 -0
- package/src/evasions/navigator.languages/package.json +5 -0
- package/src/evasions/navigator.permissions/README.md +16 -0
- package/src/evasions/navigator.permissions/index.js +66 -0
- package/src/evasions/navigator.permissions/package.json +5 -0
- package/src/evasions/navigator.plugins/README.md +24 -0
- package/src/evasions/navigator.plugins/data.json +48 -0
- package/src/evasions/navigator.plugins/functionMocks.js +47 -0
- package/src/evasions/navigator.plugins/index.js +98 -0
- package/src/evasions/navigator.plugins/magicArray.js +145 -0
- package/src/evasions/navigator.plugins/mimeTypes.js +18 -0
- package/src/evasions/navigator.plugins/package.json +5 -0
- package/src/evasions/navigator.plugins/plugins.js +18 -0
- package/src/evasions/navigator.vendor/README.md +36 -0
- package/src/evasions/navigator.vendor/index.js +64 -0
- package/src/evasions/navigator.vendor/package.json +5 -0
- package/src/evasions/navigator.webdriver/README.md +17 -0
- package/src/evasions/navigator.webdriver/index.js +48 -0
- package/src/evasions/navigator.webdriver/package.json +5 -0
- package/src/evasions/sourceurl/README.md +17 -0
- package/src/evasions/sourceurl/_fixtures/test.html +35 -0
- package/src/evasions/sourceurl/index.js +82 -0
- package/src/evasions/sourceurl/package.json +5 -0
- package/src/evasions/user-agent-override/README.md +53 -0
- package/src/evasions/user-agent-override/index.js +206 -0
- package/src/evasions/user-agent-override/package.json +5 -0
- package/src/evasions/webgl.vendor/README.md +20 -0
- package/src/evasions/webgl.vendor/index.js +57 -0
- package/src/evasions/webgl.vendor/package.json +5 -0
- package/src/evasions/window.outerdimensions/README.md +17 -0
- package/src/evasions/window.outerdimensions/index.js +42 -0
- package/src/evasions/window.outerdimensions/package.json +5 -0
- package/src/index.d.ts +111 -0
- package/src/index.ts +188 -0
- package/test/cat-and-mouse.test.ts +170 -0
- package/test/evasions/_utils/index.test.ts +710 -0
- package/test/evasions/chrome.app/index.test.ts +69 -0
- package/test/evasions/chrome.csi/index.test.ts +46 -0
- package/test/evasions/chrome.loadTimes/index.test.ts +61 -0
- package/test/evasions/chrome.runtime/index.test.ts +282 -0
- package/test/evasions/defaultArgs/index.test.ts +36 -0
- package/test/evasions/iframe.contentWindow/index.test.js +450 -0
- package/test/evasions/media.codecs/index.test.js +103 -0
- package/test/evasions/navigator.hardwareConcurrency/index.test.js +58 -0
- package/test/evasions/navigator.languages/index.test.js +101 -0
- package/test/evasions/navigator.permissions/index.test.js +104 -0
- package/test/evasions/navigator.plugins/index.test.js +55 -0
- package/test/evasions/navigator.plugins/mimeTypes.test.js +220 -0
- package/test/evasions/navigator.plugins/plugins.test.js +181 -0
- package/test/evasions/navigator.vendor/index.test.js +68 -0
- package/test/evasions/navigator.webdriver/index.test.js +47 -0
- package/test/evasions/sourceurl/_fixtures/test.html +35 -0
- package/test/evasions/sourceurl/index.test.js +62 -0
- package/test/evasions/user-agent-override/index.test.js +338 -0
- package/test/evasions/webgl.vendor/index.test.js +220 -0
- package/test/fixtures/dummy-with-service-worker.html +22 -0
- package/test/fixtures/dummy.html +11 -0
- package/test/fixtures/sw.js +1 -0
- package/test/fpscanner.test.ts +54 -0
- package/test/index.test.ts +51 -0
- package/test/service-worker.test.ts +112 -0
- package/test/stealth/_results/_thumbs/headful-chrome-stealth.js.png +0 -0
- package/test/stealth/_results/_thumbs/headful-chrome-vanilla.js.png +0 -0
- package/test/stealth/_results/_thumbs/headful-chromium-stealth.js.png +0 -0
- package/test/stealth/_results/_thumbs/headful-chromium-vanilla.js.png +0 -0
- package/test/stealth/_results/_thumbs/headless-chrome-stealth.js.png +0 -0
- package/test/stealth/_results/_thumbs/headless-chrome-vanilla.js.png +0 -0
- package/test/stealth/_results/_thumbs/headless-chromium-stealth.js.png +0 -0
- package/test/stealth/_results/_thumbs/headless-chromium-vanilla.js.png +0 -0
- package/test/stealth/_results/headful-chrome-stealth.js.png +0 -0
- package/test/stealth/_results/headful-chrome-vanilla.js.png +0 -0
- package/test/stealth/_results/headful-chromium-stealth.js.png +0 -0
- package/test/stealth/_results/headful-chromium-vanilla.js.png +0 -0
- package/test/stealth/_results/headless-chrome-stealth.js.png +0 -0
- package/test/stealth/_results/headless-chrome-vanilla.js.png +0 -0
- package/test/stealth/_results/headless-chromium-stealth.js.png +0 -0
- package/test/stealth/_results/headless-chromium-vanilla.js.png +0 -0
- package/test/stealth/headful-chrome-stealth.js +25 -0
- package/test/stealth/headful-chrome-vanilla.js +23 -0
- package/test/stealth/headful-chromium-stealth.js +22 -0
- package/test/stealth/headful-chromium-vanilla.js +20 -0
- package/test/stealth/headless-chrome-stealth.js +25 -0
- package/test/stealth/headless-chrome-vanilla.js +23 -0
- package/test/stealth/headless-chromium-stealth.js +22 -0
- package/test/stealth/headless-chromium-vanilla.js +20 -0
- package/test/util.js +82 -0
- package/tsconfig.json +10 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +28 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { expect, test } from 'vitest';
|
|
2
|
+
import Plugin from '../../../src/evasions/webgl.vendor/index.js';
|
|
3
|
+
import {
|
|
4
|
+
addExtra,
|
|
5
|
+
getStealthFingerPrint,
|
|
6
|
+
getVanillaFingerPrint,
|
|
7
|
+
vanillaPuppeteer,
|
|
8
|
+
} from '../../util.js';
|
|
9
|
+
|
|
10
|
+
// FIXME: This changed in more recent chrome versions
|
|
11
|
+
// test('vanilla: videoCard is Google Inc', async () => {
|
|
12
|
+
// const pageFn = async page => await page.evaluate(() => window.chrome) // eslint-disable-line
|
|
13
|
+
// const { videoCard } = await getVanillaFingerPrint(pageFn)
|
|
14
|
+
// expect(videoCard).toEqual(['Google Inc.', 'Google SwiftShader'])
|
|
15
|
+
// })
|
|
16
|
+
|
|
17
|
+
test.skip('stealth: videoCard is Intel Inc (requires fpcollect)', async () => {
|
|
18
|
+
const pageFn = async page => await page.evaluate(() => window.chrome); // eslint-disable-line
|
|
19
|
+
const { videoCard } = await getStealthFingerPrint(Plugin, pageFn);
|
|
20
|
+
expect(videoCard).toEqual(['Intel Inc.', 'Intel Iris OpenGL Engine']);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test.skip('stealth: customized values (requires fpcollect)', async () => {
|
|
24
|
+
const pageFn = async page => await page.evaluate(() => window.chrome); // eslint-disable-line
|
|
25
|
+
const { videoCard } = await getStealthFingerPrint(Plugin, pageFn, {
|
|
26
|
+
vendor: 'foo',
|
|
27
|
+
renderer: 'bar',
|
|
28
|
+
});
|
|
29
|
+
expect(videoCard).toEqual(['foo', 'bar']);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
/* global WebGLRenderingContext */
|
|
33
|
+
async function extendedTests() {
|
|
34
|
+
const results = {};
|
|
35
|
+
|
|
36
|
+
async function test(name, fn) {
|
|
37
|
+
const detectionPassed = await fn();
|
|
38
|
+
if (detectionPassed) console.log(`Chrome headless detected via ${name}`);
|
|
39
|
+
results[name] = detectionPassed;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const canvas = document.createElement('canvas');
|
|
43
|
+
const context = canvas.getContext('webgl');
|
|
44
|
+
|
|
45
|
+
await test('descriptorsOK', _ => {
|
|
46
|
+
const descriptors = Object.getOwnPropertyDescriptors(
|
|
47
|
+
WebGLRenderingContext.prototype
|
|
48
|
+
);
|
|
49
|
+
const str = descriptors.getParameter.toString();
|
|
50
|
+
return str === `[object Object]`;
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
await test('toStringOK', _ => {
|
|
54
|
+
const str = context.getParameter.toString();
|
|
55
|
+
return str === `function getParameter() { [native code] }`;
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
await test('toStringOK2', _ => {
|
|
59
|
+
const str = WebGLRenderingContext.prototype.getParameter.toString();
|
|
60
|
+
return str === `function getParameter() { [native code] }`;
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Make sure we not reveal our proxy through errors
|
|
64
|
+
await test('errorOK', _ => {
|
|
65
|
+
try {
|
|
66
|
+
return context.getParameter();
|
|
67
|
+
} catch (err) {
|
|
68
|
+
return !err.stack.includes(`at Object.apply`);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Should not throw (that was old stealth behavior)
|
|
73
|
+
await test('elementOK', _ => {
|
|
74
|
+
try {
|
|
75
|
+
return context.getParameter(123) === null;
|
|
76
|
+
} catch (_) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return results;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
test.skip('vanilla: webgl is native (requires fpcollect)', async () => {
|
|
85
|
+
const pageFn = async page => {
|
|
86
|
+
// page.on('console', msg => {
|
|
87
|
+
// console.log('Page console: ', msg.text())
|
|
88
|
+
// })
|
|
89
|
+
return await page.evaluate(extendedTests); // eslint-disable-line
|
|
90
|
+
};
|
|
91
|
+
const { pageFnResult: result } = await getVanillaFingerPrint(pageFn);
|
|
92
|
+
|
|
93
|
+
const wasHeadlessDetected = Object.values(result).some(e => e === false);
|
|
94
|
+
if (wasHeadlessDetected) {
|
|
95
|
+
console.log(result);
|
|
96
|
+
}
|
|
97
|
+
expect(wasHeadlessDetected).toBe(false);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test.skip('stealth: webgl is native (requires fpcollect)', async () => {
|
|
101
|
+
const pageFn = async page => await page.evaluate(extendedTests); // eslint-disable-line
|
|
102
|
+
const { pageFnResult: result } = await getStealthFingerPrint(Plugin, pageFn);
|
|
103
|
+
|
|
104
|
+
const wasHeadlessDetected = Object.values(result).some(e => e === false);
|
|
105
|
+
if (wasHeadlessDetected) {
|
|
106
|
+
console.log(result);
|
|
107
|
+
}
|
|
108
|
+
expect(wasHeadlessDetected).toBe(false);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* A very simple method to retrieve the name of the default videocard of the system
|
|
113
|
+
* using webgl.
|
|
114
|
+
*
|
|
115
|
+
* Example (Apple Retina MBP 13): {vendor: "Intel Inc.", renderer: "Intel(R) Iris(TM) Graphics 6100"}
|
|
116
|
+
*
|
|
117
|
+
* @see https://stackoverflow.com/questions/49267764/how-to-get-the-video-card-driver-name-using-javascript-browser-side
|
|
118
|
+
* @returns {Object}
|
|
119
|
+
*/
|
|
120
|
+
function getVideoCardInfo(context = 'webgl') {
|
|
121
|
+
const gl = document.createElement('canvas').getContext(context);
|
|
122
|
+
if (!gl) {
|
|
123
|
+
return {
|
|
124
|
+
error: 'no webgl',
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
|
|
128
|
+
if (debugInfo) {
|
|
129
|
+
return {
|
|
130
|
+
vendor: gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL),
|
|
131
|
+
renderer: gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
error: 'no WEBGL_debug_renderer_info',
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
test('stealth: handles WebGLRenderingContext', async () => {
|
|
140
|
+
const puppeteer = addExtra(vanillaPuppeteer).use(Plugin());
|
|
141
|
+
const browser = await puppeteer.launch({ headless: true });
|
|
142
|
+
const page = await browser.newPage();
|
|
143
|
+
|
|
144
|
+
const videoCardInfo = await page.evaluate(getVideoCardInfo, 'webgl');
|
|
145
|
+
expect(videoCardInfo.error).toBe(undefined);
|
|
146
|
+
expect(videoCardInfo.vendor).toBe('Intel Inc.');
|
|
147
|
+
expect(videoCardInfo.renderer).toBe('Intel Iris OpenGL Engine');
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test('stealth: handles WebGL2RenderingContext', async () => {
|
|
151
|
+
const puppeteer = addExtra(vanillaPuppeteer).use(Plugin());
|
|
152
|
+
const browser = await puppeteer.launch({ headless: true });
|
|
153
|
+
const page = await browser.newPage();
|
|
154
|
+
|
|
155
|
+
const videoCardInfo = await page.evaluate(getVideoCardInfo, 'webgl2');
|
|
156
|
+
expect(videoCardInfo.error).toBe(undefined);
|
|
157
|
+
expect(videoCardInfo.vendor).toBe('Intel Inc.');
|
|
158
|
+
expect(videoCardInfo.renderer).toBe('Intel Iris OpenGL Engine');
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test('vanilla: normal toString stuff', async () => {
|
|
162
|
+
const browser = await vanillaPuppeteer.launch({ headless: true });
|
|
163
|
+
const page = await browser.newPage();
|
|
164
|
+
|
|
165
|
+
const test1 = await page.evaluate(() => {
|
|
166
|
+
return WebGLRenderingContext.prototype.getParameter.toString + '';
|
|
167
|
+
});
|
|
168
|
+
expect(test1).toBe('function toString() { [native code] }');
|
|
169
|
+
|
|
170
|
+
const test2 = await page.evaluate(() => {
|
|
171
|
+
return WebGLRenderingContext.prototype.getParameter.toString();
|
|
172
|
+
});
|
|
173
|
+
expect(test2).toBe('function getParameter() { [native code] }');
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test('stealth: will not leak toString stuff', async () => {
|
|
177
|
+
const puppeteer = addExtra(vanillaPuppeteer).use(Plugin());
|
|
178
|
+
const browser = await puppeteer.launch({ headless: true });
|
|
179
|
+
const page = await browser.newPage();
|
|
180
|
+
|
|
181
|
+
const test1 = await page.evaluate(() => {
|
|
182
|
+
return WebGLRenderingContext.prototype.getParameter.toString + '';
|
|
183
|
+
});
|
|
184
|
+
expect(test1).toBe('function toString() { [native code] }'); // returns function () { [native code] }
|
|
185
|
+
|
|
186
|
+
const test2 = await page.evaluate(() => {
|
|
187
|
+
return WebGLRenderingContext.prototype.getParameter.toString();
|
|
188
|
+
});
|
|
189
|
+
expect(test2).toBe('function getParameter() { [native code] }');
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test('stealth: sets user opts correctly', async () => {
|
|
193
|
+
const puppeteer = addExtra(vanillaPuppeteer).use(
|
|
194
|
+
Plugin({ vendor: 'alice', renderer: 'bob' })
|
|
195
|
+
);
|
|
196
|
+
const browser = await puppeteer.launch({ headless: true });
|
|
197
|
+
const page = await browser.newPage();
|
|
198
|
+
|
|
199
|
+
const videoCardInfo = await page.evaluate(getVideoCardInfo, 'webgl');
|
|
200
|
+
expect(videoCardInfo.error).toBe(undefined);
|
|
201
|
+
expect(videoCardInfo.vendor).toBe('alice');
|
|
202
|
+
expect(videoCardInfo.renderer).toBe('bob');
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test('stealth: does not affect protoype', async () => {
|
|
206
|
+
const puppeteer = addExtra(vanillaPuppeteer).use(
|
|
207
|
+
Plugin({ vendor: 'alice', renderer: 'bob' })
|
|
208
|
+
);
|
|
209
|
+
const browser = await puppeteer.launch({ headless: true });
|
|
210
|
+
const page = await browser.newPage();
|
|
211
|
+
|
|
212
|
+
const result = await page.evaluate(() => {
|
|
213
|
+
try {
|
|
214
|
+
return WebGLRenderingContext.prototype.getParameter(37445);
|
|
215
|
+
} catch (err) {
|
|
216
|
+
return err.message;
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
expect(result).toBe('Illegal invocation');
|
|
220
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<title>title foo</title>
|
|
6
|
+
<!-- Testing evasions with a real html page makes things easier -->
|
|
7
|
+
<script>
|
|
8
|
+
if ('serviceWorker' in navigator) {
|
|
9
|
+
window.addEventListener('load', () => {
|
|
10
|
+
navigator.serviceWorker.register('/sw.js').then((registration) => {
|
|
11
|
+
console.log('ServiceWorker registration successful with scope: ', registration.scope);
|
|
12
|
+
}, (err) => {
|
|
13
|
+
console.log('ServiceWorker registration failed: ', err);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
</script>
|
|
18
|
+
</head>
|
|
19
|
+
<body>
|
|
20
|
+
<h1>Test page with service worker</h1>
|
|
21
|
+
</body>
|
|
22
|
+
</html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// Left empty
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import fpscanner from 'fpscanner';
|
|
2
|
+
import { expect, test } from 'vitest';
|
|
3
|
+
import Plugin from '../dist/index.js';
|
|
4
|
+
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;
|
|
20
|
+
};
|
|
21
|
+
|
|
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
|
+
});
|
|
35
|
+
|
|
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
|
+
);
|
|
42
|
+
|
|
43
|
+
if (failedChecks.length) {
|
|
44
|
+
console.warn('The following fingerprints failed:', failedChecks);
|
|
45
|
+
}
|
|
46
|
+
|
|
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);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const PLUGIN_NAME = 'stealth';
|
|
2
|
+
|
|
3
|
+
import { expect, test } from 'vitest';
|
|
4
|
+
|
|
5
|
+
import Plugin from '../dist/index.js';
|
|
6
|
+
|
|
7
|
+
test('is a function', async () => {
|
|
8
|
+
expect(typeof Plugin).toBe('function');
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test('should have the basic class members', async () => {
|
|
12
|
+
const instance = Plugin();
|
|
13
|
+
expect(instance.name).toBe(PLUGIN_NAME);
|
|
14
|
+
expect(instance._isPuppeteerExtraPlugin).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('should have the public child class members', async () => {
|
|
18
|
+
const instance = Plugin();
|
|
19
|
+
const prototype = Object.getPrototypeOf(instance);
|
|
20
|
+
const childClassMembers = Object.getOwnPropertyNames(prototype);
|
|
21
|
+
|
|
22
|
+
expect(childClassMembers.includes('constructor')).toBe(true);
|
|
23
|
+
expect(childClassMembers.includes('name')).toBe(true);
|
|
24
|
+
expect(childClassMembers.includes('name')).toBe(true);
|
|
25
|
+
expect(childClassMembers.includes('defaults')).toBe(true);
|
|
26
|
+
expect(childClassMembers.includes('availableEvasions')).toBe(true);
|
|
27
|
+
expect(childClassMembers.includes('enabledEvasions')).toBe(true);
|
|
28
|
+
expect(childClassMembers.length).toBe(7);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('should have opts with default values', async () => {
|
|
32
|
+
const instance = Plugin();
|
|
33
|
+
expect(instance.opts.enabledEvasions).toEqual(instance.availableEvasions);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('should add all dependencies dynamically', async () => {
|
|
37
|
+
const instance = Plugin();
|
|
38
|
+
const deps = new Set(
|
|
39
|
+
[...instance.opts.enabledEvasions].map(e => `${PLUGIN_NAME}/evasions/${e}`)
|
|
40
|
+
);
|
|
41
|
+
expect(instance.dependencies).toEqual(deps);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('should add all dependencies dynamically including changes', async () => {
|
|
45
|
+
const instance = Plugin();
|
|
46
|
+
const fakeDep = 'foobar';
|
|
47
|
+
instance.enabledEvasions = new Set([fakeDep]);
|
|
48
|
+
expect(instance.dependencies).toEqual(
|
|
49
|
+
new Set([`${PLUGIN_NAME}/evasions/${fakeDep}`])
|
|
50
|
+
);
|
|
51
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import http from 'node:http';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { afterAll, beforeAll, expect, test } from 'vitest';
|
|
6
|
+
import Plugin from '../dist/index.js';
|
|
7
|
+
import { addExtra, vanillaPuppeteer } from './util.js';
|
|
8
|
+
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = path.dirname(__filename);
|
|
11
|
+
|
|
12
|
+
// Create a simple HTTP server. Service Workers cannot be served from file:// URIs
|
|
13
|
+
const httpServer = async () => {
|
|
14
|
+
const server = await http
|
|
15
|
+
.createServer((req, res) => {
|
|
16
|
+
let contents, type;
|
|
17
|
+
|
|
18
|
+
if (req.url === '/sw.js') {
|
|
19
|
+
contents = fs.readFileSync(path.join(__dirname, './fixtures/sw.js'));
|
|
20
|
+
type = 'application/javascript';
|
|
21
|
+
} else {
|
|
22
|
+
contents = fs.readFileSync(
|
|
23
|
+
path.join(__dirname, './fixtures/dummy-with-service-worker.html')
|
|
24
|
+
);
|
|
25
|
+
type = 'text/html';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
res.setHeader('Content-Type', type);
|
|
29
|
+
res.writeHead(200);
|
|
30
|
+
res.end(contents);
|
|
31
|
+
})
|
|
32
|
+
.listen(0); // random free port
|
|
33
|
+
|
|
34
|
+
return `http://127.0.0.1:${server.address().port}/`;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
let browser, page, worker;
|
|
38
|
+
|
|
39
|
+
beforeAll(async () => {
|
|
40
|
+
const address = await httpServer();
|
|
41
|
+
console.log(`Server is running on port ${address}`);
|
|
42
|
+
|
|
43
|
+
browser = await addExtra(vanillaPuppeteer)
|
|
44
|
+
.use(Plugin())
|
|
45
|
+
.launch({ headless: true });
|
|
46
|
+
page = await browser.newPage();
|
|
47
|
+
|
|
48
|
+
worker = new Promise(resolve => {
|
|
49
|
+
browser.on('targetcreated', async target => {
|
|
50
|
+
if (target.type() === 'service_worker') {
|
|
51
|
+
resolve(target.worker());
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
await page.goto(address);
|
|
57
|
+
worker = await worker;
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
afterAll(async () => {
|
|
61
|
+
await browser.close();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test.skip('stealth: inconsistencies between page and worker', async () => {
|
|
65
|
+
const pageFP = await page.evaluate(detectFingerprint);
|
|
66
|
+
const workerFP = await worker.evaluate(detectFingerprint);
|
|
67
|
+
|
|
68
|
+
expect(pageFP).toEqual(workerFP);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test.skip('stealth: creepjs has good trust score', async () => {
|
|
72
|
+
page.goto('https://abrahamjuliot.github.io/creepjs/');
|
|
73
|
+
|
|
74
|
+
const score = await (
|
|
75
|
+
await (
|
|
76
|
+
await page.waitForSelector('#fingerprint-data .unblurred')
|
|
77
|
+
).getProperty('textContent')
|
|
78
|
+
).jsonValue();
|
|
79
|
+
|
|
80
|
+
expect(parseInt(score, 10) > 80).toBe(true);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
/* global OffscreenCanvas */
|
|
84
|
+
function detectFingerprint() {
|
|
85
|
+
const results = {};
|
|
86
|
+
|
|
87
|
+
const props = [
|
|
88
|
+
'userAgent',
|
|
89
|
+
'language',
|
|
90
|
+
'hardwareConcurrency',
|
|
91
|
+
'deviceMemory',
|
|
92
|
+
'languages',
|
|
93
|
+
'platform',
|
|
94
|
+
];
|
|
95
|
+
props.forEach(el => {
|
|
96
|
+
results[el] = navigator[el].toString();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const canvasOffscreenWebgl = new OffscreenCanvas(256, 256);
|
|
100
|
+
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
|
+
);
|
|
108
|
+
|
|
109
|
+
results.timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
110
|
+
|
|
111
|
+
return results;
|
|
112
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const path = require('node:path');
|
|
2
|
+
const scriptName = path.basename(__filename);
|
|
3
|
+
const screenshotPath = path.join(__dirname, '_results', `${scriptName}.png`);
|
|
4
|
+
|
|
5
|
+
const puppeteer = require('@zorilla/puppeteer-extra');
|
|
6
|
+
const pluginStealth = require('@zorilla/puppeteer-extra-plugin-stealth')();
|
|
7
|
+
puppeteer.use(pluginStealth);
|
|
8
|
+
|
|
9
|
+
async function main() {
|
|
10
|
+
console.log('start', scriptName);
|
|
11
|
+
const browser = await puppeteer.launch({
|
|
12
|
+
headless: false,
|
|
13
|
+
executablePath: `/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`, // MacOS
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const page = await browser.newPage();
|
|
17
|
+
await page.setViewport({ width: 800, height: 600 });
|
|
18
|
+
await page.goto('https://bot.sannysoft.com/');
|
|
19
|
+
await page.waitForTimeout(5000);
|
|
20
|
+
await page.screenshot({ path: screenshotPath, fullPage: true });
|
|
21
|
+
|
|
22
|
+
await browser.close();
|
|
23
|
+
console.log('end', screenshotPath);
|
|
24
|
+
}
|
|
25
|
+
main();
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const path = require('node:path');
|
|
2
|
+
const scriptName = path.basename(__filename);
|
|
3
|
+
const screenshotPath = path.join(__dirname, '_results', `${scriptName}.png`);
|
|
4
|
+
|
|
5
|
+
const puppeteer = require('puppeteer');
|
|
6
|
+
|
|
7
|
+
async function main() {
|
|
8
|
+
console.log('start', scriptName);
|
|
9
|
+
const browser = await puppeteer.launch({
|
|
10
|
+
headless: false,
|
|
11
|
+
executablePath: `/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`, // MacOS
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const page = await browser.newPage();
|
|
15
|
+
await page.setViewport({ width: 800, height: 600 });
|
|
16
|
+
await page.goto('https://bot.sannysoft.com/');
|
|
17
|
+
await page.waitForTimeout(5000);
|
|
18
|
+
await page.screenshot({ path: screenshotPath, fullPage: true });
|
|
19
|
+
|
|
20
|
+
await browser.close();
|
|
21
|
+
console.log('end', screenshotPath);
|
|
22
|
+
}
|
|
23
|
+
main();
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const path = require('node:path');
|
|
2
|
+
const scriptName = path.basename(__filename);
|
|
3
|
+
const screenshotPath = path.join(__dirname, '_results', `${scriptName}.png`);
|
|
4
|
+
|
|
5
|
+
const puppeteer = require('@zorilla/puppeteer-extra');
|
|
6
|
+
const pluginStealth = require('@zorilla/puppeteer-extra-plugin-stealth');
|
|
7
|
+
|
|
8
|
+
async function main() {
|
|
9
|
+
puppeteer.use(pluginStealth());
|
|
10
|
+
console.log('start', scriptName);
|
|
11
|
+
const browser = await puppeteer.launch({ headless: false });
|
|
12
|
+
|
|
13
|
+
const page = await browser.newPage();
|
|
14
|
+
await page.setViewport({ width: 800, height: 600 });
|
|
15
|
+
await page.goto('https://bot.sannysoft.com/');
|
|
16
|
+
await page.waitForTimeout(5000);
|
|
17
|
+
await page.screenshot({ path: screenshotPath, fullPage: true });
|
|
18
|
+
|
|
19
|
+
await browser.close();
|
|
20
|
+
console.log('end', screenshotPath);
|
|
21
|
+
}
|
|
22
|
+
main();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const path = require('node:path');
|
|
2
|
+
const scriptName = path.basename(__filename);
|
|
3
|
+
const screenshotPath = path.join(__dirname, '_results', `${scriptName}.png`);
|
|
4
|
+
|
|
5
|
+
const puppeteer = require('puppeteer');
|
|
6
|
+
|
|
7
|
+
async function main() {
|
|
8
|
+
console.log('start', scriptName);
|
|
9
|
+
const browser = await puppeteer.launch({ headless: false });
|
|
10
|
+
|
|
11
|
+
const page = await browser.newPage();
|
|
12
|
+
await page.setViewport({ width: 800, height: 600 });
|
|
13
|
+
await page.goto('https://bot.sannysoft.com/');
|
|
14
|
+
await page.waitForTimeout(5000);
|
|
15
|
+
await page.screenshot({ path: screenshotPath, fullPage: true });
|
|
16
|
+
|
|
17
|
+
await browser.close();
|
|
18
|
+
console.log('end', screenshotPath);
|
|
19
|
+
}
|
|
20
|
+
main();
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const path = require('node:path');
|
|
2
|
+
const scriptName = path.basename(__filename);
|
|
3
|
+
const screenshotPath = path.join(__dirname, '_results', `${scriptName}.png`);
|
|
4
|
+
|
|
5
|
+
const puppeteer = require('@zorilla/puppeteer-extra');
|
|
6
|
+
const pluginStealth = require('@zorilla/puppeteer-extra-plugin-stealth')();
|
|
7
|
+
puppeteer.use(pluginStealth);
|
|
8
|
+
|
|
9
|
+
async function main() {
|
|
10
|
+
console.log('start', scriptName);
|
|
11
|
+
const browser = await puppeteer.launch({
|
|
12
|
+
headless: true,
|
|
13
|
+
executablePath: `/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`, // MacOS
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const page = await browser.newPage();
|
|
17
|
+
await page.setViewport({ width: 800, height: 600 });
|
|
18
|
+
await page.goto('https://bot.sannysoft.com/');
|
|
19
|
+
await page.waitForTimeout(5000);
|
|
20
|
+
await page.screenshot({ path: screenshotPath, fullPage: true });
|
|
21
|
+
|
|
22
|
+
await browser.close();
|
|
23
|
+
console.log('end', screenshotPath);
|
|
24
|
+
}
|
|
25
|
+
main();
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const path = require('node:path');
|
|
2
|
+
const scriptName = path.basename(__filename);
|
|
3
|
+
const screenshotPath = path.join(__dirname, '_results', `${scriptName}.png`);
|
|
4
|
+
|
|
5
|
+
const puppeteer = require('puppeteer');
|
|
6
|
+
|
|
7
|
+
async function main() {
|
|
8
|
+
console.log('start', scriptName);
|
|
9
|
+
const browser = await puppeteer.launch({
|
|
10
|
+
headless: true,
|
|
11
|
+
executablePath: `/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`, // MacOS
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const page = await browser.newPage();
|
|
15
|
+
await page.setViewport({ width: 800, height: 600 });
|
|
16
|
+
await page.goto('https://bot.sannysoft.com/');
|
|
17
|
+
await page.waitForTimeout(5000);
|
|
18
|
+
await page.screenshot({ path: screenshotPath, fullPage: true });
|
|
19
|
+
|
|
20
|
+
await browser.close();
|
|
21
|
+
console.log('end', screenshotPath);
|
|
22
|
+
}
|
|
23
|
+
main();
|