@js-recon/js-recon 1.4.3 → 2.0.1-alpha.1

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 (461) hide show
  1. package/.claude-sync/sync.sh +1 -1
  2. package/.github/workflows/build-and-prettify.yaml +55 -25
  3. package/.github/workflows/codeql.yml +33 -0
  4. package/.github/workflows/exploit-smoke-test.yaml +160 -0
  5. package/.github/workflows/framework-detection-smoke-test.yaml +135 -0
  6. package/.github/workflows/plumber.yml +20 -0
  7. package/.github/workflows/promote-js-recon.yml +5 -3
  8. package/.github/workflows/publish-js-recon.yml +6 -1
  9. package/.github/workflows/rules-smoke-test.yaml +15 -7
  10. package/.nvmrc +1 -0
  11. package/.plumber.yaml +151 -0
  12. package/CHANGELOG.md +54 -0
  13. package/CONTRIBUTING.md +32 -0
  14. package/Dockerfile +1 -0
  15. package/README.md +55 -2
  16. package/SECURITY.md +1 -1
  17. package/build/analyze/engine/astEngine.js +10 -9
  18. package/build/analyze/engine/astEngine.js.map +1 -1
  19. package/build/analyze/engine/csMastSEngine.js +6 -6
  20. package/build/analyze/engine/csMastSEngine.js.map +1 -1
  21. package/build/analyze/engine/index.js +1 -1
  22. package/build/analyze/engine/index.js.map +1 -1
  23. package/build/analyze/engine/requestEngine.js +6 -6
  24. package/build/analyze/engine/requestEngine.js.map +1 -1
  25. package/build/analyze/helpers/determineCompatibleVersion.js +70 -0
  26. package/build/analyze/helpers/determineCompatibleVersion.js.map +1 -0
  27. package/build/analyze/helpers/engineHelpers/findMemberExpressionAssignment.js +9 -3
  28. package/build/analyze/helpers/engineHelpers/findMemberExpressionAssignment.js.map +1 -1
  29. package/build/analyze/helpers/initRules.js +42 -24
  30. package/build/analyze/helpers/initRules.js.map +1 -1
  31. package/build/analyze/helpers/outputHelper.js +3 -3
  32. package/build/analyze/helpers/outputHelper.js.map +1 -1
  33. package/build/analyze/helpers/schemas.js +1 -0
  34. package/build/analyze/helpers/schemas.js.map +1 -1
  35. package/build/analyze/helpers/validate.js +9 -5
  36. package/build/analyze/helpers/validate.js.map +1 -1
  37. package/build/analyze/index.js +26 -16
  38. package/build/analyze/index.js.map +1 -1
  39. package/build/cliProgram.js +710 -0
  40. package/build/cliProgram.js.map +1 -0
  41. package/build/completion/index.js +245 -339
  42. package/build/completion/index.js.map +1 -1
  43. package/build/completion/introspect.js +17 -0
  44. package/build/completion/introspect.js.map +1 -0
  45. package/build/config/applicationConfig.js +494 -0
  46. package/build/config/applicationConfig.js.map +1 -0
  47. package/build/cs_mast/index.js +18 -17
  48. package/build/cs_mast/index.js.map +1 -1
  49. package/build/endpoints/gen_report/gen_json.js +2 -2
  50. package/build/endpoints/gen_report/gen_json.js.map +1 -1
  51. package/build/endpoints/gen_report/utility/iterate_n_store.js +1 -1
  52. package/build/endpoints/gen_report/utility/iterate_n_store.js.map +1 -1
  53. package/build/endpoints/index.js +12 -12
  54. package/build/endpoints/index.js.map +1 -1
  55. package/build/endpoints/next_js/client_jsFilesHref.js +3 -3
  56. package/build/endpoints/next_js/client_jsFilesHref.js.map +1 -1
  57. package/build/endpoints/next_js/client_jsonParse.js +4 -4
  58. package/build/endpoints/next_js/client_jsonParse.js.map +1 -1
  59. package/build/endpoints/next_js/client_mappedJsonFile.js +6 -6
  60. package/build/endpoints/next_js/client_mappedJsonFile.js.map +1 -1
  61. package/build/endpoints/next_js/client_subsequentRequests.js +5 -5
  62. package/build/endpoints/next_js/client_subsequentRequests.js.map +1 -1
  63. package/build/exploit/angular/cve-2026-68945.js +61 -0
  64. package/build/exploit/angular/cve-2026-68945.js.map +1 -0
  65. package/build/exploit/index.js +60 -0
  66. package/build/exploit/index.js.map +1 -0
  67. package/build/exploit/next/cve-2025-29927.js +65 -0
  68. package/build/exploit/next/cve-2025-29927.js.map +1 -0
  69. package/build/exploit/next/cve-2026-64645.js +51 -0
  70. package/build/exploit/next/cve-2026-64645.js.map +1 -0
  71. package/build/exploit/nuxt/cve-2026-71315.js +47 -0
  72. package/build/exploit/nuxt/cve-2026-71315.js.map +1 -0
  73. package/build/exploit/nuxt/cve-2026-71320.js +114 -0
  74. package/build/exploit/nuxt/cve-2026-71320.js.map +1 -0
  75. package/build/exploit/react/cve-2025-55182.js +116 -0
  76. package/build/exploit/react/cve-2025-55182.js.map +1 -0
  77. package/build/exploit/registry/cveRegistry.js +115 -0
  78. package/build/exploit/registry/cveRegistry.js.map +1 -0
  79. package/build/exploit/types.js +2 -0
  80. package/build/exploit/types.js.map +1 -0
  81. package/build/exploit/utility/compareResponses.js +29 -0
  82. package/build/exploit/utility/compareResponses.js.map +1 -0
  83. package/build/exploit/utility/outputHelper.js +22 -0
  84. package/build/exploit/utility/outputHelper.js.map +1 -0
  85. package/build/exploit/utility/rawRequest.js +54 -0
  86. package/build/exploit/utility/rawRequest.js.map +1 -0
  87. package/build/exploit/utility/sandboxRce.js +49 -0
  88. package/build/exploit/utility/sandboxRce.js.map +1 -0
  89. package/build/exploit/utility/toEngineOutput.js +28 -0
  90. package/build/exploit/utility/toEngineOutput.js.map +1 -0
  91. package/build/exploit/utility/wsClient.js +78 -0
  92. package/build/exploit/utility/wsClient.js.map +1 -0
  93. package/build/exploit/vite/cve-2025-30208.js +44 -0
  94. package/build/exploit/vite/cve-2025-30208.js.map +1 -0
  95. package/build/exploit/vite/cve-2026-39363.js +60 -0
  96. package/build/exploit/vite/cve-2026-39363.js.map +1 -0
  97. package/build/fingerprint/index.js +11 -21
  98. package/build/fingerprint/index.js.map +1 -1
  99. package/build/globalConfig.js +2 -2
  100. package/build/globalConfig.js.map +1 -1
  101. package/build/index.js +16 -555
  102. package/build/index.js.map +1 -1
  103. package/build/lazyLoad/angular/angular_getFromPageSource.js +3 -3
  104. package/build/lazyLoad/angular/angular_getFromPageSource.js.map +1 -1
  105. package/build/lazyLoad/downloadFilesUtil.js +51 -40
  106. package/build/lazyLoad/downloadFilesUtil.js.map +1 -1
  107. package/build/lazyLoad/downloadQueue.js +154 -50
  108. package/build/lazyLoad/downloadQueue.js.map +1 -1
  109. package/build/lazyLoad/downloadResponse.js +113 -0
  110. package/build/lazyLoad/downloadResponse.js.map +1 -0
  111. package/build/lazyLoad/generic/generic_crawlPages.js +186 -0
  112. package/build/lazyLoad/generic/generic_crawlPages.js.map +1 -0
  113. package/build/lazyLoad/generic/generic_downloadFiles.js +81 -0
  114. package/build/lazyLoad/generic/generic_downloadFiles.js.map +1 -0
  115. package/build/lazyLoad/generic/generic_extractLinks.js +79 -0
  116. package/build/lazyLoad/generic/generic_extractLinks.js.map +1 -0
  117. package/build/lazyLoad/generic/generic_getScriptTags.js +152 -0
  118. package/build/lazyLoad/generic/generic_getScriptTags.js.map +1 -0
  119. package/build/lazyLoad/generic/generic_importMapDiscovery.js +87 -0
  120. package/build/lazyLoad/generic/generic_importMapDiscovery.js.map +1 -0
  121. package/build/lazyLoad/generic/generic_resolveScope.js +61 -0
  122. package/build/lazyLoad/generic/generic_resolveScope.js.map +1 -0
  123. package/build/lazyLoad/generic/generic_scanAttributesForJs.js +122 -0
  124. package/build/lazyLoad/generic/generic_scanAttributesForJs.js.map +1 -0
  125. package/build/lazyLoad/generic/generic_stringsDiscovery.js +103 -0
  126. package/build/lazyLoad/generic/generic_stringsDiscovery.js.map +1 -0
  127. package/build/lazyLoad/generic/generic_webpackChunkPaths.js +56 -0
  128. package/build/lazyLoad/generic/generic_webpackChunkPaths.js.map +1 -0
  129. package/build/lazyLoad/globals.js +35 -0
  130. package/build/lazyLoad/globals.js.map +1 -1
  131. package/build/lazyLoad/index.js +311 -175
  132. package/build/lazyLoad/index.js.map +1 -1
  133. package/build/lazyLoad/methodFilter.js +2 -0
  134. package/build/lazyLoad/methodFilter.js.map +1 -1
  135. package/build/lazyLoad/next_js/NextJsCrawler.js +39 -14
  136. package/build/lazyLoad/next_js/NextJsCrawler.js.map +1 -1
  137. package/build/lazyLoad/next_js/next_GetJSScript.js +3 -3
  138. package/build/lazyLoad/next_js/next_GetJSScript.js.map +1 -1
  139. package/build/lazyLoad/next_js/next_GetLazyResourcesBuildManifestJs.js +4 -4
  140. package/build/lazyLoad/next_js/next_GetLazyResourcesBuildManifestJs.js.map +1 -1
  141. package/build/lazyLoad/next_js/next_GetLazyResourcesWebpackJs.js +21 -20
  142. package/build/lazyLoad/next_js/next_GetLazyResourcesWebpackJs.js.map +1 -1
  143. package/build/lazyLoad/next_js/next_SubsequentRequests.js +100 -99
  144. package/build/lazyLoad/next_js/next_SubsequentRequests.js.map +1 -1
  145. package/build/lazyLoad/next_js/next_bruteForceJsFiles.js +1 -1
  146. package/build/lazyLoad/next_js/next_bruteForceJsFiles.js.map +1 -1
  147. package/build/lazyLoad/next_js/next_buildId.js +2 -2
  148. package/build/lazyLoad/next_js/next_buildId.js.map +1 -1
  149. package/build/lazyLoad/next_js/next_getClientSidePaths.js +2 -2
  150. package/build/lazyLoad/next_js/next_getClientSidePaths.js.map +1 -1
  151. package/build/lazyLoad/next_js/next_parseLayoutJs.js +6 -6
  152. package/build/lazyLoad/next_js/next_parseLayoutJs.js.map +1 -1
  153. package/build/lazyLoad/next_js/next_promiseResolve.js +1 -1
  154. package/build/lazyLoad/next_js/next_promiseResolve.js.map +1 -1
  155. package/build/lazyLoad/next_js/next_routerStateForge.js +341 -0
  156. package/build/lazyLoad/next_js/next_routerStateForge.js.map +1 -0
  157. package/build/lazyLoad/next_js/next_scriptTagsSubsequentRequests.js +1 -1
  158. package/build/lazyLoad/next_js/next_scriptTagsSubsequentRequests.js.map +1 -1
  159. package/build/lazyLoad/next_js/next_serverActionIdScan.js +77 -0
  160. package/build/lazyLoad/next_js/next_serverActionIdScan.js.map +1 -0
  161. package/build/lazyLoad/nuxt_js/nuxt_astParse.js +15 -13
  162. package/build/lazyLoad/nuxt_js/nuxt_astParse.js.map +1 -1
  163. package/build/lazyLoad/nuxt_js/nuxt_getBuildsManifest.js +2 -2
  164. package/build/lazyLoad/nuxt_js/nuxt_getBuildsManifest.js.map +1 -1
  165. package/build/lazyLoad/nuxt_js/nuxt_getFromPageSource.js +3 -3
  166. package/build/lazyLoad/nuxt_js/nuxt_getFromPageSource.js.map +1 -1
  167. package/build/lazyLoad/nuxt_js/nuxt_stringAnalysisJSFiles.js +2 -2
  168. package/build/lazyLoad/nuxt_js/nuxt_stringAnalysisJSFiles.js.map +1 -1
  169. package/build/lazyLoad/outputPath.js +92 -0
  170. package/build/lazyLoad/outputPath.js.map +1 -0
  171. package/build/lazyLoad/react/react_followImports.js +15 -5
  172. package/build/lazyLoad/react/react_followImports.js.map +1 -1
  173. package/build/lazyLoad/react/react_getScriptTags.js +11 -6
  174. package/build/lazyLoad/react/react_getScriptTags.js.map +1 -1
  175. package/build/lazyLoad/react/react_sourcemapUrls.js +3 -2
  176. package/build/lazyLoad/react/react_sourcemapUrls.js.map +1 -1
  177. package/build/lazyLoad/react/react_webpackChunkPaths.js +12 -203
  178. package/build/lazyLoad/react/react_webpackChunkPaths.js.map +1 -1
  179. package/build/lazyLoad/researchUtils.js +15 -0
  180. package/build/lazyLoad/researchUtils.js.map +1 -0
  181. package/build/lazyLoad/shared/webpackChunkParsers.js +218 -0
  182. package/build/lazyLoad/shared/webpackChunkParsers.js.map +1 -0
  183. package/build/lazyLoad/stagnation/stagnationMonitor.js +63 -0
  184. package/build/lazyLoad/stagnation/stagnationMonitor.js.map +1 -0
  185. package/build/lazyLoad/svelte/svelte_discoverPagesFromJs.js +1 -0
  186. package/build/lazyLoad/svelte/svelte_discoverPagesFromJs.js.map +1 -1
  187. package/build/lazyLoad/svelte/svelte_getFromPageSource.js +5 -5
  188. package/build/lazyLoad/svelte/svelte_getFromPageSource.js.map +1 -1
  189. package/build/lazyLoad/svelte/svelte_getVersionJson.js +2 -2
  190. package/build/lazyLoad/svelte/svelte_getVersionJson.js.map +1 -1
  191. package/build/lazyLoad/svelte/svelte_recursivePageCrawl.js +6 -6
  192. package/build/lazyLoad/svelte/svelte_recursivePageCrawl.js.map +1 -1
  193. package/build/lazyLoad/svelte/svelte_stringAnalysisJSFiles.js +2 -2
  194. package/build/lazyLoad/svelte/svelte_stringAnalysisJSFiles.js.map +1 -1
  195. package/build/lazyLoad/techDetect/checkAngularJS.js +1 -1
  196. package/build/lazyLoad/techDetect/checkAngularJS.js.map +1 -1
  197. package/build/lazyLoad/techDetect/checkNuxtJS.js +3 -3
  198. package/build/lazyLoad/techDetect/checkNuxtJS.js.map +1 -1
  199. package/build/lazyLoad/techDetect/checkSvelte.js +0 -1
  200. package/build/lazyLoad/techDetect/checkSvelte.js.map +1 -1
  201. package/build/lazyLoad/techDetect/checkVueJS.js +6 -6
  202. package/build/lazyLoad/techDetect/checkVueJS.js.map +1 -1
  203. package/build/lazyLoad/techDetect/index.js +106 -60
  204. package/build/lazyLoad/techDetect/index.js.map +1 -1
  205. package/build/lazyLoad/vue/vue_RuntimeJs.js +20 -15
  206. package/build/lazyLoad/vue/vue_RuntimeJs.js.map +1 -1
  207. package/build/lazyLoad/vue/vue_SingleJsFileOnHome.js +10 -5
  208. package/build/lazyLoad/vue/vue_SingleJsFileOnHome.js.map +1 -1
  209. package/build/lazyLoad/vue/vue_discoverJsFiles.js +20 -17
  210. package/build/lazyLoad/vue/vue_discoverJsFiles.js.map +1 -1
  211. package/build/lazyLoad/vue/vue_getClientSidePaths.js +70 -46
  212. package/build/lazyLoad/vue/vue_getClientSidePaths.js.map +1 -1
  213. package/build/lazyLoad/vue/vue_jsImports.js +3 -2
  214. package/build/lazyLoad/vue/vue_jsImports.js.map +1 -1
  215. package/build/lazyLoad/vue/vue_pageSrc.js +3 -2
  216. package/build/lazyLoad/vue/vue_pageSrc.js.map +1 -1
  217. package/build/lazyLoad/vue/vue_reconstructSourceMaps.js +6 -5
  218. package/build/lazyLoad/vue/vue_reconstructSourceMaps.js.map +1 -1
  219. package/build/lazyLoad/vue/vue_recursiveClientSidePathDownload.js +10 -21
  220. package/build/lazyLoad/vue/vue_recursiveClientSidePathDownload.js.map +1 -1
  221. package/build/lazyLoad/vue/vue_severalJsFilesHome.js +5 -4
  222. package/build/lazyLoad/vue/vue_severalJsFilesHome.js.map +1 -1
  223. package/build/lazyLoad/vue/vue_stringJsFiles.js +28 -25
  224. package/build/lazyLoad/vue/vue_stringJsFiles.js.map +1 -1
  225. package/build/lazyLoad/vue/vue_viteMapDeps.js +8 -2
  226. package/build/lazyLoad/vue/vue_viteMapDeps.js.map +1 -1
  227. package/build/load/index.js +8 -7
  228. package/build/load/index.js.map +1 -1
  229. package/build/map/angular_js/getAngularConnections.js +6 -6
  230. package/build/map/angular_js/getAngularConnections.js.map +1 -1
  231. package/build/map/graphql/resolveGraphql.js +6 -6
  232. package/build/map/graphql/resolveGraphql.js.map +1 -1
  233. package/build/map/index.js +17 -17
  234. package/build/map/index.js.map +1 -1
  235. package/build/map/interactive-mode/commandHandler.js +278 -0
  236. package/build/map/interactive-mode/commandHandler.js.map +1 -0
  237. package/build/map/interactive-mode/commandHelpers.js +189 -0
  238. package/build/map/interactive-mode/commandHelpers.js.map +1 -0
  239. package/build/map/interactive-mode/esqueryGen.js.map +1 -0
  240. package/build/map/interactive-mode/helpMenu.js +11 -0
  241. package/build/map/interactive-mode/helpMenu.js.map +1 -0
  242. package/build/map/interactive-mode/inputPatch.js.map +1 -0
  243. package/build/map/interactive-mode/interactive.js +71 -0
  244. package/build/map/interactive-mode/interactive.js.map +1 -0
  245. package/build/map/interactive-mode/keybindings.js.map +1 -0
  246. package/build/map/interactive-mode/printer.js.map +1 -0
  247. package/build/map/interactive-mode/ui.js.map +1 -0
  248. package/build/map/next_js/getAxiosInstances.js +5 -5
  249. package/build/map/next_js/getAxiosInstances.js.map +1 -1
  250. package/build/map/next_js/getExports.js +4 -4
  251. package/build/map/next_js/getExports.js.map +1 -1
  252. package/build/map/next_js/getFetchInstances.js +10 -10
  253. package/build/map/next_js/getFetchInstances.js.map +1 -1
  254. package/build/map/next_js/getTurbopackConnections.js +8 -8
  255. package/build/map/next_js/getTurbopackConnections.js.map +1 -1
  256. package/build/map/next_js/getWebpackConnections.js +27 -16
  257. package/build/map/next_js/getWebpackConnections.js.map +1 -1
  258. package/build/map/next_js/interactive.js +3 -65
  259. package/build/map/next_js/interactive.js.map +1 -1
  260. package/build/map/next_js/interactive_helpers/commandHandler.js +38 -290
  261. package/build/map/next_js/interactive_helpers/commandHandler.js.map +1 -1
  262. package/build/map/next_js/interactive_helpers/commandHelpers.js +0 -184
  263. package/build/map/next_js/interactive_helpers/commandHelpers.js.map +1 -1
  264. package/build/map/next_js/interactive_helpers/helpMenu.js +2 -10
  265. package/build/map/next_js/interactive_helpers/helpMenu.js.map +1 -1
  266. package/build/map/next_js/resolveAxios.js +6 -6
  267. package/build/map/next_js/resolveAxios.js.map +1 -1
  268. package/build/map/next_js/resolveAxiosHelpers/findAxiosClients.js +4 -4
  269. package/build/map/next_js/resolveAxiosHelpers/findAxiosClients.js.map +1 -1
  270. package/build/map/next_js/resolveAxiosHelpers/findAxiosInstance.js +2 -2
  271. package/build/map/next_js/resolveAxiosHelpers/findAxiosInstance.js.map +1 -1
  272. package/build/map/next_js/resolveAxiosHelpers/findCrossChunkParams.js +5 -5
  273. package/build/map/next_js/resolveAxiosHelpers/findCrossChunkParams.js.map +1 -1
  274. package/build/map/next_js/resolveAxiosHelpers/handleAxiosCreate.js +10 -10
  275. package/build/map/next_js/resolveAxiosHelpers/handleAxiosCreate.js.map +1 -1
  276. package/build/map/next_js/resolveAxiosHelpers/handleZDotCreate.js +13 -9
  277. package/build/map/next_js/resolveAxiosHelpers/handleZDotCreate.js.map +1 -1
  278. package/build/map/next_js/resolveAxiosHelpers/processAxiosCall.js +12 -8
  279. package/build/map/next_js/resolveAxiosHelpers/processAxiosCall.js.map +1 -1
  280. package/build/map/next_js/resolveAxiosHelpers/processDirectAxiosCall.js +12 -8
  281. package/build/map/next_js/resolveAxiosHelpers/processDirectAxiosCall.js.map +1 -1
  282. package/build/map/next_js/resolveAxiosHelpers/processExportedEndpoints.js +11 -11
  283. package/build/map/next_js/resolveAxiosHelpers/processExportedEndpoints.js.map +1 -1
  284. package/build/map/next_js/resolveAxiosHelpers/traceAxiosInstanceExports.js +16 -16
  285. package/build/map/next_js/resolveAxiosHelpers/traceAxiosInstanceExports.js.map +1 -1
  286. package/build/map/next_js/resolveFetch.js +23 -23
  287. package/build/map/next_js/resolveFetch.js.map +1 -1
  288. package/build/map/next_js/resolveNewRequest.js +10 -10
  289. package/build/map/next_js/resolveNewRequest.js.map +1 -1
  290. package/build/map/next_js/resolveServerActions.js +4 -4
  291. package/build/map/next_js/resolveServerActions.js.map +1 -1
  292. package/build/map/next_js/utils.js +8 -2
  293. package/build/map/next_js/utils.js.map +1 -1
  294. package/build/map/react_js/getReactConnections.js +21 -14
  295. package/build/map/react_js/getReactConnections.js.map +1 -1
  296. package/build/map/svelte_js/interactive.js +3 -54
  297. package/build/map/svelte_js/interactive.js.map +1 -1
  298. package/build/map/vue_js/getViteConnections.js +23 -16
  299. package/build/map/vue_js/getViteConnections.js.map +1 -1
  300. package/build/map/vue_js/interactive.js +3 -63
  301. package/build/map/vue_js/interactive.js.map +1 -1
  302. package/build/map/vue_js/interactive_helpers/commandHandler.js +34 -283
  303. package/build/map/vue_js/interactive_helpers/commandHandler.js.map +1 -1
  304. package/build/map/vue_js/interactive_helpers/helpMenu.js +2 -10
  305. package/build/map/vue_js/interactive_helpers/helpMenu.js.map +1 -1
  306. package/build/map/vue_js/taint_utils.js +1 -1
  307. package/build/map/vue_js/taint_utils.js.map +1 -1
  308. package/build/map/vue_js/vue_resolveFetch.js +12 -12
  309. package/build/map/vue_js/vue_resolveFetch.js.map +1 -1
  310. package/build/map/vue_js/vue_resolveHttpClient.js +18 -18
  311. package/build/map/vue_js/vue_resolveHttpClient.js.map +1 -1
  312. package/build/map/vue_js/vue_resolveXhr.js +11 -11
  313. package/build/map/vue_js/vue_resolveXhr.js.map +1 -1
  314. package/build/mcp/chatOneShot.js +7 -7
  315. package/build/mcp/chatOneShot.js.map +1 -1
  316. package/build/mcp/claudeCodeCreds.js +5 -5
  317. package/build/mcp/claudeCodeCreds.js.map +1 -1
  318. package/build/mcp/cli.js +36 -35
  319. package/build/mcp/cli.js.map +1 -1
  320. package/build/mcp/config.js +4 -4
  321. package/build/mcp/config.js.map +1 -1
  322. package/build/mcp/index.js +7 -7
  323. package/build/mcp/index.js.map +1 -1
  324. package/build/mcp/mcpServer.js +3 -2
  325. package/build/mcp/mcpServer.js.map +1 -1
  326. package/build/mcp/skills.js +1 -0
  327. package/build/mcp/skills.js.map +1 -1
  328. package/build/mcp/tools.js +5 -4
  329. package/build/mcp/tools.js.map +1 -1
  330. package/build/proxy/checkFeasibility.js +8 -4
  331. package/build/proxy/checkFeasibility.js.map +1 -1
  332. package/build/proxy/checkFireWallBlocking.js +23 -13
  333. package/build/proxy/checkFireWallBlocking.js.map +1 -1
  334. package/build/proxy/detectBlockedResponse.js +205 -0
  335. package/build/proxy/detectBlockedResponse.js.map +1 -0
  336. package/build/proxy/genReq.js +434 -127
  337. package/build/proxy/genReq.js.map +1 -1
  338. package/build/proxy/index.js +3 -3
  339. package/build/proxy/index.js.map +1 -1
  340. package/build/proxy/oxylabsFallback.js +110 -0
  341. package/build/proxy/oxylabsFallback.js.map +1 -0
  342. package/build/proxy/proxyFeasibilityRequest.js +11 -9
  343. package/build/proxy/proxyFeasibilityRequest.js.map +1 -1
  344. package/build/refactor/index.js +83 -79
  345. package/build/refactor/index.js.map +1 -1
  346. package/build/refactor/next/index.js +69 -26
  347. package/build/refactor/next/index.js.map +1 -1
  348. package/build/refactor/next/transform.js +11 -4
  349. package/build/refactor/next/transform.js.map +1 -1
  350. package/build/refactor/next/validator.js +7 -7
  351. package/build/refactor/next/validator.js.map +1 -1
  352. package/build/refactor/react/index.js +13 -13
  353. package/build/refactor/react/index.js.map +1 -1
  354. package/build/refactor/react/validator.js +7 -7
  355. package/build/refactor/react/validator.js.map +1 -1
  356. package/build/refactor/react-vite/index.js +3 -3
  357. package/build/refactor/react-vite/index.js.map +1 -1
  358. package/build/refactor/remote/hf-client.js +1 -0
  359. package/build/refactor/remote/hf-client.js.map +1 -1
  360. package/build/refactor/remote/version-detect.js +12 -12
  361. package/build/refactor/remote/version-detect.js.map +1 -1
  362. package/build/refactor/vue/index.js +6 -6
  363. package/build/refactor/vue/index.js.map +1 -1
  364. package/build/refactor/vue/vite.js +6 -6
  365. package/build/refactor/vue/vite.js.map +1 -1
  366. package/build/report/index.js +30 -8
  367. package/build/report/index.js.map +1 -1
  368. package/build/report/swaggerJacker.js +63 -0
  369. package/build/report/swaggerJacker.js.map +1 -0
  370. package/build/report/utility/genHtml.js +7 -7
  371. package/build/report/utility/genHtml.js.map +1 -1
  372. package/build/report/utility/populateDb/populateAnalysisFindings.js +2 -2
  373. package/build/report/utility/populateDb/populateAnalysisFindings.js.map +1 -1
  374. package/build/report/utility/populateDb/populateEndpoints.js +3 -3
  375. package/build/report/utility/populateDb/populateEndpoints.js.map +1 -1
  376. package/build/report/utility/populateDb/populateMappedJson.js +2 -2
  377. package/build/report/utility/populateDb/populateMappedJson.js.map +1 -1
  378. package/build/report/utility/populateDb/populateMappedOpenapi.js +3 -3
  379. package/build/report/utility/populateDb/populateMappedOpenapi.js.map +1 -1
  380. package/build/run/bundler-detect.js +45 -9
  381. package/build/run/bundler-detect.js.map +1 -1
  382. package/build/run/cancellableStep.js +27 -0
  383. package/build/run/cancellableStep.js.map +1 -0
  384. package/build/run/dashboard/dashboardHtml.js +150 -0
  385. package/build/run/dashboard/dashboardHtml.js.map +1 -0
  386. package/build/run/dashboard/fsBrowser.js +43 -0
  387. package/build/run/dashboard/fsBrowser.js.map +1 -0
  388. package/build/run/dashboard/server.js +123 -0
  389. package/build/run/dashboard/server.js.map +1 -0
  390. package/build/run/dashboard/state.js +97 -0
  391. package/build/run/dashboard/state.js.map +1 -0
  392. package/build/run/index.js +239 -204
  393. package/build/run/index.js.map +1 -1
  394. package/build/run/interruptHandler.js +18 -0
  395. package/build/run/interruptHandler.js.map +1 -1
  396. package/build/run/outputDirectory.js +377 -0
  397. package/build/run/outputDirectory.js.map +1 -0
  398. package/build/sourcemaps/index.js +116 -29
  399. package/build/sourcemaps/index.js.map +1 -1
  400. package/build/strings/index.js +38 -28
  401. package/build/strings/index.js.map +1 -1
  402. package/build/strings/openapi.js +4 -4
  403. package/build/strings/openapi.js.map +1 -1
  404. package/build/strings/permutate.js +4 -4
  405. package/build/strings/permutate.js.map +1 -1
  406. package/build/strings/secrets.js +1 -0
  407. package/build/strings/secrets.js.map +1 -1
  408. package/build/strings/trufflehog.js +66 -19
  409. package/build/strings/trufflehog.js.map +1 -1
  410. package/build/strings/trufflehogConfig.js +36 -0
  411. package/build/strings/trufflehogConfig.js.map +1 -0
  412. package/build/strings/trufflehogDownload.js +115 -0
  413. package/build/strings/trufflehogDownload.js.map +1 -0
  414. package/build/utility/ai.js +33 -4
  415. package/build/utility/ai.js.map +1 -1
  416. package/build/utility/banner.js +4 -3
  417. package/build/utility/banner.js.map +1 -1
  418. package/build/utility/configureSandbox.js +2 -2
  419. package/build/utility/configureSandbox.js.map +1 -1
  420. package/build/utility/downloadProgress.js +99 -0
  421. package/build/utility/downloadProgress.js.map +1 -0
  422. package/build/utility/globals.js +19 -11
  423. package/build/utility/globals.js.map +1 -1
  424. package/build/utility/heap.js +2 -1
  425. package/build/utility/heap.js.map +1 -1
  426. package/build/utility/makeReq.js +490 -257
  427. package/build/utility/makeReq.js.map +1 -1
  428. package/build/utility/openapiGenerator.js +2 -2
  429. package/build/utility/openapiGenerator.js.map +1 -1
  430. package/build/utility/printMsg.js +53 -0
  431. package/build/utility/printMsg.js.map +1 -0
  432. package/build/utility/progressLog.js +68 -7
  433. package/build/utility/progressLog.js.map +1 -1
  434. package/build/utility/resolvePath.js +2 -1
  435. package/build/utility/resolvePath.js.map +1 -1
  436. package/build/utility/ruleVersionMap.js +73 -0
  437. package/build/utility/ruleVersionMap.js.map +1 -0
  438. package/build/utility/targetInputs.js +177 -0
  439. package/build/utility/targetInputs.js.map +1 -0
  440. package/build/utility/urlUtils.js +37 -1
  441. package/build/utility/urlUtils.js.map +1 -1
  442. package/config.dist.yaml +251 -0
  443. package/contributing/testing.md +106 -0
  444. package/eslint.config.js +24 -0
  445. package/package.json +22 -5
  446. package/scripts/exploit-smoke-test.js +82 -0
  447. package/scripts/framework-detect-smoke-test.js +64 -0
  448. package/scripts/postinstall-completion.mjs +40 -0
  449. package/scripts/smoke-test.js +12 -0
  450. package/build/lazyLoad/downloadLoadedJsUtil.js +0 -98
  451. package/build/lazyLoad/downloadLoadedJsUtil.js.map +0 -1
  452. package/build/map/next_js/interactive_helpers/esqueryGen.js.map +0 -1
  453. package/build/map/next_js/interactive_helpers/inputPatch.js.map +0 -1
  454. package/build/map/next_js/interactive_helpers/keybindings.js.map +0 -1
  455. package/build/map/next_js/interactive_helpers/printer.js.map +0 -1
  456. package/build/map/next_js/interactive_helpers/ui.js.map +0 -1
  457. /package/build/map/{next_js/interactive_helpers → interactive-mode}/esqueryGen.js +0 -0
  458. /package/build/map/{next_js/interactive_helpers → interactive-mode}/inputPatch.js +0 -0
  459. /package/build/map/{next_js/interactive_helpers → interactive-mode}/keybindings.js +0 -0
  460. /package/build/map/{next_js/interactive_helpers → interactive-mode}/printer.js +0 -0
  461. /package/build/map/{next_js/interactive_helpers → interactive-mode}/ui.js +0 -0
@@ -23,14 +23,18 @@ import { ProxyAgent, Socks5ProxyAgent } from "undici";
23
23
  import puppeteer from "./puppeteerInstance.js";
24
24
  import { getChromiumPath } from "./getChromiumPath.js";
25
25
  import * as globals from "./globals.js";
26
- import { get } from "../proxy/genReq.js";
26
+ import { getWithMetadata } from "../proxy/genReq.js";
27
27
  import checkFireWallBlocking from "../proxy/checkFireWallBlocking.js";
28
28
  import { parseProxyUrl } from "../proxy/genericProxy.js";
29
29
  import { buildOxylabsProxyUrl, composeOxylabsUsername } from "../proxy/oxylabsProxy.js";
30
30
  import fs from "fs";
31
31
  import { EventEmitter } from "events";
32
+ import { AsyncLocalStorage } from "node:async_hooks";
33
+ import { createHash, randomUUID } from "node:crypto";
32
34
  import { progressError, progressLog } from "./progressLog.js";
33
35
  import { isSigintHandlerActive } from "../run/interruptHandler.js";
36
+ import detectBlockedResponse from "../proxy/detectBlockedResponse.js";
37
+ import { claimOxylabsFallback, getOxylabsFallbackConfiguration } from "../proxy/oxylabsFallback.js";
34
38
  /**
35
39
  * Proxy-wiring layer. This is the single place a resolved proxy config (see
36
40
  * `../proxy/resolveProxyConfig.ts`) is turned into an actual dispatcher/launch args — every
@@ -45,11 +49,6 @@ export const getResolvedProxyConfigFromGlobals = () => {
45
49
  oxylabs: globals.getOxylabsConfig(),
46
50
  };
47
51
  };
48
- /** Merges a proxy dispatcher (derived from the current globals) into fetch options, if a proxy is configured. */
49
- export const withProxyDispatcher = (opts = {}) => {
50
- const dispatcher = buildUndiciDispatcher(getResolvedProxyConfigFromGlobals());
51
- return dispatcher ? Object.assign(Object.assign({}, opts), { dispatcher }) : opts;
52
- };
53
52
  /**
54
53
  * Builds an undici dispatcher for the resolved proxy config, to pass as `fetch(url, { dispatcher })`.
55
54
  * Returns null for the `aws` method (routed separately via genReq.ts) or no proxy configured.
@@ -74,6 +73,34 @@ export const buildUndiciDispatcher = (resolved) => {
74
73
  }
75
74
  return null;
76
75
  };
76
+ /**
77
+ * Runs request work with an owned dispatcher and always releases its sockets.
78
+ * The callback must consume response bodies before returning.
79
+ */
80
+ export const withManagedDispatcher = (dispatcher, work) => __awaiter(void 0, void 0, void 0, function* () {
81
+ if (!dispatcher)
82
+ return yield work({});
83
+ let completed = false;
84
+ try {
85
+ const result = yield work({ dispatcher });
86
+ completed = true;
87
+ return result;
88
+ }
89
+ finally {
90
+ const cleanup = completed ? dispatcher.close() : dispatcher.destroy();
91
+ yield cleanup.catch(() => undefined);
92
+ }
93
+ });
94
+ /** Runs request work through the globally active proxy, when proxy use is enabled. */
95
+ export const withActiveProxyDispatcher = (work) => __awaiter(void 0, void 0, void 0, function* () {
96
+ const dispatcher = globals.getUseProxy() ? buildUndiciDispatcher(getResolvedProxyConfigFromGlobals()) : null;
97
+ return yield withManagedDispatcher(dispatcher, work);
98
+ });
99
+ const requestSignalContext = new AsyncLocalStorage();
100
+ /** Supplies cancellation to every makeRequest call in the async work scope. */
101
+ export const withRequestSignal = (signal, work) => requestSignalContext.run(signal, work);
102
+ /** True when the current async request scope has been cancelled. */
103
+ export const isRequestCancelled = () => { var _a; return ((_a = requestSignalContext.getStore()) === null || _a === void 0 ? void 0 : _a.aborted) === true; };
77
104
  /**
78
105
  * Builds the Puppeteer `--proxy-server=` launch arg plus optional `page.authenticate()` credentials.
79
106
  * `aws` never applies here (Puppeteer never went through API Gateway).
@@ -102,11 +129,9 @@ export const buildPuppeteerProxyArgs = (resolved) => {
102
129
  }
103
130
  return { arg: null };
104
131
  };
105
- const reportedFailures = new Set();
132
+ /** Returns browser proxy arguments only when proxy routing is currently enabled. */
133
+ export const getActivePuppeteerProxyArgs = () => globals.getUseProxy() ? buildPuppeteerProxyArgs(getResolvedProxyConfigFromGlobals()) : { arg: null };
106
134
  const reportFailure = (url, err) => {
107
- if (reportedFailures.has(url))
108
- return;
109
- reportedFailures.add(url);
110
135
  if (globals.getCacheOnly()) {
111
136
  progressError(chalk.dim(`[!] Cache miss (cache-only mode): ${url}`));
112
137
  return;
@@ -116,10 +141,26 @@ const reportFailure = (url, err) => {
116
141
  progressLog(chalk.dim("[i] Often, using -k flag (ignore SSL errors) fixes the problem"));
117
142
  }
118
143
  };
144
+ const reportTimeout = (url, requestTimeout) => {
145
+ progressError(chalk.red(`[!] Request to ${url} timed out after ${requestTimeout}ms`));
146
+ };
147
+ const reportInvalidUrl = (url) => {
148
+ progressError(chalk.red(`[!] Invalid request URL: ${url}`));
149
+ };
119
150
  // random user agents
120
151
  const UAs = [
121
152
  "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36",
122
153
  ];
154
+ const getCacheIdentityDigest = (url, headers) => {
155
+ const normalizedHeaders = [...new Headers(headers).entries()].sort(([left], [right]) => left.localeCompare(right));
156
+ return createHash("sha256")
157
+ .update(JSON.stringify([url, normalizedHeaders]))
158
+ .digest("hex");
159
+ };
160
+ const getCacheEntryPath = (url, headers) => {
161
+ const digest = getCacheIdentityDigest(url, headers);
162
+ return `${globals.getRespCacheFile()}.entries/${digest}.json`;
163
+ };
123
164
  /**
124
165
  * Reads response data from cache for future requests.
125
166
  *
@@ -135,6 +176,23 @@ const UAs = [
135
176
  * @returns A Promise that resolves to a Response object if the cache is found, or null if not
136
177
  */
137
178
  const readCache = (url, headers) => __awaiter(void 0, void 0, void 0, function* () {
179
+ const identityDigest = getCacheIdentityDigest(url, headers);
180
+ const entryPath = getCacheEntryPath(url, headers);
181
+ if (fs.existsSync(entryPath)) {
182
+ try {
183
+ const entry = JSON.parse(fs.readFileSync(entryPath, "utf-8"));
184
+ if (entry.identityDigest === identityDigest) {
185
+ return new Response(Buffer.from(entry.bodyBase64, "base64"), {
186
+ status: entry.status,
187
+ statusText: entry.statusText,
188
+ headers: entry.responseHeaders,
189
+ });
190
+ }
191
+ }
192
+ catch (_a) {
193
+ // Ignore corrupt sidecar entries and fall back to the legacy cache.
194
+ }
195
+ }
138
196
  // first, check if the file exists or not
139
197
  if (!fs.existsSync(globals.getRespCacheFile())) {
140
198
  return null;
@@ -145,13 +203,12 @@ const readCache = (url, headers) => __awaiter(void 0, void 0, void 0, function*
145
203
  try {
146
204
  cache = JSON.parse(fs.readFileSync(globals.getRespCacheFile(), "utf-8"));
147
205
  }
148
- catch (_a) {
206
+ catch (_b) {
149
207
  return null;
150
208
  }
151
209
  if (cache[url]) {
152
210
  // check if the response contains the specific request headers
153
211
  // iterate through cache[url] and build a Response
154
- let headersMatch = true;
155
212
  // first check if the essential headers match
156
213
  const rscEnabled = headers["RSC"] ? true : false;
157
214
  if (rscEnabled) {
@@ -175,139 +232,140 @@ const readCache = (url, headers) => __awaiter(void 0, void 0, void 0, function*
175
232
  /**
176
233
  * Writes response data to cache for future requests.
177
234
  *
178
- * Stores response body, status, and headers in cache file, handling special
179
- * cases like RSC (React Server Components) headers separately.
235
+ * Stores one atomic sidecar entry per URL/request-header identity. The legacy
236
+ * JSON cache remains readable, but new writes avoid whole-cache rewrites.
180
237
  *
181
238
  * @param url - The URL to cache the response for
182
239
  * @param headers - Request headers that were used
183
240
  * @param response - The Response object to cache
184
241
  * @returns Promise that resolves when caching is complete
185
242
  */
186
- const writeCache = (url, headers, response) => __awaiter(void 0, void 0, void 0, function* () {
243
+ const writeCache = (url_1, headers_1, response_1, ...args_1) => __awaiter(void 0, [url_1, headers_1, response_1, ...args_1], void 0, function* (url, headers, response, reportErrors = true, signal) {
244
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted)
245
+ return;
187
246
  try {
188
- yield writeCacheUnsafe(url, headers, response);
247
+ yield writeCacheUnsafe(url, headers, response, signal);
189
248
  }
190
249
  catch (err) {
191
250
  // Caching must never break a request. Log and move on.
192
- progressError(chalk.yellow(`[!] Failed to write response cache for ${url}: ${(err === null || err === void 0 ? void 0 : err.message) || err}`));
251
+ if (reportErrors) {
252
+ progressError(chalk.yellow(`[!] Failed to write response cache for ${url}: ${(err === null || err === void 0 ? void 0 : err.message) || err}`));
253
+ }
193
254
  }
194
255
  });
195
- const writeCacheUnsafe = (url, headers, response) => __awaiter(void 0, void 0, void 0, function* () {
196
- // clone the response
197
- const clonedResponse = response.clone();
198
- // if cache exists, return
199
- if ((yield readCache(url, headers)) !== null) {
200
- // console.log("cache already exists for ", url);
256
+ const writeCacheUnsafe = (url, headers, response, signal) => __awaiter(void 0, void 0, void 0, function* () {
257
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted)
201
258
  return;
202
- }
203
- // open the cache file, and write the response based on the special headers
204
- let cache;
205
- try {
206
- cache = JSON.parse(fs.readFileSync(globals.getRespCacheFile(), "utf-8"));
207
- }
208
- catch (_a) {
209
- cache = {};
210
- }
211
- if (!cache[url]) {
212
- cache[url] = {};
213
- }
259
+ const identityDigest = getCacheIdentityDigest(url, headers);
260
+ const entryPath = getCacheEntryPath(url, headers);
261
+ if (fs.existsSync(entryPath))
262
+ return;
263
+ const clonedResponse = response.clone();
214
264
  const bodyBuffer = Buffer.from(yield clonedResponse.arrayBuffer());
215
- const body = bodyBuffer.toString("base64");
216
- const status = clonedResponse.status;
217
- const resp_headers = clonedResponse.headers;
218
- if (headers["RSC"]) {
219
- cache[url].rsc = {
220
- req_headers: headers,
221
- status: status,
222
- body_b64: body,
223
- resp_headers: resp_headers,
224
- };
225
- // console.log("rsc", url);
226
- }
227
- else {
228
- cache[url].normal = {
229
- req_headers: headers,
230
- status: status,
231
- body_b64: body,
232
- resp_headers: resp_headers,
233
- };
234
- // console.log("normal", url);
235
- }
236
- let serialized;
265
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted)
266
+ return;
267
+ const entry = Object.freeze({
268
+ identityDigest,
269
+ status: clonedResponse.status,
270
+ statusText: clonedResponse.statusText,
271
+ bodyBase64: bodyBuffer.toString("base64"),
272
+ responseHeaders: Object.freeze(Object.fromEntries(clonedResponse.headers.entries())),
273
+ });
274
+ const entryDirectory = `${globals.getRespCacheFile()}.entries`;
275
+ fs.mkdirSync(entryDirectory, { recursive: true });
276
+ const temporaryPath = `${entryPath}.${process.pid}.${randomUUID()}.tmp`;
237
277
  try {
238
- // so, the cache is the response body for the URL
239
- serialized = JSON.stringify(cache);
278
+ fs.writeFileSync(temporaryPath, JSON.stringify(entry));
279
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted)
280
+ return;
281
+ fs.renameSync(temporaryPath, entryPath);
240
282
  }
241
- catch (err) {
242
- if (err instanceof RangeError) {
243
- // console.log(
244
- // chalk.yellow(
245
- // `[!] Response cache too large to serialize; dropping entry for ${url} and skipping cache write`
246
- // )
247
- // );
248
- // // Roll back the entry we just added so future writes don't keep retrying.
249
- // if (headers["RSC"]) {
250
- // delete cache[url].rsc;
251
- // } else {
252
- // delete cache[url].normal;
253
- // }
254
- // if (!cache[url].rsc && !cache[url].normal) {
255
- // delete cache[url];
256
- // }
257
- // return;
258
- progressError(chalk.yellow(`[!] Cache too big... Emptying cache`));
259
- serialized = JSON.stringify({}); // empty cache
260
- }
261
- // throw err;
283
+ finally {
284
+ if (fs.existsSync(temporaryPath))
285
+ fs.unlinkSync(temporaryPath);
262
286
  }
263
- fs.writeFileSync(globals.getRespCacheFile(), serialized);
264
- // console.log("wrote cache for ", url);
265
287
  });
266
- /**
267
- * Performs a single fetch request with retry logic.
268
- *
269
- * @param url - The URL to request
270
- * @param requestOptions - Request options including headers
271
- * @param requestTimeout - Timeout in milliseconds
272
- * @returns A Promise that resolves to a Response, or null if all retries fail
273
- */
274
- const singleFetch = (url, requestOptions, requestTimeout) => __awaiter(void 0, void 0, void 0, function* () {
275
- let res;
288
+ const singleFetch = (url_1, requestOptions_1, requestTimeout_1, ...args_1) => __awaiter(void 0, [url_1, requestOptions_1, requestTimeout_1, ...args_1], void 0, function* (url, requestOptions, requestTimeout, reportErrors = true, dispatcherOverride, maxAttempts = 11) {
289
+ var _a, _b;
276
290
  let counter = 0;
277
- while (true) {
278
- const controller = new AbortController();
279
- const timeoutId = setTimeout(() => controller.abort(), requestTimeout);
280
- const currentRequestOptions = withProxyDispatcher(Object.assign(Object.assign({}, requestOptions), { signal: controller.signal }));
281
- try {
282
- EventEmitter.defaultMaxListeners = 20;
283
- res = yield fetch(url, currentRequestOptions);
284
- clearTimeout(timeoutId);
285
- if (res) {
286
- break;
291
+ let shouldDestroyOwnedDispatcher = false;
292
+ const ownedDispatcher = dispatcherOverride || !globals.getUseProxy()
293
+ ? null
294
+ : buildUndiciDispatcher(getResolvedProxyConfigFromGlobals());
295
+ const dispatcher = (_a = dispatcherOverride !== null && dispatcherOverride !== void 0 ? dispatcherOverride : ownedDispatcher) !== null && _a !== void 0 ? _a : undefined;
296
+ try {
297
+ while (true) {
298
+ const controller = new AbortController();
299
+ let timedOut = false;
300
+ const timeoutId = setTimeout(() => {
301
+ timedOut = true;
302
+ controller.abort();
303
+ }, requestTimeout);
304
+ const upstreamSignal = requestOptions.signal;
305
+ const abortFromUpstream = () => controller.abort();
306
+ if (upstreamSignal === null || upstreamSignal === void 0 ? void 0 : upstreamSignal.aborted) {
307
+ shouldDestroyOwnedDispatcher = true;
308
+ controller.abort();
309
+ clearTimeout(timeoutId);
310
+ return null;
287
311
  }
288
- }
289
- catch (err) {
290
- clearTimeout(timeoutId);
291
- counter++;
292
- // BUG: https://github.com/nodejs/node/issues/47246
293
- if (err.cause && err.cause.code === "UND_ERR_HEADERS_OVERFLOW") {
294
- progressError(chalk.yellow(`[!] The tool detected a header overflow. Please increase the limit by setting environment variable \`NODE_OPTIONS="--max-http-header-size=99999999"\`. If the error still persists, please try again with a higher limit.`));
295
- process.exit(21);
312
+ else {
313
+ upstreamSignal === null || upstreamSignal === void 0 ? void 0 : upstreamSignal.addEventListener("abort", abortFromUpstream, { once: true });
296
314
  }
297
- if (err.name === "AbortError") {
298
- progressError(chalk.red(`[!] Request to ${url} timed out after ${requestTimeout}ms`));
299
- return null;
315
+ const currentRequestOptions = Object.assign(Object.assign(Object.assign({}, requestOptions), { signal: controller.signal }), (dispatcher ? { dispatcher } : {}));
316
+ try {
317
+ EventEmitter.defaultMaxListeners = 20;
318
+ const response = yield fetch(url, currentRequestOptions); // lgtm[js/insecure-download]: this is a recon tool's generic HTTP client — it fetches whatever URL/scheme the target itself serves, by design.
319
+ const arrayBuffer = yield response.arrayBuffer();
320
+ if (controller.signal.aborted) {
321
+ shouldDestroyOwnedDispatcher = true;
322
+ return null;
323
+ }
324
+ const body = Buffer.alloc(arrayBuffer.byteLength);
325
+ body.set(new Uint8Array(arrayBuffer));
326
+ return {
327
+ body,
328
+ status: response.status,
329
+ headers: new Headers(response.headers),
330
+ ok: response.ok,
331
+ text: body.toString("utf-8"),
332
+ };
300
333
  }
301
- if (counter > 10) {
302
- reportFailure(url, err);
303
- return null;
334
+ catch (err) {
335
+ counter++;
336
+ // BUG: https://github.com/nodejs/node/issues/47246
337
+ if (err.cause && err.cause.code === "UND_ERR_HEADERS_OVERFLOW") {
338
+ progressError(chalk.yellow(`[!] The tool detected a header overflow. Please increase the limit by setting environment variable \`NODE_OPTIONS="--max-http-header-size=99999999"\`. If the error still persists, please try again with a higher limit.`));
339
+ process.exit(21);
340
+ }
341
+ if (err.name === "AbortError") {
342
+ shouldDestroyOwnedDispatcher = true;
343
+ if (reportErrors && (timedOut || !(upstreamSignal === null || upstreamSignal === void 0 ? void 0 : upstreamSignal.aborted)))
344
+ reportTimeout(url, requestTimeout);
345
+ return null;
346
+ }
347
+ if (counter >= maxAttempts) {
348
+ shouldDestroyOwnedDispatcher = true;
349
+ if (reportErrors)
350
+ reportFailure(url, err);
351
+ return null;
352
+ }
353
+ yield new Promise((resolve) => setTimeout(resolve, 500));
354
+ }
355
+ finally {
356
+ clearTimeout(timeoutId);
357
+ upstreamSignal === null || upstreamSignal === void 0 ? void 0 : upstreamSignal.removeEventListener("abort", abortFromUpstream);
304
358
  }
305
- // sleep 0.5 s before retrying
306
- yield new Promise((resolve) => setTimeout(resolve, 500));
307
- continue;
308
359
  }
309
360
  }
310
- return res;
361
+ finally {
362
+ if (ownedDispatcher) {
363
+ const cleanup = shouldDestroyOwnedDispatcher || ((_b = requestOptions.signal) === null || _b === void 0 ? void 0 : _b.aborted)
364
+ ? ownedDispatcher.destroy()
365
+ : ownedDispatcher.close();
366
+ yield cleanup.catch(() => undefined);
367
+ }
368
+ }
311
369
  });
312
370
  /**
313
371
  * Handles firewall detection and bypass using headless browser.
@@ -316,58 +374,65 @@ const singleFetch = (url, requestOptions, requestTimeout) => __awaiter(void 0, v
316
374
  * @param resp_text - The response text to check for firewall signatures
317
375
  * @returns A Promise that resolves to Response content if firewall detected, or null if not
318
376
  */
319
- const handleFirewall = (url, resp_text) => __awaiter(void 0, void 0, void 0, function* () {
320
- if (resp_text.includes("/?bm-verify=") || resp_text.includes("<title>Just a moment...</title>")) {
321
- progressError(chalk.yellow(`[!] CF Firewall detected. Trying to bypass with headless browser`));
322
- const chromiumPath = getChromiumPath();
323
- const proxyArgs = buildPuppeteerProxyArgs(getResolvedProxyConfigFromGlobals());
324
- const browser = yield puppeteer.launch({
325
- headless: true,
326
- executablePath: chromiumPath,
327
- args: [
328
- ...(globals.getDisableSandbox()
329
- ? ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage"]
330
- : []),
331
- ...(proxyArgs.arg ? [proxyArgs.arg] : []),
332
- ],
333
- handleSIGINT: !isSigintHandlerActive(),
334
- });
377
+ const handleFirewall = (url_1, responseText_1, responseMetadata_1, signal_1, ...args_1) => __awaiter(void 0, [url_1, responseText_1, responseMetadata_1, signal_1, ...args_1], void 0, function* (url, responseText, responseMetadata, signal, reportErrors = true) {
378
+ var _a;
379
+ const detection = detectBlockedResponse({
380
+ status: (_a = responseMetadata === null || responseMetadata === void 0 ? void 0 : responseMetadata.status) !== null && _a !== void 0 ? _a : 200,
381
+ headers: responseMetadata === null || responseMetadata === void 0 ? void 0 : responseMetadata.headers,
382
+ body: responseText,
383
+ });
384
+ if (!detection.blocked || (signal === null || signal === void 0 ? void 0 : signal.aborted))
385
+ return null;
386
+ const firewall = detection.provider === "cloudfront" ? "CloudFront" : "Cloudflare/CDN";
387
+ if (reportErrors) {
388
+ progressError(chalk.yellow(`[!] ${firewall} Firewall detected. Trying to bypass with headless browser`));
389
+ }
390
+ const chromiumPath = getChromiumPath();
391
+ const proxyArgs = getActivePuppeteerProxyArgs();
392
+ const browser = yield puppeteer.launch({
393
+ headless: true,
394
+ executablePath: chromiumPath,
395
+ args: [
396
+ ...(globals.getDisableSandbox()
397
+ ? ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage"]
398
+ : []),
399
+ ...(proxyArgs.arg ? [proxyArgs.arg] : []),
400
+ ],
401
+ handleSIGINT: !isSigintHandlerActive(),
402
+ });
403
+ const abortBrowser = () => {
404
+ void browser.close().catch(() => undefined);
405
+ };
406
+ signal === null || signal === void 0 ? void 0 : signal.addEventListener("abort", abortBrowser, { once: true });
407
+ try {
408
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted)
409
+ return null;
335
410
  const page = yield browser.newPage();
336
- if (proxyArgs.authenticate) {
411
+ if (proxyArgs.authenticate)
337
412
  yield page.authenticate(proxyArgs.authenticate);
338
- }
339
413
  yield page.goto(url);
340
- yield new Promise((resolve) => setTimeout(resolve, 5000));
341
- const content = yield page.content();
342
- yield browser.close();
343
- return content;
344
- }
345
- else if (resp_text.includes("403 ERROR") && resp_text.includes("Generated by cloudfront")) {
346
- progressError(chalk.yellow(`[!] Cloudfront Firewall detected. Trying to bypass with headless browser`));
347
- const chromiumPath2 = getChromiumPath();
348
- const proxyArgs = buildPuppeteerProxyArgs(getResolvedProxyConfigFromGlobals());
349
- const browser = yield puppeteer.launch({
350
- headless: true,
351
- executablePath: chromiumPath2,
352
- args: [
353
- ...(globals.getDisableSandbox()
354
- ? ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage"]
355
- : []),
356
- ...(proxyArgs.arg ? [proxyArgs.arg] : []),
357
- ],
358
- handleSIGINT: !isSigintHandlerActive(),
414
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted)
415
+ return null;
416
+ yield new Promise((resolve) => {
417
+ const finish = () => {
418
+ clearTimeout(timeout);
419
+ signal === null || signal === void 0 ? void 0 : signal.removeEventListener("abort", finish);
420
+ resolve();
421
+ };
422
+ const timeout = setTimeout(finish, 5000);
423
+ signal === null || signal === void 0 ? void 0 : signal.addEventListener("abort", finish, { once: true });
359
424
  });
360
- const page = yield browser.newPage();
361
- if (proxyArgs.authenticate) {
362
- yield page.authenticate(proxyArgs.authenticate);
363
- }
364
- yield page.goto(url);
365
- yield new Promise((resolve) => setTimeout(resolve, 5000));
366
- const content = yield page.content();
367
- yield browser.close();
368
- return content;
425
+ return (signal === null || signal === void 0 ? void 0 : signal.aborted) ? null : yield page.content();
426
+ }
427
+ catch (err) {
428
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted)
429
+ return null;
430
+ throw err;
431
+ }
432
+ finally {
433
+ signal === null || signal === void 0 ? void 0 : signal.removeEventListener("abort", abortBrowser);
434
+ yield browser.close().catch(() => undefined);
369
435
  }
370
- return null;
371
436
  });
372
437
  /**
373
438
  * Makes a GET request to the given URL and returns the response.
@@ -387,13 +452,22 @@ const handleFirewall = (url, resp_text) => __awaiter(void 0, void 0, void 0, fun
387
452
  * @returns A Promise that resolves to a Response, or null if all retries fail
388
453
  */
389
454
  const makeRequest = (url, args) => __awaiter(void 0, void 0, void 0, function* () {
455
+ var _a, _b, _c, _d, _e;
390
456
  if (url.startsWith("//")) {
391
457
  url = "https:" + url;
392
458
  }
393
- const _a = args || {}, { timeout } = _a, restArgs = __rest(_a, ["timeout"]);
394
- const requestOptions = restArgs;
459
+ const _f = args || {}, { timeout, reportErrors = true } = _f, restArgs = __rest(_f, ["timeout", "reportErrors"]);
460
+ const contextualSignal = requestSignalContext.getStore();
461
+ const explicitSignal = (_a = restArgs.signal) !== null && _a !== void 0 ? _a : undefined;
462
+ const effectiveSignal = contextualSignal && explicitSignal && contextualSignal !== explicitSignal
463
+ ? AbortSignal.any([contextualSignal, explicitSignal])
464
+ : (explicitSignal !== null && explicitSignal !== void 0 ? explicitSignal : contextualSignal);
465
+ const isCancelled = () => (effectiveSignal === null || effectiveSignal === void 0 ? void 0 : effectiveSignal.aborted) === true;
466
+ if (isCancelled())
467
+ return null;
395
468
  const requestTimeout = timeout || globals.getRequestTimeout();
396
- const usingDefaultHeaders = !requestOptions.headers;
469
+ const usingDefaultHeaders = !restArgs.headers;
470
+ const requestStartedWithProxy = globals.getUseProxy();
397
471
  // Build default headers if not provided
398
472
  const baseHeaders = {
399
473
  "User-Agent": UAs[Math.floor(Math.random() * UAs.length)],
@@ -407,63 +481,111 @@ const makeRequest = (url, args) => __awaiter(void 0, void 0, void 0, function* (
407
481
  try {
408
482
  parsedOrigin = new URL(url).origin;
409
483
  }
410
- catch (_b) {
484
+ catch (_g) {
485
+ if (reportErrors)
486
+ reportInvalidUrl(url);
411
487
  return null;
412
488
  }
413
- if (!requestOptions.headers) {
414
- requestOptions.headers = Object.assign(Object.assign({}, baseHeaders), { Referer: parsedOrigin, Origin: parsedOrigin });
415
- }
489
+ const requestOptions = Object.assign(Object.assign(Object.assign({}, restArgs), (effectiveSignal ? { signal: effectiveSignal } : {})), { headers: (_b = restArgs.headers) !== null && _b !== void 0 ? _b : Object.assign(Object.assign({}, baseHeaders), { Referer: parsedOrigin, Origin: parsedOrigin }) });
490
+ // A successful default-header request may have used the no-Referer fallback.
491
+ // Probe both request identities so that response remains reusable on later
492
+ // runs, including cache-only runs that cannot repeat the network fallback.
493
+ const cacheHeaderCandidates = usingDefaultHeaders
494
+ ? [requestOptions.headers || {}, baseHeaders]
495
+ : [requestOptions.headers || {}];
416
496
  // if cache is enabled, read the cache and return if cache is present. else, continue
417
497
  if (!globals.getDisableCache()) {
418
- const cachedResponse = yield readCache(url, requestOptions.headers || {});
498
+ let cachedEntry = null;
499
+ for (const candidateHeaders of cacheHeaderCandidates) {
500
+ const response = yield readCache(url, candidateHeaders);
501
+ if (response !== null) {
502
+ cachedEntry = Object.freeze({ response, headers: candidateHeaders });
503
+ break;
504
+ }
505
+ }
506
+ if (isCancelled())
507
+ return null;
419
508
  // if the response if cached, then check if contains any signatures of firewall
420
- if (cachedResponse !== null) {
421
- const cachedText = yield cachedResponse.clone().text();
422
- const firewallContent = yield handleFirewall(url, cachedText);
423
- if (firewallContent) {
424
- yield writeCache(url, requestOptions.headers || {}, new Response(firewallContent));
425
- return new Response(firewallContent);
509
+ if (cachedEntry !== null) {
510
+ const cachedText = yield cachedEntry.response.clone().text();
511
+ if (isCancelled())
512
+ return null;
513
+ const cachedDetection = detectBlockedResponse({
514
+ status: cachedEntry.response.status,
515
+ headers: cachedEntry.response.headers,
516
+ body: cachedText,
517
+ });
518
+ const shouldRefreshBlockedCache = !globals.getCacheOnly() &&
519
+ !requestStartedWithProxy &&
520
+ getOxylabsFallbackConfiguration().enabled &&
521
+ cachedDetection.blocked;
522
+ if (shouldRefreshBlockedCache) {
523
+ const sidecarPath = getCacheEntryPath(url, cachedEntry.headers);
524
+ try {
525
+ if (fs.existsSync(sidecarPath))
526
+ fs.unlinkSync(sidecarPath);
527
+ }
528
+ catch (error) {
529
+ if (reportErrors) {
530
+ progressError(chalk.yellow(`[!] Could not replace blocked cache entry for ${url}: ${error}`));
531
+ }
532
+ }
533
+ }
534
+ else {
535
+ if (globals.getCacheOnly())
536
+ return cachedEntry.response;
537
+ const firewallContent = yield handleFirewall(url, cachedText, { status: cachedEntry.response.status, headers: cachedEntry.response.headers }, effectiveSignal, reportErrors);
538
+ if (isCancelled())
539
+ return null;
540
+ if (firewallContent) {
541
+ yield writeCache(url, cachedEntry.headers, new Response(firewallContent), reportErrors, effectiveSignal);
542
+ if (isCancelled())
543
+ return null;
544
+ return new Response(firewallContent);
545
+ }
546
+ return cachedEntry.response;
426
547
  }
427
- return cachedResponse;
428
548
  }
429
549
  }
430
550
  if (globals.getCacheOnly()) {
431
- reportFailure(url, "cache miss in cache-only mode");
551
+ if (reportErrors)
552
+ reportFailure(url, "cache miss in cache-only mode");
432
553
  return null;
433
554
  }
434
555
  if (globals.getUseProxy() && globals.getProxyMethod() === "aws") {
435
- const get_headers = requestOptions.headers;
436
- const body = yield get(url, get_headers);
556
+ const getHeaders = Object.fromEntries(new Headers(requestOptions.headers).entries());
557
+ let awsResponse;
558
+ try {
559
+ awsResponse = yield getWithMetadata(url, getHeaders, effectiveSignal);
560
+ }
561
+ catch (err) {
562
+ if (isCancelled())
563
+ return null;
564
+ if (reportErrors)
565
+ reportFailure(url, err);
566
+ return null;
567
+ }
568
+ if (isCancelled())
569
+ return null;
437
570
  // check if any firewall is there in the way
438
- if (yield checkFireWallBlocking(body)) {
571
+ if (yield checkFireWallBlocking(awsResponse.body, { status: awsResponse.status, headers: awsResponse.headers })) {
439
572
  progressError(chalk.magenta("[!] Please try again without API Gateway"));
440
573
  process.exit(18);
441
574
  }
442
575
  // craft a Response, and return that
443
- const response = new Response(body);
576
+ const response = new Response(awsResponse.body, {
577
+ status: (_c = awsResponse.status) !== null && _c !== void 0 ? _c : 200,
578
+ headers: (_d = awsResponse.headers) !== null && _d !== void 0 ? _d : {},
579
+ });
444
580
  // if cache is enabled, write the response to the cache
445
581
  if (!globals.getDisableCache()) {
446
- yield writeCache(url, get_headers, response);
582
+ yield writeCache(url, getHeaders, response, reportErrors, effectiveSignal);
447
583
  }
584
+ if (isCancelled())
585
+ return null;
448
586
  return response;
449
587
  }
450
588
  else {
451
- // Helper to read response once and store data for reuse.
452
- // We copy the bytes into a Buffer that owns its own ArrayBuffer so the
453
- // stored body cannot be invalidated by undici recycling its internal
454
- // buffer after we consume the response.
455
- const consumeResponse = (res) => __awaiter(void 0, void 0, void 0, function* () {
456
- if (!res)
457
- return null;
458
- const ab = yield res.arrayBuffer();
459
- const body = Buffer.alloc(ab.byteLength);
460
- body.set(new Uint8Array(ab));
461
- const text = body.toString("utf-8");
462
- // Snapshot headers as a plain object so we can rebuild a fresh
463
- // Headers instance per Response instead of sharing state.
464
- const headers = new Headers(res.headers);
465
- return { body, status: res.status, headers, ok: res.ok, text };
466
- });
467
589
  // Helper to create Response from stored data. Pass the body as an
468
590
  // immutable string so each Response gets a fully independent backing
469
591
  // store; undici has been known to flag Responses as already-consumed
@@ -480,76 +602,187 @@ const makeRequest = (url, args) => __awaiter(void 0, void 0, void 0, function* (
480
602
  headers.delete("transfer-encoding");
481
603
  return new Response(bodyCopy, { status: data.status, headers });
482
604
  };
605
+ let oxylabsFallbackAttempted = false;
606
+ const maybeRetryBlockedResponse = (directData, headers) => __awaiter(void 0, void 0, void 0, function* () {
607
+ var _a, _b;
608
+ if (!directData || isCancelled())
609
+ return null;
610
+ if (!getOxylabsFallbackConfiguration().enabled)
611
+ return null;
612
+ const detection = detectBlockedResponse({
613
+ status: directData.status,
614
+ headers: directData.headers,
615
+ body: directData.text,
616
+ });
617
+ if (!detection.blocked)
618
+ return null;
619
+ const method = ((_a = requestOptions.method) !== null && _a !== void 0 ? _a : "GET").toUpperCase();
620
+ if (requestStartedWithProxy || oxylabsFallbackAttempted || !["GET", "HEAD"].includes(method)) {
621
+ return null;
622
+ }
623
+ oxylabsFallbackAttempted = true;
624
+ const proxy = claimOxylabsFallback(url);
625
+ if (!proxy || isCancelled())
626
+ return null;
627
+ if (reportErrors) {
628
+ progressLog(chalk.yellow(`[i] ${(_b = detection.provider) !== null && _b !== void 0 ? _b : "CDN/WAF"} block detected; retrying this request through Oxylabs`));
629
+ }
630
+ const dispatcher = buildUndiciDispatcher({ method: "oxylabs", oxylabs: proxy });
631
+ if (!dispatcher)
632
+ return null;
633
+ let fallbackData = null;
634
+ try {
635
+ fallbackData = yield singleFetch(url, Object.assign(Object.assign({}, requestOptions), { headers }), requestTimeout, false, dispatcher, 1);
636
+ }
637
+ finally {
638
+ const cleanup = isCancelled() || !fallbackData ? dispatcher.destroy() : dispatcher.close();
639
+ yield cleanup.catch(() => undefined);
640
+ }
641
+ if (!fallbackData || isCancelled())
642
+ return null;
643
+ const fallbackDetection = detectBlockedResponse({
644
+ status: fallbackData.status,
645
+ headers: fallbackData.headers,
646
+ body: fallbackData.text,
647
+ });
648
+ if (fallbackDetection.blocked || !fallbackData.ok) {
649
+ if (reportErrors) {
650
+ progressError(chalk.yellow("[!] Oxylabs fallback did not bypass the CDN/WAF response"));
651
+ }
652
+ return null;
653
+ }
654
+ return fallbackData;
655
+ });
656
+ const finishPlainResponse = (data, headers) => __awaiter(void 0, void 0, void 0, function* () {
657
+ if (!(data === null || data === void 0 ? void 0 : data.ok))
658
+ return null;
659
+ const detection = detectBlockedResponse({
660
+ status: data.status,
661
+ headers: data.headers,
662
+ body: data.text,
663
+ });
664
+ if (detection.blocked)
665
+ return null;
666
+ if (!globals.getDisableCache()) {
667
+ yield writeCache(url, headers, createResponse(data), reportErrors, effectiveSignal);
668
+ }
669
+ return isCancelled() ? null : createResponse(data);
670
+ });
671
+ const tryBrowserRecovery = (blockedData, headers) => __awaiter(void 0, void 0, void 0, function* () {
672
+ if (!(blockedData === null || blockedData === void 0 ? void 0 : blockedData.ok) || isCancelled())
673
+ return null;
674
+ let browserContent;
675
+ try {
676
+ browserContent = yield handleFirewall(url, blockedData.text, { status: blockedData.status, headers: blockedData.headers }, effectiveSignal, reportErrors);
677
+ }
678
+ catch (error) {
679
+ if (reportErrors && !isCancelled()) {
680
+ progressError(chalk.yellow(`[!] Browser firewall bypass failed for ${url}: ${(error === null || error === void 0 ? void 0 : error.message) || error}`));
681
+ }
682
+ return null;
683
+ }
684
+ if (!browserContent || isCancelled())
685
+ return null;
686
+ const browserDetection = detectBlockedResponse({
687
+ status: 200,
688
+ headers: { "content-type": "text/html" },
689
+ body: browserContent,
690
+ });
691
+ if (browserDetection.blocked) {
692
+ if (reportErrors) {
693
+ progressError(chalk.yellow("[!] Browser fallback still returned a CDN/WAF challenge"));
694
+ }
695
+ return null;
696
+ }
697
+ const response = new Response(browserContent);
698
+ if (!globals.getDisableCache()) {
699
+ yield writeCache(url, headers, response, reportErrors, effectiveSignal);
700
+ }
701
+ return isCancelled() ? null : new Response(browserContent);
702
+ });
483
703
  // When using default headers, try both with and without Referer/Origin
484
704
  // to handle servers that return 404 for one but 200 for the other
485
705
  if (usingDefaultHeaders) {
486
706
  // First try: with Referer and Origin
487
707
  const headersWithReferer = Object.assign(Object.assign({}, baseHeaders), { Referer: new URL(url).origin, Origin: new URL(url).origin });
488
- const resWithReferer = yield singleFetch(url, Object.assign(Object.assign({}, requestOptions), { headers: headersWithReferer }), requestTimeout);
489
- const dataWithReferer = yield consumeResponse(resWithReferer);
490
- if (dataWithReferer && dataWithReferer.ok) {
491
- // Check for firewall
492
- const firewallContent = yield handleFirewall(url, dataWithReferer.text);
493
- if (firewallContent) {
494
- if (!globals.getDisableCache()) {
495
- yield writeCache(url, headersWithReferer, new Response(firewallContent));
496
- }
497
- return new Response(firewallContent);
498
- }
499
- // Cache and return successful response
500
- if (!globals.getDisableCache()) {
501
- yield writeCache(url, headersWithReferer, createResponse(dataWithReferer));
502
- }
503
- return createResponse(dataWithReferer);
504
- }
708
+ const directDataWithReferer = yield singleFetch(url, Object.assign(Object.assign({}, requestOptions), { headers: headersWithReferer }), requestTimeout, false);
709
+ if (isCancelled())
710
+ return null;
711
+ const withRefererResponse = yield finishPlainResponse(directDataWithReferer, headersWithReferer);
712
+ if (withRefererResponse)
713
+ return withRefererResponse;
505
714
  // Second try: without Referer and Origin
506
715
  const headersWithoutReferer = Object.assign({}, baseHeaders);
507
- const resWithoutReferer = yield singleFetch(url, Object.assign(Object.assign({}, requestOptions), { headers: headersWithoutReferer }), requestTimeout);
508
- const dataWithoutReferer = yield consumeResponse(resWithoutReferer);
509
- if (dataWithoutReferer && dataWithoutReferer.ok) {
510
- // Check for firewall
511
- const firewallContent = yield handleFirewall(url, dataWithoutReferer.text);
512
- if (firewallContent) {
513
- if (!globals.getDisableCache()) {
514
- yield writeCache(url, headersWithoutReferer, new Response(firewallContent));
515
- }
516
- return new Response(firewallContent);
517
- }
518
- // Cache and return successful response
519
- if (!globals.getDisableCache()) {
520
- yield writeCache(url, headersWithoutReferer, createResponse(dataWithoutReferer));
521
- }
522
- return createResponse(dataWithoutReferer);
716
+ const directDataWithoutReferer = yield singleFetch(url, Object.assign(Object.assign({}, requestOptions), { headers: headersWithoutReferer }), requestTimeout, reportErrors && !directDataWithReferer);
717
+ if (isCancelled())
718
+ return null;
719
+ const withoutRefererResponse = yield finishPlainResponse(directDataWithoutReferer, headersWithoutReferer);
720
+ if (withoutRefererResponse)
721
+ return withoutRefererResponse;
722
+ const candidates = [
723
+ { data: directDataWithoutReferer, headers: headersWithoutReferer },
724
+ { data: directDataWithReferer, headers: headersWithReferer },
725
+ ];
726
+ const blockedCandidate = candidates.find(({ data }) => data &&
727
+ detectBlockedResponse({ status: data.status, headers: data.headers, body: data.text }).blocked);
728
+ if (blockedCandidate) {
729
+ const fallbackData = yield maybeRetryBlockedResponse(blockedCandidate.data, blockedCandidate.headers);
730
+ if (isCancelled())
731
+ return null;
732
+ const fallbackResponse = yield finishPlainResponse(fallbackData, blockedCandidate.headers);
733
+ if (fallbackResponse)
734
+ return fallbackResponse;
735
+ const browserResponse = yield tryBrowserRecovery(blockedCandidate.data, blockedCandidate.headers);
736
+ if (browserResponse)
737
+ return browserResponse;
523
738
  }
524
- // Both failed, return whichever response we got (prefer non-null)
525
- const finalData = dataWithReferer || dataWithoutReferer;
526
- if (finalData) {
527
- if (!globals.getDisableCache()) {
528
- yield writeCache(url, headersWithReferer, createResponse(finalData));
739
+ // Prefer the most recent non-blocked response, then any direct response.
740
+ const finalCandidate = (_e = candidates.find(({ data }) => data &&
741
+ !detectBlockedResponse({ status: data.status, headers: data.headers, body: data.text }).blocked)) !== null && _e !== void 0 ? _e : candidates.find(({ data }) => data !== null);
742
+ if (finalCandidate === null || finalCandidate === void 0 ? void 0 : finalCandidate.data) {
743
+ const finalData = finalCandidate.data;
744
+ const finalDetection = detectBlockedResponse({
745
+ status: finalData.status,
746
+ headers: finalData.headers,
747
+ body: finalData.text,
748
+ });
749
+ if (!globals.getDisableCache() && !finalDetection.blocked) {
750
+ yield writeCache(url, finalCandidate.headers, createResponse(finalData), reportErrors, effectiveSignal);
529
751
  }
752
+ if (isCancelled())
753
+ return null;
530
754
  return createResponse(finalData);
531
755
  }
532
756
  return null;
533
757
  }
534
758
  else {
535
759
  // Custom headers provided, use them directly
536
- const res = yield singleFetch(url, requestOptions, requestTimeout);
537
- const data = yield consumeResponse(res);
538
- if (!data)
760
+ const directData = yield singleFetch(url, requestOptions, requestTimeout, reportErrors);
761
+ if (!directData || isCancelled())
539
762
  return null;
540
- // Check for firewall
541
- const firewallContent = yield handleFirewall(url, data.text);
542
- if (firewallContent) {
543
- if (!globals.getDisableCache()) {
544
- yield writeCache(url, requestOptions.headers || {}, new Response(firewallContent));
545
- }
546
- return new Response(firewallContent);
547
- }
548
- // Cache and return
549
- if (!globals.getDisableCache()) {
550
- yield writeCache(url, requestOptions.headers || {}, createResponse(data));
763
+ const directResponse = yield finishPlainResponse(directData, requestOptions.headers || {});
764
+ if (directResponse)
765
+ return directResponse;
766
+ const fallbackData = yield maybeRetryBlockedResponse(directData, requestOptions.headers || {});
767
+ if (isCancelled())
768
+ return null;
769
+ const fallbackResponse = yield finishPlainResponse(fallbackData, requestOptions.headers || {});
770
+ if (fallbackResponse)
771
+ return fallbackResponse;
772
+ const browserResponse = yield tryBrowserRecovery(directData, requestOptions.headers || {});
773
+ if (browserResponse)
774
+ return browserResponse;
775
+ const directDetection = detectBlockedResponse({
776
+ status: directData.status,
777
+ headers: directData.headers,
778
+ body: directData.text,
779
+ });
780
+ if (!globals.getDisableCache() && !directDetection.blocked) {
781
+ yield writeCache(url, requestOptions.headers || {}, createResponse(directData), reportErrors, effectiveSignal);
551
782
  }
552
- return createResponse(data);
783
+ if (isCancelled())
784
+ return null;
785
+ return createResponse(directData);
553
786
  }
554
787
  }
555
788
  });