@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.
Files changed (270) hide show
  1. package/.claude/settings.local.json +21 -0
  2. package/LICENSE +21 -0
  3. package/README.md +324 -0
  4. package/dist/evasions/README.md +13 -0
  5. package/dist/evasions/_template/README.md +18 -0
  6. package/dist/evasions/_template/index.d.ts +13 -0
  7. package/dist/evasions/_template/index.d.ts.map +1 -0
  8. package/dist/evasions/_template/index.js +26 -0
  9. package/dist/evasions/_template/index.js.map +1 -0
  10. package/dist/evasions/_template/package.json +5 -0
  11. package/dist/evasions/_utils/README.md +287 -0
  12. package/dist/evasions/_utils/index.d.ts +238 -0
  13. package/dist/evasions/_utils/index.d.ts.map +1 -0
  14. package/dist/evasions/_utils/index.js +588 -0
  15. package/dist/evasions/_utils/index.js.map +1 -0
  16. package/dist/evasions/_utils/withUtils.d.ts +12 -0
  17. package/dist/evasions/_utils/withUtils.d.ts.map +1 -0
  18. package/dist/evasions/_utils/withUtils.js +47 -0
  19. package/dist/evasions/_utils/withUtils.js.map +1 -0
  20. package/dist/evasions/chrome.app/README.md +16 -0
  21. package/dist/evasions/chrome.app/index.d.ts +11 -0
  22. package/dist/evasions/chrome.app/index.d.ts.map +1 -0
  23. package/dist/evasions/chrome.app/index.js +97 -0
  24. package/dist/evasions/chrome.app/index.js.map +1 -0
  25. package/dist/evasions/chrome.app/package.json +5 -0
  26. package/dist/evasions/chrome.csi/README.md +29 -0
  27. package/dist/evasions/chrome.csi/index.d.ts +25 -0
  28. package/dist/evasions/chrome.csi/index.d.ts.map +1 -0
  29. package/dist/evasions/chrome.csi/index.js +69 -0
  30. package/dist/evasions/chrome.csi/index.js.map +1 -0
  31. package/dist/evasions/chrome.csi/package.json +5 -0
  32. package/dist/evasions/chrome.loadTimes/README.md +27 -0
  33. package/dist/evasions/chrome.loadTimes/index.d.ts +23 -0
  34. package/dist/evasions/chrome.loadTimes/index.d.ts.map +1 -0
  35. package/dist/evasions/chrome.loadTimes/index.js +163 -0
  36. package/dist/evasions/chrome.loadTimes/index.js.map +1 -0
  37. package/dist/evasions/chrome.loadTimes/package.json +5 -0
  38. package/dist/evasions/chrome.runtime/README.md +32 -0
  39. package/dist/evasions/chrome.runtime/index.d.ts +14 -0
  40. package/dist/evasions/chrome.runtime/index.d.ts.map +1 -0
  41. package/dist/evasions/chrome.runtime/index.js +252 -0
  42. package/dist/evasions/chrome.runtime/index.js.map +1 -0
  43. package/dist/evasions/chrome.runtime/package.json +5 -0
  44. package/dist/evasions/chrome.runtime/staticData.json +41 -0
  45. package/dist/evasions/defaultArgs/README.md +17 -0
  46. package/dist/evasions/defaultArgs/index.d.ts +2 -0
  47. package/dist/evasions/defaultArgs/index.d.ts.map +1 -0
  48. package/dist/evasions/defaultArgs/index.js +43 -0
  49. package/dist/evasions/defaultArgs/index.js.map +1 -0
  50. package/dist/evasions/defaultArgs/package.json +5 -0
  51. package/dist/evasions/iframe.contentWindow/README.md +19 -0
  52. package/dist/evasions/iframe.contentWindow/index.d.ts +15 -0
  53. package/dist/evasions/iframe.contentWindow/index.d.ts.map +1 -0
  54. package/dist/evasions/iframe.contentWindow/index.js +132 -0
  55. package/dist/evasions/iframe.contentWindow/index.js.map +1 -0
  56. package/dist/evasions/iframe.contentWindow/package.json +5 -0
  57. package/dist/evasions/media.codecs/README.md +38 -0
  58. package/dist/evasions/media.codecs/index.d.ts +12 -0
  59. package/dist/evasions/media.codecs/index.d.ts.map +1 -0
  60. package/dist/evasions/media.codecs/index.js +89 -0
  61. package/dist/evasions/media.codecs/index.js.map +1 -0
  62. package/dist/evasions/media.codecs/package.json +5 -0
  63. package/dist/evasions/navigator.hardwareConcurrency/README.md +19 -0
  64. package/dist/evasions/navigator.hardwareConcurrency/index.d.ts +2 -0
  65. package/dist/evasions/navigator.hardwareConcurrency/index.d.ts.map +1 -0
  66. package/dist/evasions/navigator.hardwareConcurrency/index.js +45 -0
  67. package/dist/evasions/navigator.hardwareConcurrency/index.js.map +1 -0
  68. package/dist/evasions/navigator.hardwareConcurrency/package.json +5 -0
  69. package/dist/evasions/navigator.languages/README.md +17 -0
  70. package/dist/evasions/navigator.languages/index.d.ts +2 -0
  71. package/dist/evasions/navigator.languages/index.d.ts.map +1 -0
  72. package/dist/evasions/navigator.languages/index.js +44 -0
  73. package/dist/evasions/navigator.languages/index.js.map +1 -0
  74. package/dist/evasions/navigator.languages/package.json +5 -0
  75. package/dist/evasions/navigator.permissions/README.md +16 -0
  76. package/dist/evasions/navigator.permissions/index.d.ts +2 -0
  77. package/dist/evasions/navigator.permissions/index.d.ts.map +1 -0
  78. package/dist/evasions/navigator.permissions/index.js +66 -0
  79. package/dist/evasions/navigator.permissions/index.js.map +1 -0
  80. package/dist/evasions/navigator.permissions/package.json +5 -0
  81. package/dist/evasions/navigator.plugins/README.md +24 -0
  82. package/dist/evasions/navigator.plugins/data.json +48 -0
  83. package/dist/evasions/navigator.plugins/functionMocks.d.ts +9 -0
  84. package/dist/evasions/navigator.plugins/functionMocks.d.ts.map +1 -0
  85. package/dist/evasions/navigator.plugins/functionMocks.js +47 -0
  86. package/dist/evasions/navigator.plugins/functionMocks.js.map +1 -0
  87. package/dist/evasions/navigator.plugins/index.d.ts +19 -0
  88. package/dist/evasions/navigator.plugins/index.d.ts.map +1 -0
  89. package/dist/evasions/navigator.plugins/index.js +98 -0
  90. package/dist/evasions/navigator.plugins/index.js.map +1 -0
  91. package/dist/evasions/navigator.plugins/magicArray.d.ts +2 -0
  92. package/dist/evasions/navigator.plugins/magicArray.d.ts.map +1 -0
  93. package/dist/evasions/navigator.plugins/magicArray.js +145 -0
  94. package/dist/evasions/navigator.plugins/magicArray.js.map +1 -0
  95. package/dist/evasions/navigator.plugins/mimeTypes.d.ts +2 -0
  96. package/dist/evasions/navigator.plugins/mimeTypes.d.ts.map +1 -0
  97. package/dist/evasions/navigator.plugins/mimeTypes.js +18 -0
  98. package/dist/evasions/navigator.plugins/mimeTypes.js.map +1 -0
  99. package/dist/evasions/navigator.plugins/package.json +5 -0
  100. package/dist/evasions/navigator.plugins/plugins.d.ts +2 -0
  101. package/dist/evasions/navigator.plugins/plugins.d.ts.map +1 -0
  102. package/dist/evasions/navigator.plugins/plugins.js +18 -0
  103. package/dist/evasions/navigator.plugins/plugins.js.map +1 -0
  104. package/dist/evasions/navigator.vendor/README.md +36 -0
  105. package/dist/evasions/navigator.vendor/index.d.ts +2 -0
  106. package/dist/evasions/navigator.vendor/index.d.ts.map +1 -0
  107. package/dist/evasions/navigator.vendor/index.js +64 -0
  108. package/dist/evasions/navigator.vendor/index.js.map +1 -0
  109. package/dist/evasions/navigator.vendor/package.json +5 -0
  110. package/dist/evasions/navigator.webdriver/README.md +17 -0
  111. package/dist/evasions/navigator.webdriver/index.d.ts +13 -0
  112. package/dist/evasions/navigator.webdriver/index.d.ts.map +1 -0
  113. package/dist/evasions/navigator.webdriver/index.js +48 -0
  114. package/dist/evasions/navigator.webdriver/index.js.map +1 -0
  115. package/dist/evasions/navigator.webdriver/package.json +5 -0
  116. package/dist/evasions/sourceurl/README.md +17 -0
  117. package/dist/evasions/sourceurl/_fixtures/test.html +35 -0
  118. package/dist/evasions/sourceurl/index.d.ts +12 -0
  119. package/dist/evasions/sourceurl/index.d.ts.map +1 -0
  120. package/dist/evasions/sourceurl/index.js +82 -0
  121. package/dist/evasions/sourceurl/index.js.map +1 -0
  122. package/dist/evasions/sourceurl/package.json +5 -0
  123. package/dist/evasions/user-agent-override/README.md +53 -0
  124. package/dist/evasions/user-agent-override/index.d.ts +2 -0
  125. package/dist/evasions/user-agent-override/index.d.ts.map +1 -0
  126. package/dist/evasions/user-agent-override/index.js +206 -0
  127. package/dist/evasions/user-agent-override/index.js.map +1 -0
  128. package/dist/evasions/user-agent-override/package.json +5 -0
  129. package/dist/evasions/webgl.vendor/README.md +20 -0
  130. package/dist/evasions/webgl.vendor/index.d.ts +17 -0
  131. package/dist/evasions/webgl.vendor/index.d.ts.map +1 -0
  132. package/dist/evasions/webgl.vendor/index.js +57 -0
  133. package/dist/evasions/webgl.vendor/index.js.map +1 -0
  134. package/dist/evasions/webgl.vendor/package.json +5 -0
  135. package/dist/evasions/window.outerdimensions/README.md +17 -0
  136. package/dist/evasions/window.outerdimensions/index.d.ts +13 -0
  137. package/dist/evasions/window.outerdimensions/index.d.ts.map +1 -0
  138. package/dist/evasions/window.outerdimensions/index.js +42 -0
  139. package/dist/evasions/window.outerdimensions/index.js.map +1 -0
  140. package/dist/evasions/window.outerdimensions/package.json +5 -0
  141. package/dist/index.d.ts +130 -0
  142. package/dist/index.d.ts.map +1 -0
  143. package/dist/index.js +164 -0
  144. package/dist/index.js.map +1 -0
  145. package/examples/detect-headless.js +95 -0
  146. package/examples/test1.js +20 -0
  147. package/examples/test2.js +25 -0
  148. package/package.json +70 -0
  149. package/src/evasions/README.md +13 -0
  150. package/src/evasions/_template/README.md +18 -0
  151. package/src/evasions/_template/index.js +26 -0
  152. package/src/evasions/_template/package.json +5 -0
  153. package/src/evasions/_utils/README.md +287 -0
  154. package/src/evasions/_utils/index.js +588 -0
  155. package/src/evasions/_utils/withUtils.js +47 -0
  156. package/src/evasions/chrome.app/README.md +16 -0
  157. package/src/evasions/chrome.app/index.js +97 -0
  158. package/src/evasions/chrome.app/package.json +5 -0
  159. package/src/evasions/chrome.csi/README.md +29 -0
  160. package/src/evasions/chrome.csi/index.js +69 -0
  161. package/src/evasions/chrome.csi/package.json +5 -0
  162. package/src/evasions/chrome.loadTimes/README.md +27 -0
  163. package/src/evasions/chrome.loadTimes/index.js +163 -0
  164. package/src/evasions/chrome.loadTimes/package.json +5 -0
  165. package/src/evasions/chrome.runtime/README.md +32 -0
  166. package/src/evasions/chrome.runtime/index.js +252 -0
  167. package/src/evasions/chrome.runtime/package.json +5 -0
  168. package/src/evasions/chrome.runtime/staticData.json +41 -0
  169. package/src/evasions/defaultArgs/README.md +17 -0
  170. package/src/evasions/defaultArgs/index.js +43 -0
  171. package/src/evasions/defaultArgs/package.json +5 -0
  172. package/src/evasions/iframe.contentWindow/README.md +19 -0
  173. package/src/evasions/iframe.contentWindow/index.js +132 -0
  174. package/src/evasions/iframe.contentWindow/package.json +5 -0
  175. package/src/evasions/media.codecs/README.md +38 -0
  176. package/src/evasions/media.codecs/index.js +89 -0
  177. package/src/evasions/media.codecs/package.json +5 -0
  178. package/src/evasions/navigator.hardwareConcurrency/README.md +19 -0
  179. package/src/evasions/navigator.hardwareConcurrency/index.js +45 -0
  180. package/src/evasions/navigator.hardwareConcurrency/package.json +5 -0
  181. package/src/evasions/navigator.languages/README.md +17 -0
  182. package/src/evasions/navigator.languages/index.js +44 -0
  183. package/src/evasions/navigator.languages/package.json +5 -0
  184. package/src/evasions/navigator.permissions/README.md +16 -0
  185. package/src/evasions/navigator.permissions/index.js +66 -0
  186. package/src/evasions/navigator.permissions/package.json +5 -0
  187. package/src/evasions/navigator.plugins/README.md +24 -0
  188. package/src/evasions/navigator.plugins/data.json +48 -0
  189. package/src/evasions/navigator.plugins/functionMocks.js +47 -0
  190. package/src/evasions/navigator.plugins/index.js +98 -0
  191. package/src/evasions/navigator.plugins/magicArray.js +145 -0
  192. package/src/evasions/navigator.plugins/mimeTypes.js +18 -0
  193. package/src/evasions/navigator.plugins/package.json +5 -0
  194. package/src/evasions/navigator.plugins/plugins.js +18 -0
  195. package/src/evasions/navigator.vendor/README.md +36 -0
  196. package/src/evasions/navigator.vendor/index.js +64 -0
  197. package/src/evasions/navigator.vendor/package.json +5 -0
  198. package/src/evasions/navigator.webdriver/README.md +17 -0
  199. package/src/evasions/navigator.webdriver/index.js +48 -0
  200. package/src/evasions/navigator.webdriver/package.json +5 -0
  201. package/src/evasions/sourceurl/README.md +17 -0
  202. package/src/evasions/sourceurl/_fixtures/test.html +35 -0
  203. package/src/evasions/sourceurl/index.js +82 -0
  204. package/src/evasions/sourceurl/package.json +5 -0
  205. package/src/evasions/user-agent-override/README.md +53 -0
  206. package/src/evasions/user-agent-override/index.js +206 -0
  207. package/src/evasions/user-agent-override/package.json +5 -0
  208. package/src/evasions/webgl.vendor/README.md +20 -0
  209. package/src/evasions/webgl.vendor/index.js +57 -0
  210. package/src/evasions/webgl.vendor/package.json +5 -0
  211. package/src/evasions/window.outerdimensions/README.md +17 -0
  212. package/src/evasions/window.outerdimensions/index.js +42 -0
  213. package/src/evasions/window.outerdimensions/package.json +5 -0
  214. package/src/index.d.ts +111 -0
  215. package/src/index.ts +188 -0
  216. package/test/cat-and-mouse.test.ts +170 -0
  217. package/test/evasions/_utils/index.test.ts +710 -0
  218. package/test/evasions/chrome.app/index.test.ts +69 -0
  219. package/test/evasions/chrome.csi/index.test.ts +46 -0
  220. package/test/evasions/chrome.loadTimes/index.test.ts +61 -0
  221. package/test/evasions/chrome.runtime/index.test.ts +282 -0
  222. package/test/evasions/defaultArgs/index.test.ts +36 -0
  223. package/test/evasions/iframe.contentWindow/index.test.js +450 -0
  224. package/test/evasions/media.codecs/index.test.js +103 -0
  225. package/test/evasions/navigator.hardwareConcurrency/index.test.js +58 -0
  226. package/test/evasions/navigator.languages/index.test.js +101 -0
  227. package/test/evasions/navigator.permissions/index.test.js +104 -0
  228. package/test/evasions/navigator.plugins/index.test.js +55 -0
  229. package/test/evasions/navigator.plugins/mimeTypes.test.js +220 -0
  230. package/test/evasions/navigator.plugins/plugins.test.js +181 -0
  231. package/test/evasions/navigator.vendor/index.test.js +68 -0
  232. package/test/evasions/navigator.webdriver/index.test.js +47 -0
  233. package/test/evasions/sourceurl/_fixtures/test.html +35 -0
  234. package/test/evasions/sourceurl/index.test.js +62 -0
  235. package/test/evasions/user-agent-override/index.test.js +338 -0
  236. package/test/evasions/webgl.vendor/index.test.js +220 -0
  237. package/test/fixtures/dummy-with-service-worker.html +22 -0
  238. package/test/fixtures/dummy.html +11 -0
  239. package/test/fixtures/sw.js +1 -0
  240. package/test/fpscanner.test.ts +54 -0
  241. package/test/index.test.ts +51 -0
  242. package/test/service-worker.test.ts +112 -0
  243. package/test/stealth/_results/_thumbs/headful-chrome-stealth.js.png +0 -0
  244. package/test/stealth/_results/_thumbs/headful-chrome-vanilla.js.png +0 -0
  245. package/test/stealth/_results/_thumbs/headful-chromium-stealth.js.png +0 -0
  246. package/test/stealth/_results/_thumbs/headful-chromium-vanilla.js.png +0 -0
  247. package/test/stealth/_results/_thumbs/headless-chrome-stealth.js.png +0 -0
  248. package/test/stealth/_results/_thumbs/headless-chrome-vanilla.js.png +0 -0
  249. package/test/stealth/_results/_thumbs/headless-chromium-stealth.js.png +0 -0
  250. package/test/stealth/_results/_thumbs/headless-chromium-vanilla.js.png +0 -0
  251. package/test/stealth/_results/headful-chrome-stealth.js.png +0 -0
  252. package/test/stealth/_results/headful-chrome-vanilla.js.png +0 -0
  253. package/test/stealth/_results/headful-chromium-stealth.js.png +0 -0
  254. package/test/stealth/_results/headful-chromium-vanilla.js.png +0 -0
  255. package/test/stealth/_results/headless-chrome-stealth.js.png +0 -0
  256. package/test/stealth/_results/headless-chrome-vanilla.js.png +0 -0
  257. package/test/stealth/_results/headless-chromium-stealth.js.png +0 -0
  258. package/test/stealth/_results/headless-chromium-vanilla.js.png +0 -0
  259. package/test/stealth/headful-chrome-stealth.js +25 -0
  260. package/test/stealth/headful-chrome-vanilla.js +23 -0
  261. package/test/stealth/headful-chromium-stealth.js +22 -0
  262. package/test/stealth/headful-chromium-vanilla.js +20 -0
  263. package/test/stealth/headless-chrome-stealth.js +25 -0
  264. package/test/stealth/headless-chrome-vanilla.js +23 -0
  265. package/test/stealth/headless-chromium-stealth.js +22 -0
  266. package/test/stealth/headless-chromium-vanilla.js +20 -0
  267. package/test/util.js +82 -0
  268. package/tsconfig.json +10 -0
  269. package/tsconfig.tsbuildinfo +1 -0
  270. package/vitest.config.ts +28 -0
@@ -0,0 +1,97 @@
1
+ import { PuppeteerExtraPlugin } from '@zorilla/puppeteer-extra-plugin';
2
+ import withUtils from '../_utils/withUtils.js';
3
+
4
+ /**
5
+ * Mock the `chrome.app` object if not available (e.g. when running headless).
6
+ */
7
+ class Plugin extends PuppeteerExtraPlugin {
8
+ constructor(opts = {}) {
9
+ super(opts);
10
+ }
11
+
12
+ get name() {
13
+ return 'stealth/evasions/chrome.app';
14
+ }
15
+
16
+ async onPageCreated(page) {
17
+ await withUtils(page).evaluateOnNewDocument(utils => {
18
+ if (!window.chrome) {
19
+ // Use the exact property descriptor found in headful Chrome
20
+ // fetch it via `Object.getOwnPropertyDescriptor(window, 'chrome')`
21
+ Object.defineProperty(window, 'chrome', {
22
+ writable: true,
23
+ enumerable: true,
24
+ configurable: false, // note!
25
+ value: {}, // We'll extend that later
26
+ });
27
+ }
28
+
29
+ // That means we're running headful and don't need to mock anything
30
+ if ('app' in window.chrome) {
31
+ return; // Nothing to do here
32
+ }
33
+
34
+ const makeError = {
35
+ ErrorInInvocation: fn => {
36
+ const err = new TypeError(`Error in invocation of app.${fn}()`);
37
+ return utils.stripErrorWithAnchor(
38
+ err,
39
+ `at ${fn} (eval at <anonymous>`
40
+ );
41
+ },
42
+ };
43
+
44
+ // There's a some static data in that property which doesn't seem to change,
45
+ // we should periodically check for updates: `JSON.stringify(window.app, null, 2)`
46
+ const STATIC_DATA = JSON.parse(
47
+ `
48
+ {
49
+ "isInstalled": false,
50
+ "InstallState": {
51
+ "DISABLED": "disabled",
52
+ "INSTALLED": "installed",
53
+ "NOT_INSTALLED": "not_installed"
54
+ },
55
+ "RunningState": {
56
+ "CANNOT_RUN": "cannot_run",
57
+ "READY_TO_RUN": "ready_to_run",
58
+ "RUNNING": "running"
59
+ }
60
+ }
61
+ `.trim()
62
+ );
63
+
64
+ window.chrome.app = {
65
+ ...STATIC_DATA,
66
+
67
+ get isInstalled() {
68
+ return false;
69
+ },
70
+
71
+ getDetails: function getDetails() {
72
+ if (arguments.length) {
73
+ throw makeError.ErrorInInvocation(`getDetails`);
74
+ }
75
+ return null;
76
+ },
77
+ getIsInstalled: function getIsInstalled() {
78
+ if (arguments.length) {
79
+ throw makeError.ErrorInInvocation(`getIsInstalled`);
80
+ }
81
+ return false;
82
+ },
83
+ runningState: function runningState() {
84
+ if (arguments.length) {
85
+ throw makeError.ErrorInInvocation(`runningState`);
86
+ }
87
+ return 'cannot_run';
88
+ },
89
+ };
90
+ utils.patchToStringNested(window.chrome.app);
91
+ });
92
+ }
93
+ }
94
+
95
+ export default function (pluginConfig) {
96
+ return new Plugin(pluginConfig);
97
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "private": true,
3
+ "type": "module",
4
+ "main": "index.js"
5
+ }
@@ -0,0 +1,29 @@
1
+ ## API
2
+
3
+
4
+ #### Table of Contents
5
+
6
+ - [class: Plugin](#class-plugin)
7
+
8
+ ### class: [Plugin](https://github.com/zorillajs/zorilla/blob/e6133619b051febed630ada35241664eba59b9fa/packages/puppeteer-extra-plugin-stealth/evasions/chrome.csi/index.js#L25-L70)
9
+
10
+ - `opts` (optional, default `{}`)
11
+
12
+ **Extends: PuppeteerExtraPlugin**
13
+
14
+ Mock the `chrome.csi` function if not available (e.g. when running headless).
15
+ It's a deprecated (but unfortunately still existing) chrome specific API to fetch browser timings.
16
+
17
+ Internally chromium switched the implementation to use the WebPerformance API,
18
+ so we can do the same to create a fully functional mock. :-)
19
+
20
+ Note: We're using the deprecated PerformanceTiming API instead of the new Navigation Timing Level 2 API on purpopse.
21
+
22
+ - **See: <https://bugs.chromium.org/p/chromium/issues/detail?id=113048>**
23
+ - **See: <https://codereview.chromium.org/2456293003/>**
24
+ - **See: <https://developers.google.com/web/updates/2017/12/chrome-loadtimes-deprecated>**
25
+ - **See: <https://developer.mozilla.org/en-US/docs/Web/API/PerformanceTiming>**
26
+ - **See: <https://source.chromium.org/chromium/chromium/src/+/master:chrome/renderer/loadtimes_extension_bindings.cc;l=124?q=loadtimes&ss=chromium>**
27
+ - **See: `chrome.loadTimes` evasion**
28
+
29
+ ---
@@ -0,0 +1,69 @@
1
+ import { PuppeteerExtraPlugin } from '@zorilla/puppeteer-extra-plugin';
2
+
3
+ import withUtils from '../_utils/withUtils.js';
4
+
5
+ /**
6
+ * Mock the `chrome.csi` function if not available (e.g. when running headless).
7
+ * It's a deprecated (but unfortunately still existing) chrome specific API to fetch browser timings.
8
+ *
9
+ * Internally chromium switched the implementation to use the WebPerformance API,
10
+ * so we can do the same to create a fully functional mock. :-)
11
+ *
12
+ * Note: We're using the deprecated PerformanceTiming API instead of the new Navigation Timing Level 2 API on purpopse.
13
+ *
14
+ * @see https://bugs.chromium.org/p/chromium/issues/detail?id=113048
15
+ * @see https://codereview.chromium.org/2456293003/
16
+ * @see https://developers.google.com/web/updates/2017/12/chrome-loadtimes-deprecated
17
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PerformanceTiming
18
+ * @see https://source.chromium.org/chromium/chromium/src/+/master:chrome/renderer/loadtimes_extension_bindings.cc;l=124?q=loadtimes&ss=chromium
19
+ * @see `chrome.loadTimes` evasion
20
+ *
21
+ */
22
+ class Plugin extends PuppeteerExtraPlugin {
23
+ constructor(opts = {}) {
24
+ super(opts);
25
+ }
26
+
27
+ get name() {
28
+ return 'stealth/evasions/chrome.csi';
29
+ }
30
+
31
+ async onPageCreated(page) {
32
+ await withUtils(page).evaluateOnNewDocument(utils => {
33
+ if (!window.chrome) {
34
+ // Use the exact property descriptor found in headful Chrome
35
+ // fetch it via `Object.getOwnPropertyDescriptor(window, 'chrome')`
36
+ Object.defineProperty(window, 'chrome', {
37
+ writable: true,
38
+ enumerable: true,
39
+ configurable: false, // note!
40
+ value: {}, // We'll extend that later
41
+ });
42
+ }
43
+
44
+ // That means we're running headful and don't need to mock anything
45
+ if ('csi' in window.chrome) {
46
+ return; // Nothing to do here
47
+ }
48
+
49
+ // Check that the Navigation Timing API v1 is available, we need that
50
+ if (!window.performance || !window.performance.timing) {
51
+ return;
52
+ }
53
+
54
+ const { timing } = window.performance;
55
+
56
+ window.chrome.csi = () => ({
57
+ onloadT: timing.domContentLoadedEventEnd,
58
+ startE: timing.navigationStart,
59
+ pageT: Date.now() - timing.navigationStart,
60
+ tran: 15, // Transition type or something
61
+ });
62
+ utils.patchToString(window.chrome.csi);
63
+ });
64
+ }
65
+ }
66
+
67
+ export default function (pluginConfig) {
68
+ return new Plugin(pluginConfig);
69
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "private": true,
3
+ "type": "module",
4
+ "main": "index.js"
5
+ }
@@ -0,0 +1,27 @@
1
+ ## API
2
+
3
+
4
+ #### Table of Contents
5
+
6
+ - [class: Plugin](#class-plugin)
7
+
8
+ ### class: [Plugin](https://github.com/zorillajs/zorilla/blob/e6133619b051febed630ada35241664eba59b9fa/packages/puppeteer-extra-plugin-stealth/evasions/chrome.loadTimes/index.js#L23-L164)
9
+
10
+ - `opts` (optional, default `{}`)
11
+
12
+ **Extends: PuppeteerExtraPlugin**
13
+
14
+ Mock the `chrome.loadTimes` function if not available (e.g. when running headless).
15
+ It's a deprecated (but unfortunately still existing) chrome specific API to fetch browser timings and connection info.
16
+
17
+ Internally chromium switched the implementation to use the WebPerformance API,
18
+ so we can do the same to create a fully functional mock. :-)
19
+
20
+ Note: We're using the deprecated PerformanceTiming API instead of the new Navigation Timing Level 2 API on purpopse.
21
+
22
+ - **See: <https://developers.google.com/web/updates/2017/12/chrome-loadtimes-deprecated>**
23
+ - **See: <https://developer.mozilla.org/en-US/docs/Web/API/PerformanceTiming>**
24
+ - **See: <https://source.chromium.org/chromium/chromium/src/+/master:chrome/renderer/loadtimes_extension_bindings.cc;l=124?q=loadtimes&ss=chromium>**
25
+ - **See: `chrome.csi` evasion**
26
+
27
+ ---
@@ -0,0 +1,163 @@
1
+ import { PuppeteerExtraPlugin } from '@zorilla/puppeteer-extra-plugin';
2
+
3
+ import withUtils from '../_utils/withUtils.js';
4
+
5
+ /**
6
+ * Mock the `chrome.loadTimes` function if not available (e.g. when running headless).
7
+ * It's a deprecated (but unfortunately still existing) chrome specific API to fetch browser timings and connection info.
8
+ *
9
+ * Internally chromium switched the implementation to use the WebPerformance API,
10
+ * so we can do the same to create a fully functional mock. :-)
11
+ *
12
+ * Note: We're using the deprecated PerformanceTiming API instead of the new Navigation Timing Level 2 API on purpopse.
13
+ *
14
+ * @see https://developers.google.com/web/updates/2017/12/chrome-loadtimes-deprecated
15
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PerformanceTiming
16
+ * @see https://source.chromium.org/chromium/chromium/src/+/master:chrome/renderer/loadtimes_extension_bindings.cc;l=124?q=loadtimes&ss=chromium
17
+ * @see `chrome.csi` evasion
18
+ *
19
+ */
20
+ class Plugin extends PuppeteerExtraPlugin {
21
+ constructor(opts = {}) {
22
+ super(opts);
23
+ }
24
+
25
+ get name() {
26
+ return 'stealth/evasions/chrome.loadTimes';
27
+ }
28
+
29
+ async onPageCreated(page) {
30
+ await withUtils(page).evaluateOnNewDocument(
31
+ (utils, { opts }) => {
32
+ if (!window.chrome) {
33
+ // Use the exact property descriptor found in headful Chrome
34
+ // fetch it via `Object.getOwnPropertyDescriptor(window, 'chrome')`
35
+ Object.defineProperty(window, 'chrome', {
36
+ writable: true,
37
+ enumerable: true,
38
+ configurable: false, // note!
39
+ value: {}, // We'll extend that later
40
+ });
41
+ }
42
+
43
+ // That means we're running headful and don't need to mock anything
44
+ if ('loadTimes' in window.chrome) {
45
+ return; // Nothing to do here
46
+ }
47
+
48
+ // Check that the Navigation Timing API v1 + v2 is available, we need that
49
+ if (
50
+ !window.performance ||
51
+ !window.performance.timing ||
52
+ !window.PerformancePaintTiming
53
+ ) {
54
+ return;
55
+ }
56
+
57
+ const { performance } = window;
58
+
59
+ // Some stuff is not available on about:blank as it requires a navigation to occur,
60
+ // let's harden the code to not fail then:
61
+ const ntEntryFallback = {
62
+ nextHopProtocol: 'h2',
63
+ type: 'other',
64
+ };
65
+
66
+ // The API exposes some funky info regarding the connection
67
+ const protocolInfo = {
68
+ get connectionInfo() {
69
+ const ntEntry =
70
+ performance.getEntriesByType('navigation')[0] || ntEntryFallback;
71
+ return ntEntry.nextHopProtocol;
72
+ },
73
+ get npnNegotiatedProtocol() {
74
+ // NPN is deprecated in favor of ALPN, but this implementation returns the
75
+ // HTTP/2 or HTTP2+QUIC/39 requests negotiated via ALPN.
76
+ const ntEntry =
77
+ performance.getEntriesByType('navigation')[0] || ntEntryFallback;
78
+ return ['h2', 'hq'].includes(ntEntry.nextHopProtocol)
79
+ ? ntEntry.nextHopProtocol
80
+ : 'unknown';
81
+ },
82
+ get navigationType() {
83
+ const ntEntry =
84
+ performance.getEntriesByType('navigation')[0] || ntEntryFallback;
85
+ return ntEntry.type;
86
+ },
87
+ get wasAlternateProtocolAvailable() {
88
+ // The Alternate-Protocol header is deprecated in favor of Alt-Svc
89
+ // (https://www.mnot.net/blog/2016/03/09/alt-svc), so technically this
90
+ // should always return false.
91
+ return false;
92
+ },
93
+ get wasFetchedViaSpdy() {
94
+ // SPDY is deprecated in favor of HTTP/2, but this implementation returns
95
+ // true for HTTP/2 or HTTP2+QUIC/39 as well.
96
+ const ntEntry =
97
+ performance.getEntriesByType('navigation')[0] || ntEntryFallback;
98
+ return ['h2', 'hq'].includes(ntEntry.nextHopProtocol);
99
+ },
100
+ get wasNpnNegotiated() {
101
+ // NPN is deprecated in favor of ALPN, but this implementation returns true
102
+ // for HTTP/2 or HTTP2+QUIC/39 requests negotiated via ALPN.
103
+ const ntEntry =
104
+ performance.getEntriesByType('navigation')[0] || ntEntryFallback;
105
+ return ['h2', 'hq'].includes(ntEntry.nextHopProtocol);
106
+ },
107
+ };
108
+
109
+ const { timing } = window.performance;
110
+
111
+ // Truncate number to specific number of decimals, most of the `loadTimes` stuff has 3
112
+ function toFixed(num, fixed) {
113
+ var re = new RegExp('^-?\\d+(?:.\\d{0,' + (fixed || -1) + '})?');
114
+ return num.toString().match(re)[0];
115
+ }
116
+
117
+ const timingInfo = {
118
+ get firstPaintAfterLoadTime() {
119
+ // This was never actually implemented and always returns 0.
120
+ return 0;
121
+ },
122
+ get requestTime() {
123
+ return timing.navigationStart / 1000;
124
+ },
125
+ get startLoadTime() {
126
+ return timing.navigationStart / 1000;
127
+ },
128
+ get commitLoadTime() {
129
+ return timing.responseStart / 1000;
130
+ },
131
+ get finishDocumentLoadTime() {
132
+ return timing.domContentLoadedEventEnd / 1000;
133
+ },
134
+ get finishLoadTime() {
135
+ return timing.loadEventEnd / 1000;
136
+ },
137
+ get firstPaintTime() {
138
+ const fpEntry = performance.getEntriesByType('paint')[0] || {
139
+ startTime: timing.loadEventEnd / 1000, // Fallback if no navigation occured (`about:blank`)
140
+ };
141
+ return toFixed(
142
+ (fpEntry.startTime + performance.timeOrigin) / 1000,
143
+ 3
144
+ );
145
+ },
146
+ };
147
+
148
+ window.chrome.loadTimes = () => ({
149
+ ...protocolInfo,
150
+ ...timingInfo,
151
+ });
152
+ utils.patchToString(window.chrome.loadTimes);
153
+ },
154
+ {
155
+ opts: this.opts,
156
+ }
157
+ );
158
+ }
159
+ }
160
+
161
+ export default function (pluginConfig) {
162
+ return new Plugin(pluginConfig);
163
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "private": true,
3
+ "type": "module",
4
+ "main": "index.js"
5
+ }
@@ -0,0 +1,32 @@
1
+ ## API
2
+
3
+
4
+ #### Table of Contents
5
+
6
+ - [class: Plugin](#class-plugin)
7
+ - [sendMessageHandler()](#sendmessagehandler)
8
+ - [connectHandler()](#connecthandler)
9
+
10
+ ### class: [Plugin](https://github.com/zorillajs/zorilla/blob/e6133619b051febed630ada35241664eba59b9fa/packages/puppeteer-extra-plugin-stealth/evasions/chrome.runtime/index.js#L13-L251)
11
+
12
+ - `opts` (optional, default `{}`)
13
+
14
+ **Extends: PuppeteerExtraPlugin**
15
+
16
+ Mock the `chrome.runtime` object if not available (e.g. when running headless) and on a secure site.
17
+
18
+ ---
19
+
20
+ ### [sendMessageHandler()](https://github.com/zorillajs/zorilla/blob/e6133619b051febed630ada35241664eba59b9fa/packages/puppeteer-extra-plugin-stealth/evasions/chrome.runtime/index.js#L80-L123)
21
+
22
+ Mock `chrome.runtime.sendMessage`
23
+
24
+ ---
25
+
26
+ ### [connectHandler()](https://github.com/zorillajs/zorilla/blob/e6133619b051febed630ada35241664eba59b9fa/packages/puppeteer-extra-plugin-stealth/evasions/chrome.runtime/index.js#L136-L210)
27
+
28
+ Mock `chrome.runtime.connect`
29
+
30
+ - **See: <https://developer.chrome.com/apps/runtime#method-connect>**
31
+
32
+ ---