@prosopo/provider 3.13.7 → 4.7.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 (761) hide show
  1. package/.turbo/turbo-build$colon$cjs.log +107 -56
  2. package/.turbo/turbo-build$colon$tsc.log +71 -0
  3. package/.turbo/turbo-build.log +110 -57
  4. package/CHANGELOG.md +1474 -0
  5. package/dist/api/admin/apiAdminRoutesProvider.d.ts +9 -0
  6. package/dist/api/admin/apiAdminRoutesProvider.d.ts.map +1 -0
  7. package/dist/api/admin/apiAdminRoutesProvider.js +31 -1
  8. package/dist/api/admin/apiAdminRoutesProvider.js.map +1 -0
  9. package/dist/api/admin/apiClearAllCountersEndpoint.d.ts +14 -0
  10. package/dist/api/admin/apiClearAllCountersEndpoint.d.ts.map +1 -0
  11. package/dist/api/admin/apiClearAllCountersEndpoint.js +59 -0
  12. package/dist/api/admin/apiClearAllCountersEndpoint.js.map +1 -0
  13. package/dist/api/admin/apiDnsEventEndpoint.d.ts +15 -0
  14. package/dist/api/admin/apiDnsEventEndpoint.d.ts.map +1 -0
  15. package/dist/api/admin/apiDnsEventEndpoint.js +64 -0
  16. package/dist/api/admin/apiDnsEventEndpoint.js.map +1 -0
  17. package/dist/api/admin/apiGetAllDecisionMachinesEndpoint.d.ts +13 -0
  18. package/dist/api/admin/apiGetAllDecisionMachinesEndpoint.d.ts.map +1 -0
  19. package/dist/api/admin/apiGetAllDecisionMachinesEndpoint.js +49 -0
  20. package/dist/api/admin/apiGetAllDecisionMachinesEndpoint.js.map +1 -0
  21. package/dist/api/admin/apiGetDecisionMachineEndpoint.d.ts +14 -0
  22. package/dist/api/admin/apiGetDecisionMachineEndpoint.d.ts.map +1 -0
  23. package/dist/api/admin/apiGetDecisionMachineEndpoint.js +42 -0
  24. package/dist/api/admin/apiGetDecisionMachineEndpoint.js.map +1 -0
  25. package/dist/api/admin/apiRegisterSiteKeyEndpoint.d.ts +14 -0
  26. package/dist/api/admin/apiRegisterSiteKeyEndpoint.d.ts.map +1 -0
  27. package/dist/api/admin/apiRegisterSiteKeyEndpoint.js +1 -1
  28. package/dist/api/admin/apiRegisterSiteKeyEndpoint.js.map +1 -0
  29. package/dist/api/admin/apiRegisterSiteKeysEndpoint.d.ts +14 -0
  30. package/dist/api/admin/apiRegisterSiteKeysEndpoint.d.ts.map +1 -0
  31. package/dist/api/admin/apiRegisterSiteKeysEndpoint.js +33 -0
  32. package/dist/api/admin/apiRegisterSiteKeysEndpoint.js.map +1 -0
  33. package/dist/api/admin/apiRemoveAllDecisionMachinesEndpoint.d.ts +13 -0
  34. package/dist/api/admin/apiRemoveAllDecisionMachinesEndpoint.d.ts.map +1 -0
  35. package/dist/api/admin/apiRemoveAllDecisionMachinesEndpoint.js +41 -0
  36. package/dist/api/admin/apiRemoveAllDecisionMachinesEndpoint.js.map +1 -0
  37. package/dist/api/admin/apiRemoveDecisionMachineEndpoint.d.ts +14 -0
  38. package/dist/api/admin/apiRemoveDecisionMachineEndpoint.d.ts.map +1 -0
  39. package/dist/api/admin/apiRemoveDecisionMachineEndpoint.js +42 -0
  40. package/dist/api/admin/apiRemoveDecisionMachineEndpoint.js.map +1 -0
  41. package/dist/api/admin/apiRemoveDetectorKeyEndpoint.d.ts +23 -0
  42. package/dist/api/admin/apiRemoveDetectorKeyEndpoint.d.ts.map +1 -0
  43. package/dist/api/admin/apiRemoveDetectorKeyEndpoint.js +1 -1
  44. package/dist/api/admin/apiRemoveDetectorKeyEndpoint.js.map +1 -0
  45. package/dist/api/admin/apiRemoveSiteKeyEndpoint.d.ts +14 -0
  46. package/dist/api/admin/apiRemoveSiteKeyEndpoint.d.ts.map +1 -0
  47. package/dist/api/admin/apiRemoveSiteKeyEndpoint.js +32 -0
  48. package/dist/api/admin/apiRemoveSiteKeyEndpoint.js.map +1 -0
  49. package/dist/api/admin/apiRemoveSiteKeysEndpoint.d.ts +14 -0
  50. package/dist/api/admin/apiRemoveSiteKeysEndpoint.d.ts.map +1 -0
  51. package/dist/api/admin/apiRemoveSiteKeysEndpoint.js +34 -0
  52. package/dist/api/admin/apiRemoveSiteKeysEndpoint.js.map +1 -0
  53. package/dist/api/admin/apiToggleMaintenanceModeEndpoint.d.ts +13 -0
  54. package/dist/api/admin/apiToggleMaintenanceModeEndpoint.d.ts.map +1 -0
  55. package/dist/api/admin/apiToggleMaintenanceModeEndpoint.js +1 -1
  56. package/dist/api/admin/apiToggleMaintenanceModeEndpoint.js.map +1 -0
  57. package/dist/api/admin/apiUpdateDecisionMachineEndpoint.d.ts +14 -0
  58. package/dist/api/admin/apiUpdateDecisionMachineEndpoint.d.ts.map +1 -0
  59. package/dist/api/admin/apiUpdateDecisionMachineEndpoint.js +60 -0
  60. package/dist/api/admin/apiUpdateDecisionMachineEndpoint.js.map +1 -0
  61. package/dist/api/admin/apiUpdateDetectorKeyEndpoint.d.ts +14 -0
  62. package/dist/api/admin/apiUpdateDetectorKeyEndpoint.d.ts.map +1 -0
  63. package/dist/api/admin/apiUpdateDetectorKeyEndpoint.js +1 -1
  64. package/dist/api/admin/apiUpdateDetectorKeyEndpoint.js.map +1 -0
  65. package/dist/api/admin/createApiAdminRoutesProvider.d.ts +4 -0
  66. package/dist/api/admin/createApiAdminRoutesProvider.d.ts.map +1 -0
  67. package/dist/api/admin/createApiAdminRoutesProvider.js.map +1 -0
  68. package/dist/api/blacklistRequestInspector.d.ts +20 -0
  69. package/dist/api/blacklistRequestInspector.d.ts.map +1 -0
  70. package/dist/api/blacklistRequestInspector.js +20 -6
  71. package/dist/api/blacklistRequestInspector.js.map +1 -0
  72. package/dist/api/block.d.ts +4 -0
  73. package/dist/api/block.d.ts.map +1 -0
  74. package/dist/api/block.js +19 -8
  75. package/dist/api/block.js.map +1 -0
  76. package/dist/api/captcha/checkSpamEmail.d.ts +6 -0
  77. package/dist/api/captcha/checkSpamEmail.d.ts.map +1 -0
  78. package/dist/api/captcha/checkSpamEmail.js +80 -0
  79. package/dist/api/captcha/checkSpamEmail.js.map +1 -0
  80. package/dist/api/captcha/contextAwareValidation.d.ts +4 -0
  81. package/dist/api/captcha/contextAwareValidation.d.ts.map +1 -0
  82. package/dist/api/captcha/contextAwareValidation.js.map +1 -0
  83. package/dist/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.d.ts +31 -0
  84. package/dist/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.d.ts.map +1 -0
  85. package/dist/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.js +123 -0
  86. package/dist/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.js.map +1 -0
  87. package/dist/api/captcha/getFrictionlessCaptchaChallenge/constants.d.ts +3 -0
  88. package/dist/api/captcha/getFrictionlessCaptchaChallenge/constants.d.ts.map +1 -0
  89. package/dist/api/captcha/getFrictionlessCaptchaChallenge/constants.js +13 -0
  90. package/dist/api/captcha/getFrictionlessCaptchaChallenge/constants.js.map +1 -0
  91. package/dist/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.d.ts +36 -0
  92. package/dist/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.d.ts.map +1 -0
  93. package/dist/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.js +287 -0
  94. package/dist/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.js.map +1 -0
  95. package/dist/api/captcha/getFrictionlessCaptchaChallenge/decryptSimdReadings.d.ts +4 -0
  96. package/dist/api/captcha/getFrictionlessCaptchaChallenge/decryptSimdReadings.d.ts.map +1 -0
  97. package/dist/api/captcha/getFrictionlessCaptchaChallenge/decryptSimdReadings.js +14 -0
  98. package/dist/api/captcha/getFrictionlessCaptchaChallenge/decryptSimdReadings.js.map +1 -0
  99. package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.d.ts +7 -0
  100. package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.d.ts.map +1 -0
  101. package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js +314 -0
  102. package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js.map +1 -0
  103. package/dist/api/captcha/getFrictionlessCaptchaChallenge/honeypotResponse.d.ts +5 -0
  104. package/dist/api/captcha/getFrictionlessCaptchaChallenge/honeypotResponse.d.ts.map +1 -0
  105. package/dist/api/captcha/getFrictionlessCaptchaChallenge/honeypotResponse.js +17 -0
  106. package/dist/api/captcha/getFrictionlessCaptchaChallenge/honeypotResponse.js.map +1 -0
  107. package/dist/api/captcha/getFrictionlessCaptchaChallenge/sessionDedup.d.ts +14 -0
  108. package/dist/api/captcha/getFrictionlessCaptchaChallenge/sessionDedup.d.ts.map +1 -0
  109. package/dist/api/captcha/getFrictionlessCaptchaChallenge/sessionDedup.js +28 -0
  110. package/dist/api/captcha/getFrictionlessCaptchaChallenge/sessionDedup.js.map +1 -0
  111. package/dist/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.d.ts +23 -0
  112. package/dist/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.d.ts.map +1 -0
  113. package/dist/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.js +61 -0
  114. package/dist/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.js.map +1 -0
  115. package/dist/api/captcha/getFrictionlessCaptchaChallenge.d.ts +2 -0
  116. package/dist/api/captcha/getFrictionlessCaptchaChallenge.d.ts.map +1 -0
  117. package/dist/api/captcha/getFrictionlessCaptchaChallenge.js +2 -364
  118. package/dist/api/captcha/getFrictionlessCaptchaChallenge.js.map +1 -0
  119. package/dist/api/captcha/getImageCaptchaChallenge.d.ts +7 -0
  120. package/dist/api/captcha/getImageCaptchaChallenge.d.ts.map +1 -0
  121. package/dist/api/captcha/getImageCaptchaChallenge.js +36 -9
  122. package/dist/api/captcha/getImageCaptchaChallenge.js.map +1 -0
  123. package/dist/api/captcha/getPoWCaptchaChallenge.d.ts +7 -0
  124. package/dist/api/captcha/getPoWCaptchaChallenge.d.ts.map +1 -0
  125. package/dist/api/captcha/getPoWCaptchaChallenge.js +49 -7
  126. package/dist/api/captcha/getPoWCaptchaChallenge.js.map +1 -0
  127. package/dist/api/captcha/getPuzzleCaptchaChallenge.d.ts +7 -0
  128. package/dist/api/captcha/getPuzzleCaptchaChallenge.d.ts.map +1 -0
  129. package/dist/api/captcha/getPuzzleCaptchaChallenge.js +201 -0
  130. package/dist/api/captcha/getPuzzleCaptchaChallenge.js.map +1 -0
  131. package/dist/api/captcha/maintenanceModeResponses.d.ts +5 -0
  132. package/dist/api/captcha/maintenanceModeResponses.d.ts.map +1 -0
  133. package/dist/api/captcha/maintenanceModeResponses.js +42 -0
  134. package/dist/api/captcha/maintenanceModeResponses.js.map +1 -0
  135. package/dist/api/captcha/submitImageCaptchaSolution.d.ts +6 -0
  136. package/dist/api/captcha/submitImageCaptchaSolution.d.ts.map +1 -0
  137. package/dist/api/captcha/submitImageCaptchaSolution.js +19 -2
  138. package/dist/api/captcha/submitImageCaptchaSolution.js.map +1 -0
  139. package/dist/api/captcha/submitPoWCaptchaSolution.d.ts +6 -0
  140. package/dist/api/captcha/submitPoWCaptchaSolution.d.ts.map +1 -0
  141. package/dist/api/captcha/submitPoWCaptchaSolution.js +94 -6
  142. package/dist/api/captcha/submitPoWCaptchaSolution.js.map +1 -0
  143. package/dist/api/captcha/submitPuzzleCaptchaSolution.d.ts +6 -0
  144. package/dist/api/captcha/submitPuzzleCaptchaSolution.d.ts.map +1 -0
  145. package/dist/api/captcha/submitPuzzleCaptchaSolution.js +108 -0
  146. package/dist/api/captcha/submitPuzzleCaptchaSolution.js.map +1 -0
  147. package/dist/api/captcha.d.ts +4 -0
  148. package/dist/api/captcha.d.ts.map +1 -0
  149. package/dist/api/captcha.js +28 -3
  150. package/dist/api/captcha.js.map +1 -0
  151. package/dist/api/dnsEventUrl.d.ts +3 -0
  152. package/dist/api/dnsEventUrl.d.ts.map +1 -0
  153. package/dist/api/dnsEventUrl.js +25 -0
  154. package/dist/api/dnsEventUrl.js.map +1 -0
  155. package/dist/api/domainMiddleware.d.ts +4 -0
  156. package/dist/api/domainMiddleware.d.ts.map +1 -0
  157. package/dist/api/domainMiddleware.js +36 -5
  158. package/dist/api/domainMiddleware.js.map +1 -0
  159. package/dist/api/headerCheckMiddleware.d.ts +4 -0
  160. package/dist/api/headerCheckMiddleware.d.ts.map +1 -0
  161. package/dist/api/headerCheckMiddleware.js.map +1 -0
  162. package/dist/api/ignoreMiddleware.d.ts +3 -0
  163. package/dist/api/ignoreMiddleware.d.ts.map +1 -0
  164. package/dist/api/ignoreMiddleware.js.map +1 -0
  165. package/dist/api/ipInfoMiddleware.d.ts +4 -0
  166. package/dist/api/ipInfoMiddleware.d.ts.map +1 -0
  167. package/dist/api/ipInfoMiddleware.js +20 -0
  168. package/dist/api/ipInfoMiddleware.js.map +1 -0
  169. package/dist/api/ja4Middleware.d.ts +10 -0
  170. package/dist/api/ja4Middleware.d.ts.map +1 -0
  171. package/dist/api/ja4Middleware.js +1 -1
  172. package/dist/api/ja4Middleware.js.map +1 -0
  173. package/dist/api/public.d.ts +4 -0
  174. package/dist/api/public.d.ts.map +1 -0
  175. package/dist/api/public.js.map +1 -0
  176. package/dist/api/robotsMiddleware.d.ts +3 -0
  177. package/dist/api/robotsMiddleware.d.ts.map +1 -0
  178. package/dist/api/robotsMiddleware.js.map +1 -0
  179. package/dist/api/startProviderApi.d.ts +9 -0
  180. package/dist/api/startProviderApi.d.ts.map +1 -0
  181. package/dist/api/startProviderApi.js +217 -0
  182. package/dist/api/startProviderApi.js.map +1 -0
  183. package/dist/api/testSiteKey.d.ts +4 -0
  184. package/dist/api/testSiteKey.d.ts.map +1 -0
  185. package/dist/api/testSiteKey.js +17 -0
  186. package/dist/api/testSiteKey.js.map +1 -0
  187. package/dist/api/validateAddress.d.ts +5 -0
  188. package/dist/api/validateAddress.d.ts.map +1 -0
  189. package/dist/api/validateAddress.js.map +1 -0
  190. package/dist/api/verify.d.ts +4 -0
  191. package/dist/api/verify.d.ts.map +1 -0
  192. package/dist/api/verify.js +146 -9
  193. package/dist/api/verify.js.map +1 -0
  194. package/dist/cjs/api/admin/apiAdminRoutesProvider.cjs +31 -1
  195. package/dist/cjs/api/admin/apiClearAllCountersEndpoint.cjs +59 -0
  196. package/dist/cjs/api/admin/apiDnsEventEndpoint.cjs +65 -0
  197. package/dist/cjs/api/admin/apiGetAllDecisionMachinesEndpoint.cjs +49 -0
  198. package/dist/cjs/api/admin/apiGetDecisionMachineEndpoint.cjs +42 -0
  199. package/dist/cjs/api/admin/apiRegisterSiteKeyEndpoint.cjs +5 -5
  200. package/dist/cjs/api/admin/apiRegisterSiteKeysEndpoint.cjs +34 -0
  201. package/dist/cjs/api/admin/apiRemoveAllDecisionMachinesEndpoint.cjs +41 -0
  202. package/dist/cjs/api/admin/apiRemoveDecisionMachineEndpoint.cjs +42 -0
  203. package/dist/cjs/api/admin/apiRemoveDetectorKeyEndpoint.cjs +6 -6
  204. package/dist/cjs/api/admin/apiRemoveSiteKeyEndpoint.cjs +33 -0
  205. package/dist/cjs/api/admin/apiRemoveSiteKeysEndpoint.cjs +35 -0
  206. package/dist/cjs/api/admin/apiToggleMaintenanceModeEndpoint.cjs +5 -5
  207. package/dist/cjs/api/admin/apiUpdateDecisionMachineEndpoint.cjs +60 -0
  208. package/dist/cjs/api/admin/apiUpdateDetectorKeyEndpoint.cjs +7 -7
  209. package/dist/cjs/api/blacklistRequestInspector.cjs +20 -6
  210. package/dist/cjs/api/block.cjs +19 -8
  211. package/dist/cjs/api/captcha/checkSpamEmail.cjs +79 -0
  212. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.cjs +123 -0
  213. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/constants.cjs +13 -0
  214. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.cjs +287 -0
  215. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/decryptSimdReadings.cjs +14 -0
  216. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/handler.cjs +313 -0
  217. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/honeypotResponse.cjs +17 -0
  218. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/sessionDedup.cjs +28 -0
  219. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.cjs +61 -0
  220. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge.cjs +2 -364
  221. package/dist/cjs/api/captcha/getImageCaptchaChallenge.cjs +35 -8
  222. package/dist/cjs/api/captcha/getPoWCaptchaChallenge.cjs +48 -6
  223. package/dist/cjs/api/captcha/getPuzzleCaptchaChallenge.cjs +200 -0
  224. package/dist/cjs/api/captcha/maintenanceModeResponses.cjs +42 -0
  225. package/dist/cjs/api/captcha/submitImageCaptchaSolution.cjs +19 -2
  226. package/dist/cjs/api/captcha/submitPoWCaptchaSolution.cjs +92 -4
  227. package/dist/cjs/api/captcha/submitPuzzleCaptchaSolution.cjs +107 -0
  228. package/dist/cjs/api/captcha.cjs +29 -4
  229. package/dist/cjs/api/dnsEventUrl.cjs +25 -0
  230. package/dist/cjs/api/domainMiddleware.cjs +36 -5
  231. package/dist/cjs/api/ipInfoMiddleware.cjs +20 -0
  232. package/dist/cjs/api/ja4Middleware.cjs +7 -7
  233. package/dist/cjs/api/startProviderApi.cjs +240 -0
  234. package/dist/cjs/api/testSiteKey.cjs +17 -0
  235. package/dist/cjs/api/verify.cjs +145 -8
  236. package/dist/cjs/compositeIpAddress.cjs +6 -6
  237. package/dist/cjs/index.cjs +19 -0
  238. package/dist/cjs/rules/lang.cjs +1 -1
  239. package/dist/cjs/schedulers/updateSpamEmailDomains.cjs +46 -0
  240. package/dist/cjs/services/ipComparison.cjs +9 -10
  241. package/dist/cjs/tasks/captchaManager.cjs +338 -66
  242. package/dist/cjs/tasks/client/clientTasks.cjs +116 -5
  243. package/dist/cjs/tasks/decisionMachine/decisionMachineRunner.cjs +285 -0
  244. package/dist/cjs/tasks/detection/decodeBehavior.cjs +325 -0
  245. package/dist/cjs/tasks/detection/decodeBehavior.js +15 -0
  246. package/dist/cjs/tasks/detection/decodePayload.cjs +692 -661
  247. package/dist/cjs/tasks/detection/decodePayload.js +15 -0
  248. package/dist/cjs/tasks/detection/decodeSimd.cjs +348 -0
  249. package/dist/cjs/tasks/detection/decodeSimd.js +15 -0
  250. package/dist/cjs/tasks/detection/getBotScore.cjs +4 -2
  251. package/dist/cjs/tasks/frictionless/frictionlessTasks.cjs +146 -43
  252. package/dist/cjs/tasks/frictionless/frictionlessTasksUtils.cjs +10 -7
  253. package/dist/cjs/tasks/frictionless/routingMachine.cjs +58 -0
  254. package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasks.cjs +371 -57
  255. package/dist/cjs/tasks/powCaptcha/powTasks.cjs +450 -55
  256. package/dist/cjs/tasks/puzzleCaptcha/puzzleTasks.cjs +525 -0
  257. package/dist/cjs/tasks/puzzleCaptcha/puzzleTasksUtils.cjs +7 -0
  258. package/dist/cjs/tasks/spam/checkSpamEmail.cjs +147 -0
  259. package/dist/cjs/tasks/spam/checkTrafficFilter.cjs +41 -0
  260. package/dist/cjs/tasks/spam/evaluateEmailSpamRules.cjs +92 -0
  261. package/dist/cjs/tasks/spam/updateSpamEmailDomains.cjs +58 -0
  262. package/dist/cjs/tasks/tasks.cjs +111 -13
  263. package/dist/cjs/util/usageCounters.cjs +201 -0
  264. package/dist/cjs/util.cjs +3 -4
  265. package/dist/cjs/utils/devicePlatform.cjs +10 -0
  266. package/dist/cjs/utils/dns.cjs +102 -0
  267. package/dist/cjs/utils/honeypot/encoders.cjs +86 -0
  268. package/dist/cjs/utils/honeypot/phraseBank.cjs +47 -0
  269. package/dist/cjs/utils/normalizeRequestIp.cjs +27 -0
  270. package/dist/compositeIpAddress.d.ts +5 -0
  271. package/dist/compositeIpAddress.d.ts.map +1 -0
  272. package/dist/compositeIpAddress.js +1 -1
  273. package/dist/compositeIpAddress.js.map +1 -0
  274. package/dist/index.d.ts +23 -0
  275. package/dist/index.d.ts.map +1 -0
  276. package/dist/index.js +16 -0
  277. package/dist/index.js.map +1 -0
  278. package/dist/pairs.d.ts +3 -0
  279. package/dist/pairs.d.ts.map +1 -0
  280. package/dist/pairs.js.map +1 -0
  281. package/dist/rules/lang.d.ts +3 -0
  282. package/dist/rules/lang.d.ts.map +1 -0
  283. package/dist/rules/lang.js +1 -1
  284. package/dist/rules/lang.js.map +1 -0
  285. package/dist/schedulers/captchaScheduler.d.ts +4 -0
  286. package/dist/schedulers/captchaScheduler.d.ts.map +1 -0
  287. package/dist/schedulers/captchaScheduler.js.map +1 -0
  288. package/dist/schedulers/getClientList.d.ts +4 -0
  289. package/dist/schedulers/getClientList.d.ts.map +1 -0
  290. package/dist/schedulers/getClientList.js.map +1 -0
  291. package/dist/schedulers/setClientEntropy.d.ts +4 -0
  292. package/dist/schedulers/setClientEntropy.d.ts.map +1 -0
  293. package/dist/schedulers/setClientEntropy.js.map +1 -0
  294. package/dist/schedulers/updateSpamEmailDomains.d.ts +4 -0
  295. package/dist/schedulers/updateSpamEmailDomains.d.ts.map +1 -0
  296. package/dist/schedulers/updateSpamEmailDomains.js +46 -0
  297. package/dist/schedulers/updateSpamEmailDomains.js.map +1 -0
  298. package/dist/services/ipComparison.d.ts +4 -0
  299. package/dist/services/ipComparison.d.ts.map +1 -0
  300. package/dist/services/ipComparison.js +3 -4
  301. package/dist/services/ipComparison.js.map +1 -0
  302. package/dist/tasks/captchaManager.d.ts +52 -0
  303. package/dist/tasks/captchaManager.d.ts.map +1 -0
  304. package/dist/tasks/captchaManager.js +336 -64
  305. package/dist/tasks/captchaManager.js.map +1 -0
  306. package/dist/tasks/client/clientTasks.d.ts +69 -0
  307. package/dist/tasks/client/clientTasks.d.ts.map +1 -0
  308. package/dist/tasks/client/clientTasks.js +117 -6
  309. package/dist/tasks/client/clientTasks.js.map +1 -0
  310. package/dist/tasks/dataset/datasetTasks.d.ts +13 -0
  311. package/dist/tasks/dataset/datasetTasks.d.ts.map +1 -0
  312. package/dist/tasks/dataset/datasetTasks.js.map +1 -0
  313. package/dist/tasks/dataset/datasetTasksUtils.d.ts +3 -0
  314. package/dist/tasks/dataset/datasetTasksUtils.d.ts.map +1 -0
  315. package/dist/tasks/dataset/datasetTasksUtils.js.map +1 -0
  316. package/dist/tasks/decisionMachine/decisionMachineRunner.d.ts +20 -0
  317. package/dist/tasks/decisionMachine/decisionMachineRunner.d.ts.map +1 -0
  318. package/dist/tasks/decisionMachine/decisionMachineRunner.js +285 -0
  319. package/dist/tasks/decisionMachine/decisionMachineRunner.js.map +1 -0
  320. package/dist/tasks/detection/decodeBehavior.d.ts +3 -0
  321. package/dist/tasks/detection/decodeBehavior.d.ts.map +1 -0
  322. package/dist/tasks/detection/decodeBehavior.js +15 -0
  323. package/dist/tasks/detection/decodeBehavior.js.map +1 -0
  324. package/dist/tasks/detection/decodePayload.d.ts +5 -0
  325. package/dist/tasks/detection/decodePayload.d.ts.map +1 -0
  326. package/dist/tasks/detection/decodePayload.js +15 -849
  327. package/dist/tasks/detection/decodePayload.js.map +1 -0
  328. package/dist/tasks/detection/decodeSimd.d.ts +3 -0
  329. package/dist/tasks/detection/decodeSimd.d.ts.map +1 -0
  330. package/dist/tasks/detection/decodeSimd.js +15 -0
  331. package/dist/tasks/detection/decodeSimd.js.map +1 -0
  332. package/dist/tasks/detection/getBehavioralData.d.ts +4 -0
  333. package/dist/tasks/detection/getBehavioralData.d.ts.map +1 -0
  334. package/dist/tasks/detection/getBehavioralData.js +51 -0
  335. package/dist/tasks/detection/getBehavioralData.js.map +1 -0
  336. package/dist/tasks/detection/getBotScore.d.ts +22 -0
  337. package/dist/tasks/detection/getBotScore.d.ts.map +1 -0
  338. package/dist/tasks/detection/getBotScore.js +6 -4
  339. package/dist/tasks/detection/getBotScore.js.map +1 -0
  340. package/dist/tasks/frictionless/frictionlessTasks.d.ts +69 -0
  341. package/dist/tasks/frictionless/frictionlessTasks.d.ts.map +1 -0
  342. package/dist/tasks/frictionless/frictionlessTasks.js +144 -43
  343. package/dist/tasks/frictionless/frictionlessTasks.js.map +1 -0
  344. package/dist/tasks/frictionless/frictionlessTasksUtils.d.ts +6 -0
  345. package/dist/tasks/frictionless/frictionlessTasksUtils.d.ts.map +1 -0
  346. package/dist/tasks/frictionless/frictionlessTasksUtils.js +10 -7
  347. package/dist/tasks/frictionless/frictionlessTasksUtils.js.map +1 -0
  348. package/dist/tasks/frictionless/routingMachine.d.ts +15 -0
  349. package/dist/tasks/frictionless/routingMachine.d.ts.map +1 -0
  350. package/dist/tasks/frictionless/routingMachine.js +58 -0
  351. package/dist/tasks/frictionless/routingMachine.js.map +1 -0
  352. package/dist/tasks/imgCaptcha/imgCaptchaTasks.d.ts +33 -0
  353. package/dist/tasks/imgCaptcha/imgCaptchaTasks.d.ts.map +1 -0
  354. package/dist/tasks/imgCaptcha/imgCaptchaTasks.js +371 -57
  355. package/dist/tasks/imgCaptcha/imgCaptchaTasks.js.map +1 -0
  356. package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.d.ts +7 -0
  357. package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.d.ts.map +1 -0
  358. package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.js.map +1 -0
  359. package/dist/tasks/index.d.ts +2 -0
  360. package/dist/tasks/index.d.ts.map +1 -0
  361. package/dist/tasks/index.js.map +1 -0
  362. package/dist/tasks/powCaptcha/powTasks.d.ts +36 -0
  363. package/dist/tasks/powCaptcha/powTasks.d.ts.map +1 -0
  364. package/dist/tasks/powCaptcha/powTasks.js +452 -57
  365. package/dist/tasks/powCaptcha/powTasks.js.map +1 -0
  366. package/dist/tasks/powCaptcha/powTasksUtils.d.ts +3 -0
  367. package/dist/tasks/powCaptcha/powTasksUtils.d.ts.map +1 -0
  368. package/dist/tasks/powCaptcha/powTasksUtils.js.map +1 -0
  369. package/dist/tasks/puzzleCaptcha/puzzleTasks.d.ts +32 -0
  370. package/dist/tasks/puzzleCaptcha/puzzleTasks.d.ts.map +1 -0
  371. package/dist/tasks/puzzleCaptcha/puzzleTasks.js +525 -0
  372. package/dist/tasks/puzzleCaptcha/puzzleTasks.js.map +1 -0
  373. package/dist/tasks/puzzleCaptcha/puzzleTasksUtils.d.ts +2 -0
  374. package/dist/tasks/puzzleCaptcha/puzzleTasksUtils.d.ts.map +1 -0
  375. package/dist/tasks/puzzleCaptcha/puzzleTasksUtils.js +7 -0
  376. package/dist/tasks/puzzleCaptcha/puzzleTasksUtils.js.map +1 -0
  377. package/dist/tasks/spam/checkSpamEmail.d.ts +5 -0
  378. package/dist/tasks/spam/checkSpamEmail.d.ts.map +1 -0
  379. package/dist/tasks/spam/checkSpamEmail.js +147 -0
  380. package/dist/tasks/spam/checkSpamEmail.js.map +1 -0
  381. package/dist/tasks/spam/checkTrafficFilter.d.ts +10 -0
  382. package/dist/tasks/spam/checkTrafficFilter.d.ts.map +1 -0
  383. package/dist/tasks/spam/checkTrafficFilter.js +41 -0
  384. package/dist/tasks/spam/checkTrafficFilter.js.map +1 -0
  385. package/dist/tasks/spam/evaluateEmailSpamRules.d.ts +16 -0
  386. package/dist/tasks/spam/evaluateEmailSpamRules.d.ts.map +1 -0
  387. package/dist/tasks/spam/evaluateEmailSpamRules.js +92 -0
  388. package/dist/tasks/spam/evaluateEmailSpamRules.js.map +1 -0
  389. package/dist/tasks/spam/updateSpamEmailDomains.d.ts +4 -0
  390. package/dist/tasks/spam/updateSpamEmailDomains.d.ts.map +1 -0
  391. package/dist/tasks/spam/updateSpamEmailDomains.js +58 -0
  392. package/dist/tasks/spam/updateSpamEmailDomains.js.map +1 -0
  393. package/dist/tasks/tasks.d.ts +36 -0
  394. package/dist/tasks/tasks.d.ts.map +1 -0
  395. package/dist/tasks/tasks.js +102 -4
  396. package/dist/tasks/tasks.js.map +1 -0
  397. package/dist/tests/index.d.ts +2 -0
  398. package/dist/tests/index.d.ts.map +1 -0
  399. package/dist/tests/index.js +2 -0
  400. package/dist/tests/index.js.map +1 -0
  401. package/dist/tests/integration/api/admin/apiRegisterSiteKeyEndpoint.integration.test.d.ts +2 -0
  402. package/dist/tests/integration/api/admin/apiRegisterSiteKeyEndpoint.integration.test.d.ts.map +1 -0
  403. package/dist/tests/integration/api/admin/apiRegisterSiteKeyEndpoint.integration.test.js +82 -0
  404. package/dist/tests/integration/api/admin/apiRegisterSiteKeyEndpoint.integration.test.js.map +1 -0
  405. package/dist/tests/integration/api/admin/apiRegisterSiteKeysEndpoint.integration.test.d.ts +2 -0
  406. package/dist/tests/integration/api/admin/apiRegisterSiteKeysEndpoint.integration.test.d.ts.map +1 -0
  407. package/dist/tests/integration/api/admin/apiRegisterSiteKeysEndpoint.integration.test.js +107 -0
  408. package/dist/tests/integration/api/admin/apiRegisterSiteKeysEndpoint.integration.test.js.map +1 -0
  409. package/dist/tests/integration/api/blacklistRequestInspector.integration.test.d.ts +2 -0
  410. package/dist/tests/integration/api/blacklistRequestInspector.integration.test.d.ts.map +1 -0
  411. package/dist/tests/integration/api/blacklistRequestInspector.integration.test.js +525 -0
  412. package/dist/tests/integration/api/blacklistRequestInspector.integration.test.js.map +1 -0
  413. package/dist/tests/integration/clientSettingsPersistence.integration.test.d.ts +2 -0
  414. package/dist/tests/integration/clientSettingsPersistence.integration.test.d.ts.map +1 -0
  415. package/dist/tests/integration/clientSettingsPersistence.integration.test.js +165 -0
  416. package/dist/tests/integration/clientSettingsPersistence.integration.test.js.map +1 -0
  417. package/dist/tests/integration/decisionMachines.integration.test.d.ts +2 -0
  418. package/dist/tests/integration/decisionMachines.integration.test.d.ts.map +1 -0
  419. package/dist/tests/integration/decisionMachines.integration.test.js +511 -0
  420. package/dist/tests/integration/decisionMachines.integration.test.js.map +1 -0
  421. package/dist/tests/integration/imgCaptcha.integration.test.d.ts +2 -0
  422. package/dist/tests/integration/imgCaptcha.integration.test.d.ts.map +1 -0
  423. package/dist/tests/integration/imgCaptcha.integration.test.js +646 -0
  424. package/dist/tests/integration/imgCaptcha.integration.test.js.map +1 -0
  425. package/dist/tests/integration/ipValidation.integration.test.d.ts +2 -0
  426. package/dist/tests/integration/ipValidation.integration.test.d.ts.map +1 -0
  427. package/dist/tests/integration/ipValidation.integration.test.js +81 -0
  428. package/dist/tests/integration/ipValidation.integration.test.js.map +1 -0
  429. package/dist/tests/integration/mocks/solvedTestCaptchas.d.ts +32 -0
  430. package/dist/tests/integration/mocks/solvedTestCaptchas.d.ts.map +1 -0
  431. package/dist/tests/integration/mocks/solvedTestCaptchas.js +1046 -0
  432. package/dist/tests/integration/mocks/solvedTestCaptchas.js.map +1 -0
  433. package/dist/tests/integration/powCaptcha.integration.test.d.ts +2 -0
  434. package/dist/tests/integration/powCaptcha.integration.test.d.ts.map +1 -0
  435. package/dist/tests/integration/powCaptcha.integration.test.js +502 -0
  436. package/dist/tests/integration/powCaptcha.integration.test.js.map +1 -0
  437. package/dist/tests/integration/registerSitekey.d.ts +3 -0
  438. package/dist/tests/integration/registerSitekey.d.ts.map +1 -0
  439. package/dist/tests/integration/registerSitekey.js +23 -0
  440. package/dist/tests/integration/registerSitekey.js.map +1 -0
  441. package/dist/tests/integration/routingDecisionMachines.integration.test.d.ts +2 -0
  442. package/dist/tests/integration/routingDecisionMachines.integration.test.d.ts.map +1 -0
  443. package/dist/tests/integration/routingDecisionMachines.integration.test.js +276 -0
  444. package/dist/tests/integration/routingDecisionMachines.integration.test.js.map +1 -0
  445. package/dist/tests/integration/testUtils.d.ts +4 -0
  446. package/dist/tests/integration/testUtils.d.ts.map +1 -0
  447. package/dist/tests/integration/testUtils.js +15 -0
  448. package/dist/tests/integration/testUtils.js.map +1 -0
  449. package/dist/tests/integration/usageCounters.integration.test.d.ts +2 -0
  450. package/dist/tests/integration/usageCounters.integration.test.d.ts.map +1 -0
  451. package/dist/tests/integration/usageCounters.integration.test.js +103 -0
  452. package/dist/tests/integration/usageCounters.integration.test.js.map +1 -0
  453. package/dist/tests/integration/userAccessPolicy.d.ts +2 -0
  454. package/dist/tests/integration/userAccessPolicy.d.ts.map +1 -0
  455. package/dist/tests/integration/userAccessPolicy.js +2 -0
  456. package/dist/tests/integration/userAccessPolicy.js.map +1 -0
  457. package/dist/tests/unit/api/admin/apiClearAllCountersEndpoint.unit.test.d.ts +2 -0
  458. package/dist/tests/unit/api/admin/apiClearAllCountersEndpoint.unit.test.d.ts.map +1 -0
  459. package/dist/tests/unit/api/admin/apiClearAllCountersEndpoint.unit.test.js +63 -0
  460. package/dist/tests/unit/api/admin/apiClearAllCountersEndpoint.unit.test.js.map +1 -0
  461. package/dist/tests/unit/api/admin/apiRegisterSiteKeyEndpoint.unit.test.d.ts +2 -0
  462. package/dist/tests/unit/api/admin/apiRegisterSiteKeyEndpoint.unit.test.d.ts.map +1 -0
  463. package/dist/tests/unit/api/admin/apiRegisterSiteKeyEndpoint.unit.test.js +55 -0
  464. package/dist/tests/unit/api/admin/apiRegisterSiteKeyEndpoint.unit.test.js.map +1 -0
  465. package/dist/tests/unit/api/admin/apiRegisterSiteKeysEndpoint.unit.test.d.ts +2 -0
  466. package/dist/tests/unit/api/admin/apiRegisterSiteKeysEndpoint.unit.test.d.ts.map +1 -0
  467. package/dist/tests/unit/api/admin/apiRegisterSiteKeysEndpoint.unit.test.js +67 -0
  468. package/dist/tests/unit/api/admin/apiRegisterSiteKeysEndpoint.unit.test.js.map +1 -0
  469. package/dist/tests/unit/api/admin/apiRemoveDetectorKeyEndpoint.unit.test.d.ts +2 -0
  470. package/dist/tests/unit/api/admin/apiRemoveDetectorKeyEndpoint.unit.test.d.ts.map +1 -0
  471. package/dist/tests/unit/api/admin/apiRemoveDetectorKeyEndpoint.unit.test.js +56 -0
  472. package/dist/tests/unit/api/admin/apiRemoveDetectorKeyEndpoint.unit.test.js.map +1 -0
  473. package/dist/tests/unit/api/admin/apiToggleMaintenanceModeEndpoint.unit.test.d.ts +2 -0
  474. package/dist/tests/unit/api/admin/apiToggleMaintenanceModeEndpoint.unit.test.d.ts.map +1 -0
  475. package/dist/tests/unit/api/admin/apiToggleMaintenanceModeEndpoint.unit.test.js +90 -0
  476. package/dist/tests/unit/api/admin/apiToggleMaintenanceModeEndpoint.unit.test.js.map +1 -0
  477. package/dist/tests/unit/api/admin/apiUpdateDetectorKeyEndpoint.unit.test.d.ts +2 -0
  478. package/dist/tests/unit/api/admin/apiUpdateDetectorKeyEndpoint.unit.test.d.ts.map +1 -0
  479. package/dist/tests/unit/api/admin/apiUpdateDetectorKeyEndpoint.unit.test.js +59 -0
  480. package/dist/tests/unit/api/admin/apiUpdateDetectorKeyEndpoint.unit.test.js.map +1 -0
  481. package/dist/tests/unit/api/adminRoutes.unit.test.d.ts +2 -0
  482. package/dist/tests/unit/api/adminRoutes.unit.test.d.ts.map +1 -0
  483. package/dist/tests/unit/api/adminRoutes.unit.test.js +131 -0
  484. package/dist/tests/unit/api/adminRoutes.unit.test.js.map +1 -0
  485. package/dist/tests/unit/api/blacklistRequestInspector.unit.test.d.ts +2 -0
  486. package/dist/tests/unit/api/blacklistRequestInspector.unit.test.d.ts.map +1 -0
  487. package/dist/tests/unit/api/blacklistRequestInspector.unit.test.js +137 -0
  488. package/dist/tests/unit/api/blacklistRequestInspector.unit.test.js.map +1 -0
  489. package/dist/tests/unit/api/block.unit.test.d.ts +2 -0
  490. package/dist/tests/unit/api/block.unit.test.d.ts.map +1 -0
  491. package/dist/tests/unit/api/block.unit.test.js +60 -0
  492. package/dist/tests/unit/api/block.unit.test.js.map +1 -0
  493. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.unit.test.d.ts +2 -0
  494. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.unit.test.d.ts.map +1 -0
  495. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.unit.test.js +118 -0
  496. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.unit.test.js.map +1 -0
  497. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/constants.unit.test.d.ts +2 -0
  498. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/constants.unit.test.d.ts.map +1 -0
  499. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/constants.unit.test.js +37 -0
  500. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/constants.unit.test.js.map +1 -0
  501. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.unit.test.d.ts +2 -0
  502. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.unit.test.d.ts.map +1 -0
  503. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.unit.test.js +154 -0
  504. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.unit.test.js.map +1 -0
  505. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/decryptSimdReadings.unit.test.d.ts +2 -0
  506. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/decryptSimdReadings.unit.test.d.ts.map +1 -0
  507. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/decryptSimdReadings.unit.test.js +46 -0
  508. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/decryptSimdReadings.unit.test.js.map +1 -0
  509. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/sessionDedup.unit.test.d.ts +2 -0
  510. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/sessionDedup.unit.test.d.ts.map +1 -0
  511. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/sessionDedup.unit.test.js +69 -0
  512. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/sessionDedup.unit.test.js.map +1 -0
  513. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.unit.test.d.ts +2 -0
  514. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.unit.test.d.ts.map +1 -0
  515. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.unit.test.js +98 -0
  516. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.unit.test.js.map +1 -0
  517. package/dist/tests/unit/api/captcha/maintenanceModeResponses.unit.test.d.ts +2 -0
  518. package/dist/tests/unit/api/captcha/maintenanceModeResponses.unit.test.d.ts.map +1 -0
  519. package/dist/tests/unit/api/captcha/maintenanceModeResponses.unit.test.js +60 -0
  520. package/dist/tests/unit/api/captcha/maintenanceModeResponses.unit.test.js.map +1 -0
  521. package/dist/tests/unit/api/captcha/maintenanceModeShortCircuit.unit.test.d.ts +2 -0
  522. package/dist/tests/unit/api/captcha/maintenanceModeShortCircuit.unit.test.d.ts.map +1 -0
  523. package/dist/tests/unit/api/captcha/maintenanceModeShortCircuit.unit.test.js +134 -0
  524. package/dist/tests/unit/api/captcha/maintenanceModeShortCircuit.unit.test.js.map +1 -0
  525. package/dist/tests/unit/api/captcha.unit.test.d.ts +2 -0
  526. package/dist/tests/unit/api/captcha.unit.test.d.ts.map +1 -0
  527. package/dist/tests/unit/api/captcha.unit.test.js +39 -0
  528. package/dist/tests/unit/api/captcha.unit.test.js.map +1 -0
  529. package/dist/tests/unit/api/contextAwareValidation.unit.test.d.ts +2 -0
  530. package/dist/tests/unit/api/contextAwareValidation.unit.test.d.ts.map +1 -0
  531. package/dist/tests/unit/api/contextAwareValidation.unit.test.js +84 -0
  532. package/dist/tests/unit/api/contextAwareValidation.unit.test.js.map +1 -0
  533. package/dist/tests/unit/api/getFrictionlessCaptchaChallenge.unit.test.d.ts +2 -0
  534. package/dist/tests/unit/api/getFrictionlessCaptchaChallenge.unit.test.d.ts.map +1 -0
  535. package/dist/tests/unit/api/getFrictionlessCaptchaChallenge.unit.test.js +435 -0
  536. package/dist/tests/unit/api/getFrictionlessCaptchaChallenge.unit.test.js.map +1 -0
  537. package/dist/tests/unit/api/headerCheckMiddleware.unit.test.d.ts +2 -0
  538. package/dist/tests/unit/api/headerCheckMiddleware.unit.test.d.ts.map +1 -0
  539. package/dist/tests/unit/api/headerCheckMiddleware.unit.test.js +126 -0
  540. package/dist/tests/unit/api/headerCheckMiddleware.unit.test.js.map +1 -0
  541. package/dist/tests/unit/api/ignoreMiddleware.unit.test.d.ts +2 -0
  542. package/dist/tests/unit/api/ignoreMiddleware.unit.test.d.ts.map +1 -0
  543. package/dist/tests/unit/api/ignoreMiddleware.unit.test.js +113 -0
  544. package/dist/tests/unit/api/ignoreMiddleware.unit.test.js.map +1 -0
  545. package/dist/tests/unit/api/ja4Middleware.unit.test.d.ts +2 -0
  546. package/dist/tests/unit/api/ja4Middleware.unit.test.d.ts.map +1 -0
  547. package/dist/tests/unit/api/ja4Middleware.unit.test.js +138 -0
  548. package/dist/tests/unit/api/ja4Middleware.unit.test.js.map +1 -0
  549. package/dist/tests/unit/api/public.unit.test.d.ts +2 -0
  550. package/dist/tests/unit/api/public.unit.test.d.ts.map +1 -0
  551. package/dist/tests/unit/api/public.unit.test.js +198 -0
  552. package/dist/tests/unit/api/public.unit.test.js.map +1 -0
  553. package/dist/tests/unit/api/robotsMiddleware.unit.test.d.ts +2 -0
  554. package/dist/tests/unit/api/robotsMiddleware.unit.test.d.ts.map +1 -0
  555. package/dist/tests/unit/api/robotsMiddleware.unit.test.js +50 -0
  556. package/dist/tests/unit/api/robotsMiddleware.unit.test.js.map +1 -0
  557. package/dist/tests/unit/api/testSiteKey.unit.test.d.ts +2 -0
  558. package/dist/tests/unit/api/testSiteKey.unit.test.d.ts.map +1 -0
  559. package/dist/tests/unit/api/testSiteKey.unit.test.js +51 -0
  560. package/dist/tests/unit/api/testSiteKey.unit.test.js.map +1 -0
  561. package/dist/tests/unit/api/validateAddress.unit.test.d.ts +2 -0
  562. package/dist/tests/unit/api/validateAddress.unit.test.d.ts.map +1 -0
  563. package/dist/tests/unit/api/validateAddress.unit.test.js +141 -0
  564. package/dist/tests/unit/api/validateAddress.unit.test.js.map +1 -0
  565. package/dist/tests/unit/compositeIpAddress.unit.test.d.ts +2 -0
  566. package/dist/tests/unit/compositeIpAddress.unit.test.d.ts.map +1 -0
  567. package/dist/tests/unit/compositeIpAddress.unit.test.js +158 -0
  568. package/dist/tests/unit/compositeIpAddress.unit.test.js.map +1 -0
  569. package/dist/tests/unit/pairs.unit.test.d.ts +2 -0
  570. package/dist/tests/unit/pairs.unit.test.d.ts.map +1 -0
  571. package/dist/tests/unit/pairs.unit.test.js +229 -0
  572. package/dist/tests/unit/pairs.unit.test.js.map +1 -0
  573. package/dist/tests/unit/rules/lang.unit.test.d.ts +2 -0
  574. package/dist/tests/unit/rules/lang.unit.test.d.ts.map +1 -0
  575. package/dist/tests/unit/rules/lang.unit.test.js +207 -0
  576. package/dist/tests/unit/rules/lang.unit.test.js.map +1 -0
  577. package/dist/tests/unit/schedulers/captchaScheduler.unit.test.d.ts +2 -0
  578. package/dist/tests/unit/schedulers/captchaScheduler.unit.test.d.ts.map +1 -0
  579. package/dist/tests/unit/schedulers/captchaScheduler.unit.test.js +75 -0
  580. package/dist/tests/unit/schedulers/captchaScheduler.unit.test.js.map +1 -0
  581. package/dist/tests/unit/schedulers/getClientList.unit.test.d.ts +2 -0
  582. package/dist/tests/unit/schedulers/getClientList.unit.test.d.ts.map +1 -0
  583. package/dist/tests/unit/schedulers/getClientList.unit.test.js +114 -0
  584. package/dist/tests/unit/schedulers/getClientList.unit.test.js.map +1 -0
  585. package/dist/tests/unit/schedulers/setClientEntropy.unit.test.d.ts +2 -0
  586. package/dist/tests/unit/schedulers/setClientEntropy.unit.test.d.ts.map +1 -0
  587. package/dist/tests/unit/schedulers/setClientEntropy.unit.test.js +114 -0
  588. package/dist/tests/unit/schedulers/setClientEntropy.unit.test.js.map +1 -0
  589. package/dist/tests/unit/services/ipComparison.unit.test.d.ts +2 -0
  590. package/dist/tests/unit/services/ipComparison.unit.test.d.ts.map +1 -0
  591. package/dist/tests/unit/services/ipComparison.unit.test.js +272 -0
  592. package/dist/tests/unit/services/ipComparison.unit.test.js.map +1 -0
  593. package/dist/tests/unit/tasks/captchaManager.unit.test.d.ts +2 -0
  594. package/dist/tests/unit/tasks/captchaManager.unit.test.d.ts.map +1 -0
  595. package/dist/tests/unit/tasks/captchaManager.unit.test.js +576 -0
  596. package/dist/tests/unit/tasks/captchaManager.unit.test.js.map +1 -0
  597. package/dist/tests/unit/tasks/client/clientTasks.unit.test.d.ts +2 -0
  598. package/dist/tests/unit/tasks/client/clientTasks.unit.test.d.ts.map +1 -0
  599. package/dist/tests/unit/tasks/client/clientTasks.unit.test.js +343 -0
  600. package/dist/tests/unit/tasks/client/clientTasks.unit.test.js.map +1 -0
  601. package/dist/tests/unit/tasks/dataset/datasetTasks.unit.test.d.ts +2 -0
  602. package/dist/tests/unit/tasks/dataset/datasetTasks.unit.test.d.ts.map +1 -0
  603. package/dist/tests/unit/tasks/dataset/datasetTasks.unit.test.js +92 -0
  604. package/dist/tests/unit/tasks/dataset/datasetTasks.unit.test.js.map +1 -0
  605. package/dist/tests/unit/tasks/dataset/datasetTasksUtils.unit.test.d.ts +2 -0
  606. package/dist/tests/unit/tasks/dataset/datasetTasksUtils.unit.test.d.ts.map +1 -0
  607. package/dist/tests/unit/tasks/dataset/datasetTasksUtils.unit.test.js +75 -0
  608. package/dist/tests/unit/tasks/dataset/datasetTasksUtils.unit.test.js.map +1 -0
  609. package/dist/tests/unit/tasks/decisionMachine/decisionMachineCustomHeaders.unit.test.d.ts +2 -0
  610. package/dist/tests/unit/tasks/decisionMachine/decisionMachineCustomHeaders.unit.test.d.ts.map +1 -0
  611. package/dist/tests/unit/tasks/decisionMachine/decisionMachineCustomHeaders.unit.test.js +213 -0
  612. package/dist/tests/unit/tasks/decisionMachine/decisionMachineCustomHeaders.unit.test.js.map +1 -0
  613. package/dist/tests/unit/tasks/decisionMachine/decisionMachineRunner.unit.test.d.ts +2 -0
  614. package/dist/tests/unit/tasks/decisionMachine/decisionMachineRunner.unit.test.d.ts.map +1 -0
  615. package/dist/tests/unit/tasks/decisionMachine/decisionMachineRunner.unit.test.js +304 -0
  616. package/dist/tests/unit/tasks/decisionMachine/decisionMachineRunner.unit.test.js.map +1 -0
  617. package/dist/tests/unit/tasks/detection/getBehavioralData.unit.test.d.ts +2 -0
  618. package/dist/tests/unit/tasks/detection/getBehavioralData.unit.test.d.ts.map +1 -0
  619. package/dist/tests/unit/tasks/detection/getBehavioralData.unit.test.js +109 -0
  620. package/dist/tests/unit/tasks/detection/getBehavioralData.unit.test.js.map +1 -0
  621. package/dist/tests/unit/tasks/detection/getBotScore.unit.test.d.ts +2 -0
  622. package/dist/tests/unit/tasks/detection/getBotScore.unit.test.d.ts.map +1 -0
  623. package/dist/tests/unit/tasks/detection/getBotScore.unit.test.js +115 -0
  624. package/dist/tests/unit/tasks/detection/getBotScore.unit.test.js.map +1 -0
  625. package/dist/tests/unit/tasks/frictionless/decryptPayload.unit.test.d.ts +2 -0
  626. package/dist/tests/unit/tasks/frictionless/decryptPayload.unit.test.d.ts.map +1 -0
  627. package/dist/tests/unit/tasks/frictionless/decryptPayload.unit.test.js +142 -0
  628. package/dist/tests/unit/tasks/frictionless/decryptPayload.unit.test.js.map +1 -0
  629. package/dist/tests/unit/tasks/frictionless/frictionlessTasks.unit.test.d.ts +2 -0
  630. package/dist/tests/unit/tasks/frictionless/frictionlessTasks.unit.test.d.ts.map +1 -0
  631. package/dist/tests/unit/tasks/frictionless/frictionlessTasks.unit.test.js +350 -0
  632. package/dist/tests/unit/tasks/frictionless/frictionlessTasks.unit.test.js.map +1 -0
  633. package/dist/tests/unit/tasks/frictionless/frictionlessTasksUtils.unit.test.d.ts +2 -0
  634. package/dist/tests/unit/tasks/frictionless/frictionlessTasksUtils.unit.test.d.ts.map +1 -0
  635. package/dist/tests/unit/tasks/frictionless/frictionlessTasksUtils.unit.test.js +103 -0
  636. package/dist/tests/unit/tasks/frictionless/frictionlessTasksUtils.unit.test.js.map +1 -0
  637. package/dist/tests/unit/tasks/frictionless/routingMachine.unit.test.d.ts +2 -0
  638. package/dist/tests/unit/tasks/frictionless/routingMachine.unit.test.d.ts.map +1 -0
  639. package/dist/tests/unit/tasks/frictionless/routingMachine.unit.test.js +169 -0
  640. package/dist/tests/unit/tasks/frictionless/routingMachine.unit.test.js.map +1 -0
  641. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.unit.test.d.ts +2 -0
  642. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.unit.test.d.ts.map +1 -0
  643. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.unit.test.js +1098 -0
  644. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.unit.test.js.map +1 -0
  645. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.unit.test.d.ts +2 -0
  646. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.unit.test.d.ts.map +1 -0
  647. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.unit.test.js +56 -0
  648. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.unit.test.js.map +1 -0
  649. package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.d.ts +2 -0
  650. package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.d.ts.map +1 -0
  651. package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.js +1714 -0
  652. package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.js.map +1 -0
  653. package/dist/tests/unit/tasks/powCaptcha/powTasksUtils.unit.test.d.ts +2 -0
  654. package/dist/tests/unit/tasks/powCaptcha/powTasksUtils.unit.test.d.ts.map +1 -0
  655. package/dist/tests/unit/tasks/powCaptcha/powTasksUtils.unit.test.js +169 -0
  656. package/dist/tests/unit/tasks/powCaptcha/powTasksUtils.unit.test.js.map +1 -0
  657. package/dist/tests/unit/tasks/puzzleCaptcha/puzzleTasks.unit.test.d.ts +2 -0
  658. package/dist/tests/unit/tasks/puzzleCaptcha/puzzleTasks.unit.test.d.ts.map +1 -0
  659. package/dist/tests/unit/tasks/puzzleCaptcha/puzzleTasks.unit.test.js +313 -0
  660. package/dist/tests/unit/tasks/puzzleCaptcha/puzzleTasks.unit.test.js.map +1 -0
  661. package/dist/tests/unit/tasks/puzzleCaptcha/puzzleTasksUtils.unit.test.d.ts +2 -0
  662. package/dist/tests/unit/tasks/puzzleCaptcha/puzzleTasksUtils.unit.test.d.ts.map +1 -0
  663. package/dist/tests/unit/tasks/puzzleCaptcha/puzzleTasksUtils.unit.test.js +29 -0
  664. package/dist/tests/unit/tasks/puzzleCaptcha/puzzleTasksUtils.unit.test.js.map +1 -0
  665. package/dist/tests/unit/tasks/spam/checkSpamEmail.unit.test.d.ts +2 -0
  666. package/dist/tests/unit/tasks/spam/checkSpamEmail.unit.test.d.ts.map +1 -0
  667. package/dist/tests/unit/tasks/spam/checkSpamEmail.unit.test.js +434 -0
  668. package/dist/tests/unit/tasks/spam/checkSpamEmail.unit.test.js.map +1 -0
  669. package/dist/tests/unit/tasks/spam/checkTrafficFilter.unit.test.d.ts +2 -0
  670. package/dist/tests/unit/tasks/spam/checkTrafficFilter.unit.test.d.ts.map +1 -0
  671. package/dist/tests/unit/tasks/spam/checkTrafficFilter.unit.test.js +112 -0
  672. package/dist/tests/unit/tasks/spam/checkTrafficFilter.unit.test.js.map +1 -0
  673. package/dist/tests/unit/tasks/spam/evaluateEmailSpamRules.unit.test.d.ts +2 -0
  674. package/dist/tests/unit/tasks/spam/evaluateEmailSpamRules.unit.test.d.ts.map +1 -0
  675. package/dist/tests/unit/tasks/spam/evaluateEmailSpamRules.unit.test.js +94 -0
  676. package/dist/tests/unit/tasks/spam/evaluateEmailSpamRules.unit.test.js.map +1 -0
  677. package/dist/tests/unit/tasks/streaming/providerDbStreaming.unit.test.d.ts +2 -0
  678. package/dist/tests/unit/tasks/streaming/providerDbStreaming.unit.test.d.ts.map +1 -0
  679. package/dist/tests/unit/tasks/streaming/providerDbStreaming.unit.test.js +94 -0
  680. package/dist/tests/unit/tasks/streaming/providerDbStreaming.unit.test.js.map +1 -0
  681. package/dist/tests/unit/tasks/writeQueueIntegration.unit.test.d.ts +2 -0
  682. package/dist/tests/unit/tasks/writeQueueIntegration.unit.test.d.ts.map +1 -0
  683. package/dist/tests/unit/tasks/writeQueueIntegration.unit.test.js +208 -0
  684. package/dist/tests/unit/tasks/writeQueueIntegration.unit.test.js.map +1 -0
  685. package/dist/tests/unit/testUtils/mockProviderEnv.d.ts +26 -0
  686. package/dist/tests/unit/testUtils/mockProviderEnv.d.ts.map +1 -0
  687. package/dist/tests/unit/testUtils/mockProviderEnv.js +149 -0
  688. package/dist/tests/unit/testUtils/mockProviderEnv.js.map +1 -0
  689. package/dist/tests/unit/util/redisCache.unit.test.d.ts +2 -0
  690. package/dist/tests/unit/util/redisCache.unit.test.d.ts.map +1 -0
  691. package/dist/tests/unit/util/redisCache.unit.test.js +257 -0
  692. package/dist/tests/unit/util/redisCache.unit.test.js.map +1 -0
  693. package/dist/tests/unit/util/usageCounters.unit.test.d.ts +2 -0
  694. package/dist/tests/unit/util/usageCounters.unit.test.d.ts.map +1 -0
  695. package/dist/tests/unit/util/usageCounters.unit.test.js +242 -0
  696. package/dist/tests/unit/util/usageCounters.unit.test.js.map +1 -0
  697. package/dist/tests/unit/util.evaluateIpValidationRules.unit.test.d.ts +2 -0
  698. package/dist/tests/unit/util.evaluateIpValidationRules.unit.test.d.ts.map +1 -0
  699. package/dist/tests/unit/util.evaluateIpValidationRules.unit.test.js +507 -0
  700. package/dist/tests/unit/util.evaluateIpValidationRules.unit.test.js.map +1 -0
  701. package/dist/tests/unit/util.ipDistance.unit.test.d.ts +2 -0
  702. package/dist/tests/unit/util.ipDistance.unit.test.d.ts.map +1 -0
  703. package/dist/tests/unit/util.ipDistance.unit.test.js +99 -0
  704. package/dist/tests/unit/util.ipDistance.unit.test.js.map +1 -0
  705. package/dist/tests/unit/util.unit.test.d.ts +2 -0
  706. package/dist/tests/unit/util.unit.test.d.ts.map +1 -0
  707. package/dist/tests/unit/util.unit.test.js +167 -0
  708. package/dist/tests/unit/util.unit.test.js.map +1 -0
  709. package/dist/tests/unit/utils/devicePlatform.unit.test.d.ts +2 -0
  710. package/dist/tests/unit/utils/devicePlatform.unit.test.d.ts.map +1 -0
  711. package/dist/tests/unit/utils/devicePlatform.unit.test.js +58 -0
  712. package/dist/tests/unit/utils/devicePlatform.unit.test.js.map +1 -0
  713. package/dist/tests/unit/utils/hashUserAgent.unit.test.d.ts +2 -0
  714. package/dist/tests/unit/utils/hashUserAgent.unit.test.d.ts.map +1 -0
  715. package/dist/tests/unit/utils/hashUserAgent.unit.test.js +52 -0
  716. package/dist/tests/unit/utils/hashUserAgent.unit.test.js.map +1 -0
  717. package/dist/tests/unit/utils/hashUserIp.unit.test.d.ts +2 -0
  718. package/dist/tests/unit/utils/hashUserIp.unit.test.d.ts.map +1 -0
  719. package/dist/tests/unit/utils/hashUserIp.unit.test.js +81 -0
  720. package/dist/tests/unit/utils/hashUserIp.unit.test.js.map +1 -0
  721. package/dist/util/usageCounters.d.ts +22 -0
  722. package/dist/util/usageCounters.d.ts.map +1 -0
  723. package/dist/util/usageCounters.js +201 -0
  724. package/dist/util/usageCounters.js.map +1 -0
  725. package/dist/util.d.ts +25 -0
  726. package/dist/util.d.ts.map +1 -0
  727. package/dist/util.js +3 -4
  728. package/dist/util.js.map +1 -0
  729. package/dist/utils/devicePlatform.d.ts +5 -0
  730. package/dist/utils/devicePlatform.d.ts.map +1 -0
  731. package/dist/utils/devicePlatform.js +10 -0
  732. package/dist/utils/devicePlatform.js.map +1 -0
  733. package/dist/utils/dns.d.ts +22 -0
  734. package/dist/utils/dns.d.ts.map +1 -0
  735. package/dist/utils/dns.js +84 -0
  736. package/dist/utils/dns.js.map +1 -0
  737. package/dist/utils/hashUserAgent.d.ts +2 -0
  738. package/dist/utils/hashUserAgent.d.ts.map +1 -0
  739. package/dist/utils/hashUserAgent.js.map +1 -0
  740. package/dist/utils/hashUserIp.d.ts +2 -0
  741. package/dist/utils/hashUserIp.d.ts.map +1 -0
  742. package/dist/utils/hashUserIp.js.map +1 -0
  743. package/dist/utils/honeypot/encoders.d.ts +3 -0
  744. package/dist/utils/honeypot/encoders.d.ts.map +1 -0
  745. package/dist/utils/honeypot/encoders.js +86 -0
  746. package/dist/utils/honeypot/encoders.js.map +1 -0
  747. package/dist/utils/honeypot/phraseBank.d.ts +3 -0
  748. package/dist/utils/honeypot/phraseBank.d.ts.map +1 -0
  749. package/dist/utils/honeypot/phraseBank.js +47 -0
  750. package/dist/utils/honeypot/phraseBank.js.map +1 -0
  751. package/dist/utils/normalizeRequestIp.d.ts +3 -0
  752. package/dist/utils/normalizeRequestIp.d.ts.map +1 -0
  753. package/dist/utils/normalizeRequestIp.js +27 -0
  754. package/dist/utils/normalizeRequestIp.js.map +1 -0
  755. package/package.json +30 -21
  756. package/vite.cjs.config.ts +17 -5
  757. package/vite.esm.config.ts +18 -4
  758. package/vite.test.config.ts +1 -1
  759. package/vite.threads.test.config.ts +1 -1
  760. package/dist/cjs/services/ipInfo.cjs +0 -87
  761. package/dist/services/ipInfo.js +0 -87
@@ -0,0 +1,1714 @@
1
+ import { stringToHex, u8aToHex } from "@polkadot/util";
2
+ import { ApiParams, CaptchaStatus, CaptchaType, POW_SEPARATOR, } from "@prosopo/types";
3
+ import { AccessPolicyType, } from "@prosopo/user-access-policy";
4
+ import { getIPAddress, verifyRecency } from "@prosopo/util";
5
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
6
+ import { getCompositeIpAddress } from "../../../../compositeIpAddress.js";
7
+ import { PowCaptchaManager } from "../../../../tasks/powCaptcha/powTasks.js";
8
+ import { checkPowSignature, validateSolution, } from "../../../../tasks/powCaptcha/powTasksUtils.js";
9
+ vi.mock("@prosopo/util-crypto", () => ({
10
+ signatureVerify: vi.fn(),
11
+ }));
12
+ vi.mock("@polkadot/util", () => ({
13
+ u8aToHex: vi.fn(),
14
+ stringToHex: vi.fn(),
15
+ }));
16
+ vi.mock("@prosopo/util", async (importOriginal) => {
17
+ const actual = (await importOriginal());
18
+ return {
19
+ ...actual,
20
+ verifyRecency: vi.fn(),
21
+ };
22
+ });
23
+ vi.mock("../../../../tasks/powCaptcha/powTasksUtils.js", () => ({
24
+ checkPowSignature: vi.fn(),
25
+ validateSolution: vi.fn(),
26
+ }));
27
+ describe("PowCaptchaManager", () => {
28
+ let db;
29
+ let pair;
30
+ let powCaptchaManager;
31
+ let mockEnv;
32
+ let originalDecide;
33
+ const mockDecisionMachine = (mockFn) => {
34
+ originalDecide = powCaptchaManager.decisionMachineRunner.decide;
35
+ powCaptchaManager.decisionMachineRunner.decide = mockFn;
36
+ };
37
+ const restoreDecisionMachine = () => {
38
+ if (originalDecide) {
39
+ powCaptchaManager.decisionMachineRunner.decide = originalDecide;
40
+ originalDecide = undefined;
41
+ }
42
+ };
43
+ beforeEach(() => {
44
+ db = {
45
+ storePowCaptchaRecord: vi.fn(),
46
+ getPowCaptchaRecordByChallenge: vi.fn(),
47
+ updatePowCaptchaRecordResult: vi.fn(),
48
+ updatePowCaptchaRecord: vi.fn(),
49
+ markDappUserPoWCommitmentsChecked: vi.fn(),
50
+ getClientRecord: vi.fn(),
51
+ getSessionRecordBySessionId: vi.fn(),
52
+ updateSessionRecord: vi.fn(),
53
+ getSpamEmailDomain: vi.fn(),
54
+ };
55
+ pair = {
56
+ sign: vi.fn(),
57
+ address: "testAddress",
58
+ };
59
+ mockEnv = {
60
+ config: {
61
+ ipApi: {
62
+ apiKey: "testKey",
63
+ baseUrl: "https://api.ipapi.is",
64
+ },
65
+ },
66
+ ipInfoService: {
67
+ initialize: vi.fn().mockResolvedValue(undefined),
68
+ isAvailable: vi.fn().mockReturnValue(true),
69
+ lookup: vi.fn(async (ip) => ({
70
+ isValid: false,
71
+ error: "stubbed in test",
72
+ ip,
73
+ })),
74
+ },
75
+ };
76
+ powCaptchaManager = new PowCaptchaManager(db, pair, mockEnv.config);
77
+ vi.clearAllMocks();
78
+ });
79
+ afterEach(() => {
80
+ restoreDecisionMachine();
81
+ });
82
+ describe("getPowCaptchaChallenge", () => {
83
+ it("should generate a PoW captcha challenge", async () => {
84
+ const userAccount = "userAccount";
85
+ const dappAccount = "dappAccount";
86
+ const origin = "origin";
87
+ const challengeRegExp = new RegExp(`[0-9]+___${userAccount}___${dappAccount}`);
88
+ pair.sign.mockReturnValueOnce("signedChallenge");
89
+ u8aToHex.mockReturnValueOnce("hexSignedChallenge");
90
+ const result = await powCaptchaManager.getPowCaptchaChallenge(userAccount, dappAccount, origin);
91
+ expect(result.challenge.match(challengeRegExp)).toBeTruthy();
92
+ expect(result.difficulty).toEqual(4);
93
+ expect(result.providerSignature).toEqual("hexSignedChallenge");
94
+ expect(pair.sign).toHaveBeenCalledWith(stringToHex(result.challenge));
95
+ });
96
+ });
97
+ describe("verifyPowCaptchaSolution", () => {
98
+ it("should verify a valid PoW captcha solution", async () => {
99
+ const requestedAtTimestamp = 123456789;
100
+ const userAccount = "testUserAccount";
101
+ const challenge = `${requestedAtTimestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${pair.address}`;
102
+ const difficulty = 4;
103
+ const providerSignature = "testSignature";
104
+ const userSignature = "testTimestampSignature";
105
+ const nonce = 12345;
106
+ const timeout = 1000;
107
+ const ipAddress = getIPAddress("1.1.1.1");
108
+ const headers = { a: "1", b: "2", c: "3" };
109
+ const challengeRecord = {
110
+ challenge,
111
+ difficulty,
112
+ dappAccount: pair.address,
113
+ userAccount,
114
+ requestedAtTimestamp: new Date(requestedAtTimestamp),
115
+ result: { status: CaptchaStatus.pending },
116
+ userSubmitted: false,
117
+ serverChecked: false,
118
+ ipAddress: getCompositeIpAddress(ipAddress),
119
+ headers,
120
+ ja4: "ja4",
121
+ providerSignature,
122
+ lastUpdatedTimestamp: new Date(),
123
+ };
124
+ verifyRecency.mockImplementation(() => true);
125
+ checkPowSignature.mockImplementation(() => true);
126
+ validateSolution.mockImplementation(() => true);
127
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
128
+ db.updatePowCaptchaRecordResult.mockResolvedValue(true);
129
+ db.markDappUserPoWCommitmentsChecked.mockResolvedValue(true);
130
+ const verifyPowCaptchaSolutionArgs = [
131
+ challenge,
132
+ providerSignature,
133
+ nonce,
134
+ timeout,
135
+ userSignature,
136
+ ipAddress,
137
+ headers,
138
+ undefined,
139
+ ];
140
+ const result = await powCaptchaManager.verifyPowCaptchaSolution(...verifyPowCaptchaSolutionArgs);
141
+ expect(result.verified).toBe(true);
142
+ const verifyRecencyArgs = [
143
+ challenge,
144
+ timeout,
145
+ ];
146
+ expect(verifyRecency).toHaveBeenCalledWith(...verifyRecencyArgs);
147
+ const checKPowSignatureArgs1 = [
148
+ requestedAtTimestamp.toString(),
149
+ userSignature,
150
+ userAccount,
151
+ ApiParams.timestamp,
152
+ ];
153
+ expect(checkPowSignature).toHaveBeenCalledWith(...checKPowSignatureArgs1);
154
+ const checKPowSignatureArgs2 = [
155
+ challenge,
156
+ providerSignature,
157
+ pair.address,
158
+ ApiParams.challenge,
159
+ ];
160
+ expect(checkPowSignature).toHaveBeenCalledWith(...checKPowSignatureArgs2);
161
+ const validateSolutionArgs = [
162
+ nonce,
163
+ challenge,
164
+ difficulty,
165
+ ];
166
+ expect(validateSolution).toHaveBeenCalledWith(...validateSolutionArgs);
167
+ const updatePowCaptchaRecordArgs = [
168
+ challenge,
169
+ { status: CaptchaStatus.approved },
170
+ false,
171
+ true,
172
+ userSignature,
173
+ undefined,
174
+ ];
175
+ expect(db.updatePowCaptchaRecordResult).toHaveBeenCalledWith(...updatePowCaptchaRecordArgs);
176
+ });
177
+ it("should throw an error if PoW captcha solution is invalid", async () => {
178
+ const challenge = `${12345}${POW_SEPARATOR}userAccount${POW_SEPARATOR}dappAccount`;
179
+ const difficulty = 4;
180
+ const signature = "testSignature";
181
+ const nonce = 12345;
182
+ const timeout = 1000;
183
+ const timestampSignature = "testTimestampSignature";
184
+ const ipAddress = getIPAddress("1.1.1.1");
185
+ const headers = { a: "1", b: "2", c: "3" };
186
+ const challengeRecord = {
187
+ challenge,
188
+ dappAccount: pair.address,
189
+ userAccount: "testUserAccount",
190
+ requestedAtTimestamp: new Date(12345),
191
+ result: { status: CaptchaStatus.pending },
192
+ userSubmitted: false,
193
+ serverChecked: false,
194
+ ipAddress: getCompositeIpAddress(ipAddress),
195
+ headers,
196
+ ja4: "ja4",
197
+ providerSignature: "testSignature",
198
+ difficulty,
199
+ lastUpdatedTimestamp: new Date(0),
200
+ };
201
+ verifyRecency.mockImplementation(() => {
202
+ return true;
203
+ });
204
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
205
+ validateSolution.mockImplementation(() => false);
206
+ expect((await powCaptchaManager.verifyPowCaptchaSolution(challenge, signature, nonce, timeout, timestampSignature, ipAddress, headers, undefined)).verified).toBe(false);
207
+ expect(verifyRecency).toHaveBeenCalledWith(challenge, timeout);
208
+ });
209
+ });
210
+ describe("serverVerifyPowCaptchaSolution", () => {
211
+ it("should verify a valid PoW captcha solution on the server", async () => {
212
+ const dappAccount = "dappAccount";
213
+ const timestamp = 123456789;
214
+ const userAccount = "testUserAccount";
215
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${dappAccount}`;
216
+ const timeout = 1000;
217
+ const challengeRecord = {
218
+ challenge,
219
+ dappAccount,
220
+ userAccount,
221
+ requestedAtTimestamp: new Date(timestamp),
222
+ serverChecked: false,
223
+ result: { status: CaptchaStatus.approved },
224
+ ipAddress: getCompositeIpAddress(getIPAddress("1.1.1.1")),
225
+ };
226
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
227
+ verifyRecency.mockImplementation(() => true);
228
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, timeout, mockEnv);
229
+ expect(result.verified).toBe(true);
230
+ expect(db.getPowCaptchaRecordByChallenge).toHaveBeenCalledWith(challenge);
231
+ expect(verifyRecency).toHaveBeenCalledWith(challenge, timeout);
232
+ const markDappUserPoWCommitmentsCheckedArgs = [[challenge]];
233
+ expect(db.markDappUserPoWCommitmentsChecked).toHaveBeenCalledWith(...markDappUserPoWCommitmentsCheckedArgs);
234
+ });
235
+ it("should return verified:false if a challenge cannot be found", async () => {
236
+ const dappAccount = "dappAccount";
237
+ const timestamp = 123456678;
238
+ const userAccount = "testUserAccount";
239
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${dappAccount}`;
240
+ const timeout = 1000;
241
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(null);
242
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, timeout, mockEnv);
243
+ expect(result.verified).toBe(false);
244
+ expect(db.getPowCaptchaRecordByChallenge).toHaveBeenCalledWith(challenge);
245
+ });
246
+ it("should return verified:false when user is blocked by access policy", async () => {
247
+ const dappAccount = "dappAccount";
248
+ const timestamp = 123456789;
249
+ const userAccount = "testUserAccount";
250
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${dappAccount}`;
251
+ const timeout = 1000;
252
+ const ipAddress = getIPAddress("1.1.1.1");
253
+ const headers = { a: "1", b: "2", c: "3" };
254
+ const sessionId = "test-session-id";
255
+ const decryptedHeadHash = "abc123def456";
256
+ const challengeRecord = {
257
+ challenge,
258
+ difficulty: 4,
259
+ dappAccount,
260
+ userAccount,
261
+ requestedAtTimestamp: new Date(timestamp),
262
+ result: { status: CaptchaStatus.approved },
263
+ userSubmitted: true,
264
+ serverChecked: false,
265
+ ipAddress: getCompositeIpAddress(ipAddress),
266
+ headers,
267
+ ja4: "ja4",
268
+ providerSignature: "testSignature",
269
+ lastUpdatedTimestamp: new Date(),
270
+ sessionId,
271
+ };
272
+ const sessionRecord = {
273
+ sessionId,
274
+ createdAt: new Date(),
275
+ token: "test-token",
276
+ score: 0.5,
277
+ threshold: 0.5,
278
+ scoreComponents: { baseScore: 0.5 },
279
+ providerSelectEntropy: 13337,
280
+ ipAddress: getCompositeIpAddress(ipAddress),
281
+ captchaType: CaptchaType.pow,
282
+ webView: false,
283
+ iFrame: false,
284
+ decryptedHeadHash,
285
+ };
286
+ const mockAccessRulesStorage = {
287
+ findRules: vi.fn().mockResolvedValue([
288
+ {
289
+ type: AccessPolicyType.Block,
290
+ headHash: decryptedHeadHash,
291
+ },
292
+ ]),
293
+ insertRules: vi.fn(),
294
+ deleteRules: vi.fn(),
295
+ deleteAllRules: vi.fn(),
296
+ fetchRules: vi.fn(),
297
+ getMissingRuleIds: vi.fn(),
298
+ findRuleIds: vi.fn(),
299
+ fetchAllRuleIds: vi.fn(),
300
+ };
301
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
302
+ db.getSessionRecordBySessionId = vi
303
+ .fn()
304
+ .mockResolvedValue(sessionRecord);
305
+ verifyRecency.mockImplementation(() => true);
306
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, timeout, mockEnv, undefined, mockAccessRulesStorage);
307
+ expect(result.verified).toBe(false);
308
+ });
309
+ it("should not block when access policy has captchaType (not a hard block)", async () => {
310
+ const dappAccount = "dappAccount";
311
+ const timestamp = 123456789;
312
+ const userAccount = "testUserAccount";
313
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${dappAccount}`;
314
+ const timeout = 1000;
315
+ const ipAddress = getIPAddress("1.1.1.1");
316
+ const headers = { a: "1", b: "2", c: "3" };
317
+ const sessionId = "test-session-id";
318
+ const decryptedHeadHash = "abc123def456";
319
+ const challengeRecord = {
320
+ challenge,
321
+ difficulty: 4,
322
+ dappAccount,
323
+ userAccount,
324
+ requestedAtTimestamp: new Date(timestamp),
325
+ result: { status: CaptchaStatus.approved },
326
+ userSubmitted: true,
327
+ serverChecked: false,
328
+ ipAddress: getCompositeIpAddress(ipAddress),
329
+ headers,
330
+ ja4: "ja4",
331
+ providerSignature: "testSignature",
332
+ lastUpdatedTimestamp: new Date(),
333
+ sessionId,
334
+ };
335
+ const sessionRecord = {
336
+ sessionId,
337
+ createdAt: new Date(),
338
+ token: "test-token",
339
+ score: 0.5,
340
+ threshold: 0.5,
341
+ scoreComponents: { baseScore: 0.5 },
342
+ providerSelectEntropy: 13337,
343
+ ipAddress: getCompositeIpAddress(ipAddress),
344
+ captchaType: CaptchaType.pow,
345
+ webView: false,
346
+ iFrame: false,
347
+ decryptedHeadHash,
348
+ };
349
+ const mockAccessRulesStorage = {
350
+ findRules: vi.fn().mockResolvedValue([
351
+ {
352
+ type: AccessPolicyType.Block,
353
+ headHash: decryptedHeadHash,
354
+ captchaType: CaptchaType.image,
355
+ },
356
+ ]),
357
+ insertRules: vi.fn(),
358
+ deleteRules: vi.fn(),
359
+ deleteAllRules: vi.fn(),
360
+ fetchRules: vi.fn(),
361
+ getMissingRuleIds: vi.fn(),
362
+ findRuleIds: vi.fn(),
363
+ fetchAllRuleIds: vi.fn(),
364
+ };
365
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
366
+ db.getSessionRecordBySessionId = vi
367
+ .fn()
368
+ .mockResolvedValue(sessionRecord);
369
+ verifyRecency.mockImplementation(() => true);
370
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, timeout, mockEnv, undefined, mockAccessRulesStorage);
371
+ expect(result.verified).toBe(true);
372
+ });
373
+ it("should verify successfully when no blocking access policy exists", async () => {
374
+ const dappAccount = "dappAccount";
375
+ const timestamp = 123456789;
376
+ const userAccount = "testUserAccount";
377
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${dappAccount}`;
378
+ const timeout = 1000;
379
+ const ipAddress = getIPAddress("1.1.1.1");
380
+ const headers = { a: "1", b: "2", c: "3" };
381
+ const challengeRecord = {
382
+ challenge,
383
+ difficulty: 4,
384
+ dappAccount,
385
+ userAccount,
386
+ requestedAtTimestamp: new Date(timestamp),
387
+ result: { status: CaptchaStatus.approved },
388
+ userSubmitted: true,
389
+ serverChecked: false,
390
+ ipAddress: getCompositeIpAddress(ipAddress),
391
+ headers,
392
+ ja4: "ja4",
393
+ providerSignature: "testSignature",
394
+ lastUpdatedTimestamp: new Date(),
395
+ };
396
+ const mockAccessRulesStorage = {
397
+ findRules: vi.fn().mockResolvedValue([]),
398
+ insertRules: vi.fn(),
399
+ deleteRules: vi.fn(),
400
+ deleteAllRules: vi.fn(),
401
+ fetchRules: vi.fn(),
402
+ getMissingRuleIds: vi.fn(),
403
+ findRuleIds: vi.fn(),
404
+ fetchAllRuleIds: vi.fn(),
405
+ };
406
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
407
+ verifyRecency.mockImplementation(() => true);
408
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, timeout, mockEnv, undefined, mockAccessRulesStorage);
409
+ expect(result.verified).toBe(true);
410
+ });
411
+ });
412
+ describe("serverVerifyPowCaptchaSolution with decision machine", () => {
413
+ it("should allow when decision machine returns allow", async () => {
414
+ const dappAccount = "dappAccount";
415
+ const timestamp = 123456789;
416
+ const userAccount = "testUserAccount";
417
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${dappAccount}`;
418
+ const timeout = 1000;
419
+ const ipAddress = getIPAddress("1.1.1.1");
420
+ const headers = { a: "1", b: "2", c: "3" };
421
+ const behavioralDataPacked = {
422
+ c1: [1, 2, 3],
423
+ c2: [4, 5, 6],
424
+ c3: [7, 8, 9],
425
+ d: "test-device",
426
+ };
427
+ const challengeRecord = {
428
+ challenge,
429
+ difficulty: 4,
430
+ dappAccount,
431
+ userAccount,
432
+ requestedAtTimestamp: new Date(timestamp),
433
+ result: { status: CaptchaStatus.approved },
434
+ userSubmitted: true,
435
+ serverChecked: false,
436
+ ipAddress: getCompositeIpAddress(ipAddress),
437
+ headers,
438
+ ja4: "ja4",
439
+ providerSignature: "testSignature",
440
+ lastUpdatedTimestamp: new Date(),
441
+ behavioralDataPacked,
442
+ deviceCapability: "test-device",
443
+ };
444
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
445
+ verifyRecency.mockImplementation(() => true);
446
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, timeout, mockEnv);
447
+ expect(result.verified).toBe(true);
448
+ });
449
+ it("should deny when decision machine returns deny", async () => {
450
+ const dappAccount = "dappAccount";
451
+ const timestamp = 123456789;
452
+ const userAccount = "testUserAccount";
453
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${dappAccount}`;
454
+ const timeout = 1000;
455
+ const ipAddress = getIPAddress("1.1.1.1");
456
+ const headers = { a: "1", b: "2", c: "3" };
457
+ const behavioralDataPacked = {
458
+ c1: [1, 2, 3],
459
+ c2: [4, 5, 6],
460
+ c3: [7, 8, 9],
461
+ d: "suspicious-device",
462
+ };
463
+ const challengeRecord = {
464
+ challenge,
465
+ difficulty: 4,
466
+ dappAccount,
467
+ userAccount,
468
+ requestedAtTimestamp: new Date(timestamp),
469
+ result: { status: CaptchaStatus.approved },
470
+ userSubmitted: true,
471
+ serverChecked: false,
472
+ ipAddress: getCompositeIpAddress(ipAddress),
473
+ headers,
474
+ ja4: "ja4",
475
+ providerSignature: "testSignature",
476
+ lastUpdatedTimestamp: new Date(),
477
+ behavioralDataPacked,
478
+ deviceCapability: "suspicious-device",
479
+ };
480
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
481
+ db.updatePowCaptchaRecord.mockResolvedValue(undefined);
482
+ verifyRecency.mockImplementation(() => true);
483
+ mockDecisionMachine(vi.fn().mockResolvedValue({
484
+ decision: "deny",
485
+ reason: "Suspicious device detected",
486
+ score: 0,
487
+ }));
488
+ try {
489
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, timeout, mockEnv);
490
+ expect(result.verified).toBe(false);
491
+ }
492
+ finally {
493
+ restoreDecisionMachine();
494
+ }
495
+ });
496
+ it("should allow when no behavioral data is present (no decision machine run)", async () => {
497
+ const dappAccount = "dappAccount";
498
+ const timestamp = 123456789;
499
+ const userAccount = "testUserAccount";
500
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${dappAccount}`;
501
+ const timeout = 1000;
502
+ const ipAddress = getIPAddress("1.1.1.1");
503
+ const headers = { a: "1", b: "2", c: "3" };
504
+ const challengeRecord = {
505
+ challenge,
506
+ difficulty: 4,
507
+ dappAccount,
508
+ userAccount,
509
+ requestedAtTimestamp: new Date(timestamp),
510
+ result: { status: CaptchaStatus.approved },
511
+ userSubmitted: true,
512
+ serverChecked: false,
513
+ ipAddress: getCompositeIpAddress(ipAddress),
514
+ headers,
515
+ ja4: "ja4",
516
+ providerSignature: "testSignature",
517
+ lastUpdatedTimestamp: new Date(),
518
+ };
519
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
520
+ verifyRecency.mockImplementation(() => true);
521
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, timeout, mockEnv);
522
+ expect(result.verified).toBe(true);
523
+ });
524
+ it("should default to allow if decision machine fails", async () => {
525
+ const dappAccount = "dappAccount";
526
+ const timestamp = 123456789;
527
+ const userAccount = "testUserAccount";
528
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${dappAccount}`;
529
+ const timeout = 1000;
530
+ const ipAddress = getIPAddress("1.1.1.1");
531
+ const headers = { a: "1", b: "2", c: "3" };
532
+ const behavioralDataPacked = {
533
+ c1: [1, 2, 3],
534
+ c2: [4, 5, 6],
535
+ c3: [7, 8, 9],
536
+ d: "test-device",
537
+ };
538
+ const challengeRecord = {
539
+ challenge,
540
+ difficulty: 4,
541
+ dappAccount,
542
+ userAccount,
543
+ requestedAtTimestamp: new Date(timestamp),
544
+ result: { status: CaptchaStatus.approved },
545
+ userSubmitted: true,
546
+ serverChecked: false,
547
+ ipAddress: getCompositeIpAddress(ipAddress),
548
+ headers,
549
+ ja4: "ja4",
550
+ providerSignature: "testSignature",
551
+ lastUpdatedTimestamp: new Date(),
552
+ behavioralDataPacked,
553
+ deviceCapability: "test-device",
554
+ };
555
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
556
+ verifyRecency.mockImplementation(() => true);
557
+ mockDecisionMachine(vi.fn().mockRejectedValue(new Error("Decision machine error")));
558
+ try {
559
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, timeout, mockEnv);
560
+ expect(result.verified).toBe(true);
561
+ }
562
+ finally {
563
+ restoreDecisionMachine();
564
+ }
565
+ });
566
+ });
567
+ describe("IP Validation Guard Conditions", () => {
568
+ it("should skip IP validation when ipValidationRules is undefined", async () => {
569
+ const dappAccount = "testDappAccount";
570
+ const userAccount = "testUserAccount";
571
+ const challenge = `123456789${POW_SEPARATOR}userAccount${POW_SEPARATOR}${pair.address}`;
572
+ const timeout = 1000;
573
+ const ip = "1.1.1.1";
574
+ const challengeRecord = {
575
+ dappAccount,
576
+ userAccount,
577
+ result: { status: CaptchaStatus.approved },
578
+ userSubmitted: true,
579
+ challenge,
580
+ serverChecked: false,
581
+ difficulty: 4,
582
+ requestedAtTimestamp: new Date(),
583
+ ipAddress: getCompositeIpAddress(ip),
584
+ headers: { a: "1", b: "2", c: "3" },
585
+ ja4: "ja4",
586
+ providerSignature: "testSignature",
587
+ lastUpdatedTimestamp: new Date(),
588
+ };
589
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
590
+ db.getClientRecord.mockResolvedValue({
591
+ settings: {},
592
+ });
593
+ checkPowSignature.mockReturnValue(true);
594
+ validateSolution.mockReturnValue(true);
595
+ verifyRecency.mockImplementation(() => true);
596
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, timeout, mockEnv, ip);
597
+ expect(result.verified).toBe(true);
598
+ expect(db.updatePowCaptchaRecord).toHaveBeenCalledWith(challenge, {
599
+ providedIp: getCompositeIpAddress(ip),
600
+ });
601
+ });
602
+ it("should skip IP validation when ipValidationRules.enabled is false", async () => {
603
+ const dappAccount = pair.address;
604
+ const challenge = `123456789${POW_SEPARATOR}userAccount${POW_SEPARATOR}${pair.address}`;
605
+ const timeout = 1000;
606
+ const ip = "1.1.1.1";
607
+ const challengeRecord = {
608
+ userAccount: "userAccount",
609
+ challenge,
610
+ serverChecked: false,
611
+ difficulty: 4,
612
+ dappAccount,
613
+ result: {
614
+ status: CaptchaStatus.approved,
615
+ },
616
+ requestedAtTimestamp: new Date(),
617
+ ipAddress: getCompositeIpAddress(ip),
618
+ headers: { a: "1", b: "2", c: "3" },
619
+ ja4: "ja4",
620
+ providerSignature: "testSignature",
621
+ userSubmitted: true,
622
+ lastUpdatedTimestamp: new Date(),
623
+ };
624
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
625
+ db.getClientRecord.mockResolvedValue({
626
+ settings: {
627
+ ipValidationRules: {
628
+ enabled: false,
629
+ actions: {
630
+ countryChangeAction: "reject",
631
+ cityChangeAction: "reject",
632
+ },
633
+ },
634
+ },
635
+ });
636
+ checkPowSignature.mockReturnValue(true);
637
+ validateSolution.mockReturnValue(true);
638
+ verifyRecency.mockImplementation(() => true);
639
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, timeout, mockEnv, ip);
640
+ expect(result.verified).toBe(true);
641
+ expect(db.updatePowCaptchaRecord).toHaveBeenCalledWith(challenge, {
642
+ providedIp: getCompositeIpAddress(ip),
643
+ });
644
+ });
645
+ it("should skip IP validation when ipValidationRules.enabled is undefined", async () => {
646
+ const dappAccount = pair.address;
647
+ const challenge = `123456789${POW_SEPARATOR}userAccount${POW_SEPARATOR}${pair.address}`;
648
+ const timeout = 1000;
649
+ const ip = "1.1.1.1";
650
+ const challengeRecord = {
651
+ dappAccount,
652
+ challenge,
653
+ serverChecked: false,
654
+ difficulty: 4,
655
+ result: {
656
+ status: CaptchaStatus.approved,
657
+ },
658
+ requestedAtTimestamp: new Date(),
659
+ ipAddress: getCompositeIpAddress(ip),
660
+ userAccount: "userAccount",
661
+ headers: { a: "1", b: "2", c: "3" },
662
+ ja4: "ja4",
663
+ providerSignature: "testSignature",
664
+ userSubmitted: true,
665
+ lastUpdatedTimestamp: new Date(),
666
+ };
667
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
668
+ db.getClientRecord.mockResolvedValue({
669
+ settings: {
670
+ ipValidationRules: {
671
+ actions: {
672
+ countryChangeAction: "reject",
673
+ cityChangeAction: "reject",
674
+ },
675
+ },
676
+ },
677
+ });
678
+ checkPowSignature.mockReturnValue(true);
679
+ validateSolution.mockReturnValue(true);
680
+ verifyRecency.mockImplementation(() => true);
681
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, timeout, mockEnv, ip);
682
+ expect(result.verified).toBe(true);
683
+ expect(db.updatePowCaptchaRecord).toHaveBeenCalledWith(challenge, {
684
+ providedIp: getCompositeIpAddress(ip),
685
+ });
686
+ });
687
+ it("should skip IP validation when clientRecord is null", async () => {
688
+ const dappAccount = pair.address;
689
+ const challenge = `123456789${POW_SEPARATOR}userAccount${POW_SEPARATOR}${pair.address}`;
690
+ const timeout = 1000;
691
+ const ip = "1.1.1.1";
692
+ const challengeRecord = {
693
+ dappAccount,
694
+ challenge,
695
+ serverChecked: false,
696
+ difficulty: 4,
697
+ result: {
698
+ status: CaptchaStatus.approved,
699
+ },
700
+ requestedAtTimestamp: new Date(),
701
+ ipAddress: getCompositeIpAddress(ip),
702
+ userAccount: "userAccount",
703
+ headers: { a: "1", b: "2", c: "3" },
704
+ ja4: "ja4",
705
+ providerSignature: "testSignature",
706
+ userSubmitted: true,
707
+ lastUpdatedTimestamp: new Date(),
708
+ };
709
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
710
+ db.getClientRecord.mockResolvedValue(null);
711
+ checkPowSignature.mockReturnValue(true);
712
+ validateSolution.mockReturnValue(true);
713
+ verifyRecency.mockImplementation(() => true);
714
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, timeout, mockEnv, ip);
715
+ expect(result.verified).toBe(true);
716
+ expect(db.updatePowCaptchaRecord).toHaveBeenCalledWith(challenge, {
717
+ providedIp: getCompositeIpAddress(ip),
718
+ });
719
+ });
720
+ it("should skip IP validation when no IP is provided", async () => {
721
+ const dappAccount = pair.address;
722
+ const challenge = `123456789${POW_SEPARATOR}userAccount${POW_SEPARATOR}${pair.address}`;
723
+ const timeout = 1000;
724
+ const challengeRecord = {
725
+ dappAccount,
726
+ challenge,
727
+ serverChecked: false,
728
+ difficulty: 4,
729
+ result: {
730
+ status: CaptchaStatus.approved,
731
+ },
732
+ requestedAtTimestamp: new Date(),
733
+ ipAddress: getCompositeIpAddress("1.1.1.1"),
734
+ userAccount: "userAccount",
735
+ headers: { a: "1", b: "2", c: "3" },
736
+ ja4: "ja4",
737
+ providerSignature: "testSignature",
738
+ userSubmitted: true,
739
+ lastUpdatedTimestamp: new Date(),
740
+ };
741
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
742
+ db.getClientRecord.mockResolvedValue({
743
+ settings: {
744
+ ipValidationRules: {
745
+ enabled: true,
746
+ actions: {
747
+ countryChangeAction: "reject",
748
+ },
749
+ },
750
+ },
751
+ });
752
+ checkPowSignature.mockReturnValue(true);
753
+ validateSolution.mockReturnValue(true);
754
+ verifyRecency.mockImplementation(() => true);
755
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, timeout, mockEnv);
756
+ expect(result.verified).toBe(true);
757
+ });
758
+ });
759
+ describe("Decision machine with no-cache header and no behavioral data", () => {
760
+ it("should deny when no-cache header is present and no behavioral data exists", async () => {
761
+ const dappAccount = "5EZVvsHMrKCFKp5NYNoTyDjTjetoVo1Z4UNNbTwJf1GfN6Xm";
762
+ const timestamp = 1770650564052;
763
+ const userAccount = "5CBFuSD5rgzhwVLLtDsA1WbLVkfrrAMEHdbiBBuZ78QvcEpv";
764
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${dappAccount}${POW_SEPARATOR}351147`;
765
+ const timeout = 1000;
766
+ const ipAddress = getIPAddress("81.159.254.145");
767
+ const headers = {
768
+ host: "pronode2.prosopo.io",
769
+ "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
770
+ "content-length": "182",
771
+ accept: "*/*",
772
+ "accept-encoding": "gzip, deflate, br, zstd",
773
+ "accept-language": "de,de-DE;q=0.9,en;q=0.8",
774
+ "cache-control": "no-cache",
775
+ "content-type": "application/json",
776
+ dnt: "1",
777
+ origin: "https://www.twickets.live",
778
+ pragma: "no-cache",
779
+ priority: "u=1, i",
780
+ "prosopo-site-key": dappAccount,
781
+ "prosopo-user": userAccount,
782
+ referer: "https://www.twickets.live/",
783
+ "sec-ch-ua": '"Google Chrome";v="126", "Chromium";v="126", "Not_A Brand";v="24"',
784
+ "sec-ch-ua-mobile": "?0",
785
+ "sec-ch-ua-platform": '"Windows"',
786
+ "sec-fetch-dest": "empty",
787
+ "sec-fetch-mode": "cors",
788
+ "sec-fetch-site": "cross-site",
789
+ };
790
+ const challengeRecord = {
791
+ challenge,
792
+ difficulty: 4,
793
+ dappAccount,
794
+ userAccount,
795
+ requestedAtTimestamp: new Date(timestamp),
796
+ result: { status: CaptchaStatus.approved },
797
+ userSubmitted: true,
798
+ serverChecked: false,
799
+ ipAddress: getCompositeIpAddress(ipAddress),
800
+ headers,
801
+ ja4: "t13d1516h2_8daaf6152771_02713d6af862",
802
+ providerSignature: "testSignature",
803
+ lastUpdatedTimestamp: new Date(),
804
+ sessionId: "pronode2-6d5deeee-78f1-4af0-97b3-f070037438dd",
805
+ };
806
+ const decisionMachineSource = `
807
+ /**
808
+ * Decision machine for blocking German requests with no-cache header
809
+ */
810
+
811
+ function checkNoCacheNoBehavioural(headers, behavioralDataPacked) {
812
+ const cacheControl =
813
+ "cache-control" in headers ? headers["cache-control"] : "";
814
+
815
+ const hasNoCache = cacheControl.toLowerCase().includes("no-cache");
816
+
817
+ const hasNoBehavioralData =
818
+ !behavioralDataPacked || behavioralDataPacked === "0";
819
+
820
+ if (hasNoCache && hasNoBehavioralData) {
821
+ return {
822
+ decision: "deny",
823
+ reason: "no-cache request with no behavioral data",
824
+ score: 0,
825
+ tags: ["blocked"],
826
+ };
827
+ }
828
+
829
+ return null;
830
+ }
831
+
832
+ module.exports = (input) => {
833
+ const {
834
+ userAccount,
835
+ dappAccount,
836
+ captchaResult,
837
+ headers,
838
+ captchaType,
839
+ countryCode,
840
+ behavioralDataPacked,
841
+ } = input;
842
+
843
+ const noCacheNoBehavioural = checkNoCacheNoBehavioural(
844
+ headers,
845
+ behavioralDataPacked,
846
+ );
847
+ if (noCacheNoBehavioural) {
848
+ return noCacheNoBehavioural;
849
+ }
850
+
851
+ if (captchaResult === "passed") {
852
+ return {
853
+ decision: "allow",
854
+ reason: "Captcha verification successful",
855
+ score: 100,
856
+ tags: [\`captcha-type:\${captchaType || "unknown"}\`],
857
+ };
858
+ }
859
+
860
+ return {
861
+ decision: "deny",
862
+ reason: "Captcha verification failed",
863
+ score: 0,
864
+ tags: ["blocked"],
865
+ };
866
+ };
867
+ `;
868
+ db.getSessionRecordBySessionId.mockResolvedValue(undefined);
869
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
870
+ db.updatePowCaptchaRecord.mockResolvedValue(undefined);
871
+ verifyRecency.mockImplementation(() => true);
872
+ mockDecisionMachine(async (input) => {
873
+ const decideFn = eval(`(function() { const module = { exports: {} }; ${decisionMachineSource}; return module.exports; })()`);
874
+ return decideFn(input);
875
+ });
876
+ try {
877
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, timeout, mockEnv);
878
+ expect(result.verified).toBe(false);
879
+ expect(db.updatePowCaptchaRecord).toHaveBeenCalledWith(challenge, {
880
+ result: {
881
+ status: CaptchaStatus.disapproved,
882
+ reason: "no-cache request with no behavioral data",
883
+ },
884
+ });
885
+ }
886
+ finally {
887
+ restoreDecisionMachine();
888
+ }
889
+ });
890
+ it("should allow when no-cache header is present but behavioral data exists", async () => {
891
+ const dappAccount = "5EZVvsHMrKCFKp5NYNoTyDjTjetoVo1Z4UNNbTwJf1GfN6Xm";
892
+ const timestamp = 1770650564052;
893
+ const userAccount = "5CBFuSD5rgzhwVLLtDsA1WbLVkfrrAMEHdbiBBuZ78QvcEpv";
894
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${dappAccount}${POW_SEPARATOR}351147`;
895
+ const timeout = 1000;
896
+ const ipAddress = getIPAddress("81.159.254.145");
897
+ const headers = {
898
+ "cache-control": "no-cache",
899
+ "user-agent": "Mozilla/5.0",
900
+ };
901
+ const behavioralDataPacked = {
902
+ c1: [1, 2, 3],
903
+ c2: [4, 5, 6],
904
+ c3: [7, 8, 9],
905
+ d: "test-device",
906
+ };
907
+ const challengeRecord = {
908
+ challenge,
909
+ difficulty: 4,
910
+ dappAccount,
911
+ userAccount,
912
+ requestedAtTimestamp: new Date(timestamp),
913
+ result: { status: CaptchaStatus.approved },
914
+ userSubmitted: true,
915
+ serverChecked: false,
916
+ ipAddress: getCompositeIpAddress(ipAddress),
917
+ headers,
918
+ ja4: "t13d1516h2_8daaf6152771_02713d6af862",
919
+ providerSignature: "testSignature",
920
+ lastUpdatedTimestamp: new Date(),
921
+ behavioralDataPacked,
922
+ deviceCapability: "test-device",
923
+ };
924
+ const decisionMachineSource = `
925
+ function checkNoCacheNoBehavioural(headers, behavioralDataPacked) {
926
+ const cacheControl =
927
+ "cache-control" in headers ? headers["cache-control"] : "";
928
+ const hasNoCache = cacheControl.toLowerCase().includes("no-cache");
929
+ const hasNoBehavioralData =
930
+ !behavioralDataPacked || behavioralDataPacked === "0";
931
+
932
+ if (hasNoCache && hasNoBehavioralData) {
933
+ return {
934
+ decision: "deny",
935
+ reason: "no-cache request with no behavioral data",
936
+ score: 0,
937
+ tags: ["blocked"],
938
+ };
939
+ }
940
+ return null;
941
+ }
942
+
943
+ module.exports = (input) => {
944
+ const noCacheNoBehavioural = checkNoCacheNoBehavioural(
945
+ input.headers,
946
+ input.behavioralDataPacked,
947
+ );
948
+ if (noCacheNoBehavioural) {
949
+ return noCacheNoBehavioural;
950
+ }
951
+
952
+ if (input.captchaResult === "passed") {
953
+ return {
954
+ decision: "allow",
955
+ reason: "Captcha verification successful",
956
+ score: 100,
957
+ tags: [\`captcha-type:\${input.captchaType || "unknown"}\`],
958
+ };
959
+ }
960
+
961
+ return {
962
+ decision: "deny",
963
+ reason: "Captcha verification failed",
964
+ score: 0,
965
+ tags: ["blocked"],
966
+ };
967
+ };
968
+ `;
969
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
970
+ verifyRecency.mockImplementation(() => true);
971
+ mockDecisionMachine(async (input) => {
972
+ const decideFn = eval(`(function() { const module = { exports: {} }; ${decisionMachineSource}; return module.exports; })()`);
973
+ return decideFn(input);
974
+ });
975
+ try {
976
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, timeout, mockEnv);
977
+ expect(result.verified).toBe(true);
978
+ }
979
+ finally {
980
+ restoreDecisionMachine();
981
+ }
982
+ });
983
+ it("should allow when behavioral data is missing but no no-cache header", async () => {
984
+ const dappAccount = "5EZVvsHMrKCFKp5NYNoTyDjTjetoVo1Z4UNNbTwJf1GfN6Xm";
985
+ const timestamp = 1770650564052;
986
+ const userAccount = "5CBFuSD5rgzhwVLLtDsA1WbLVkfrrAMEHdbiBBuZ78QvcEpv";
987
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${dappAccount}${POW_SEPARATOR}351147`;
988
+ const timeout = 1000;
989
+ const ipAddress = getIPAddress("81.159.254.145");
990
+ const headers = {
991
+ "user-agent": "Mozilla/5.0",
992
+ };
993
+ const challengeRecord = {
994
+ challenge,
995
+ difficulty: 4,
996
+ dappAccount,
997
+ userAccount,
998
+ requestedAtTimestamp: new Date(timestamp),
999
+ result: { status: CaptchaStatus.approved },
1000
+ userSubmitted: true,
1001
+ serverChecked: false,
1002
+ ipAddress: getCompositeIpAddress(ipAddress),
1003
+ headers,
1004
+ ja4: "t13d1516h2_8daaf6152771_02713d6af862",
1005
+ providerSignature: "testSignature",
1006
+ lastUpdatedTimestamp: new Date(),
1007
+ };
1008
+ const decisionMachineSource = `
1009
+ function checkNoCacheNoBehavioural(headers, behavioralDataPacked) {
1010
+ const cacheControl =
1011
+ "cache-control" in headers ? headers["cache-control"] : "";
1012
+ const hasNoCache = cacheControl.toLowerCase().includes("no-cache");
1013
+ const hasNoBehavioralData =
1014
+ !behavioralDataPacked || behavioralDataPacked === "0";
1015
+
1016
+ if (hasNoCache && hasNoBehavioralData) {
1017
+ return {
1018
+ decision: "deny",
1019
+ reason: "no-cache request with no behavioral data",
1020
+ score: 0,
1021
+ tags: ["blocked"],
1022
+ };
1023
+ }
1024
+ return null;
1025
+ }
1026
+
1027
+ module.exports = (input) => {
1028
+ const noCacheNoBehavioural = checkNoCacheNoBehavioural(
1029
+ input.headers,
1030
+ input.behavioralDataPacked,
1031
+ );
1032
+ if (noCacheNoBehavioural) {
1033
+ return noCacheNoBehavioural;
1034
+ }
1035
+
1036
+ if (input.captchaResult === "passed") {
1037
+ return {
1038
+ decision: "allow",
1039
+ reason: "Captcha verification successful",
1040
+ score: 100,
1041
+ tags: [\`captcha-type:\${input.captchaType || "unknown"}\`],
1042
+ };
1043
+ }
1044
+
1045
+ return {
1046
+ decision: "deny",
1047
+ reason: "Captcha verification failed",
1048
+ score: 0,
1049
+ tags: ["blocked"],
1050
+ };
1051
+ };
1052
+ `;
1053
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
1054
+ verifyRecency.mockImplementation(() => true);
1055
+ mockDecisionMachine(async (input) => {
1056
+ const decideFn = eval(`(function() { const module = { exports: {} }; ${decisionMachineSource}; return module.exports; })()`);
1057
+ return decideFn(input);
1058
+ });
1059
+ try {
1060
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, timeout, mockEnv);
1061
+ expect(result.verified).toBe(true);
1062
+ }
1063
+ finally {
1064
+ restoreDecisionMachine();
1065
+ }
1066
+ });
1067
+ });
1068
+ describe("serverVerifyPowCaptchaSolution with spam email domain checking", () => {
1069
+ it("should disapprove when email domain is found in spam list", async () => {
1070
+ const dappAccount = "dappAccount";
1071
+ const timestamp = 123456789;
1072
+ const userAccount = "testUserAccount";
1073
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${dappAccount}`;
1074
+ const timeout = 1000;
1075
+ const spamEmail = "user@spammydomain.com";
1076
+ const challengeRecord = {
1077
+ challenge,
1078
+ difficulty: 4,
1079
+ dappAccount,
1080
+ userAccount,
1081
+ requestedAtTimestamp: new Date(timestamp),
1082
+ result: { status: CaptchaStatus.approved },
1083
+ userSubmitted: true,
1084
+ serverChecked: false,
1085
+ ipAddress: getCompositeIpAddress(getIPAddress("1.1.1.1")),
1086
+ headers: {},
1087
+ ja4: "ja4",
1088
+ providerSignature: "testSignature",
1089
+ lastUpdatedTimestamp: new Date(),
1090
+ };
1091
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
1092
+ verifyRecency.mockImplementation(() => true);
1093
+ db.markDappUserPoWCommitmentsChecked.mockResolvedValue(undefined);
1094
+ db.updatePowCaptchaRecord.mockResolvedValue(undefined);
1095
+ db.getSpamEmailDomain.mockResolvedValue({
1096
+ domain: "spammydomain.com",
1097
+ });
1098
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, timeout, mockEnv, undefined, undefined, spamEmail, true);
1099
+ expect(result.verified).toBe(false);
1100
+ expect(db.getSpamEmailDomain).toHaveBeenCalledWith("spammydomain.com");
1101
+ expect(db.updatePowCaptchaRecord).toHaveBeenCalledWith(challengeRecord.challenge, {
1102
+ result: {
1103
+ status: CaptchaStatus.disapproved,
1104
+ reason: "API.SPAM_EMAIL_DOMAIN",
1105
+ },
1106
+ });
1107
+ });
1108
+ it("should allow when email domain is not in spam list", async () => {
1109
+ const dappAccount = "dappAccount";
1110
+ const timestamp = 123456789;
1111
+ const userAccount = "testUserAccount";
1112
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${dappAccount}`;
1113
+ const timeout = 1000;
1114
+ const legitimateEmail = "user@legitimate.com";
1115
+ const challengeRecord = {
1116
+ challenge,
1117
+ difficulty: 4,
1118
+ dappAccount,
1119
+ userAccount,
1120
+ requestedAtTimestamp: new Date(timestamp),
1121
+ result: { status: CaptchaStatus.approved },
1122
+ userSubmitted: true,
1123
+ serverChecked: false,
1124
+ ipAddress: getCompositeIpAddress(getIPAddress("1.1.1.1")),
1125
+ headers: {},
1126
+ ja4: "ja4",
1127
+ providerSignature: "testSignature",
1128
+ lastUpdatedTimestamp: new Date(),
1129
+ };
1130
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
1131
+ verifyRecency.mockImplementation(() => true);
1132
+ db.markDappUserPoWCommitmentsChecked.mockResolvedValue(undefined);
1133
+ db.getSpamEmailDomain.mockResolvedValue(null);
1134
+ mockDecisionMachine(async () => ({
1135
+ decision: "allow",
1136
+ reason: "Passed all checks",
1137
+ score: 1,
1138
+ tags: [],
1139
+ }));
1140
+ try {
1141
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, timeout, mockEnv, undefined, undefined, legitimateEmail, true);
1142
+ expect(result.verified).toBe(true);
1143
+ expect(db.getSpamEmailDomain).toHaveBeenCalledWith("legitimate.com");
1144
+ expect(db.updatePowCaptchaRecord).not.toHaveBeenCalledWith(challengeRecord.challenge, expect.objectContaining({
1145
+ result: expect.objectContaining({
1146
+ reason: "API.SPAM_EMAIL_DOMAIN",
1147
+ }),
1148
+ }));
1149
+ }
1150
+ finally {
1151
+ restoreDecisionMachine();
1152
+ }
1153
+ });
1154
+ it("should skip spam check when no email is provided", async () => {
1155
+ const dappAccount = "dappAccount";
1156
+ const timestamp = 123456789;
1157
+ const userAccount = "testUserAccount";
1158
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${dappAccount}`;
1159
+ const timeout = 1000;
1160
+ const challengeRecord = {
1161
+ challenge,
1162
+ difficulty: 4,
1163
+ dappAccount,
1164
+ userAccount,
1165
+ requestedAtTimestamp: new Date(timestamp),
1166
+ result: { status: CaptchaStatus.approved },
1167
+ userSubmitted: true,
1168
+ serverChecked: false,
1169
+ ipAddress: getCompositeIpAddress(getIPAddress("1.1.1.1")),
1170
+ headers: {},
1171
+ ja4: "ja4",
1172
+ providerSignature: "testSignature",
1173
+ lastUpdatedTimestamp: new Date(),
1174
+ };
1175
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
1176
+ verifyRecency.mockImplementation(() => true);
1177
+ db.markDappUserPoWCommitmentsChecked.mockResolvedValue(undefined);
1178
+ mockDecisionMachine(async () => ({
1179
+ decision: "allow",
1180
+ reason: "Passed all checks",
1181
+ score: 1,
1182
+ tags: [],
1183
+ }));
1184
+ try {
1185
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, timeout, mockEnv, undefined, undefined, undefined);
1186
+ expect(result.verified).toBe(true);
1187
+ expect(db.getSpamEmailDomain).not.toHaveBeenCalled();
1188
+ }
1189
+ finally {
1190
+ restoreDecisionMachine();
1191
+ }
1192
+ });
1193
+ it("should handle @domain.com email format correctly", async () => {
1194
+ const dappAccount = "dappAccount";
1195
+ const timestamp = 123456789;
1196
+ const userAccount = "testUserAccount";
1197
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${dappAccount}`;
1198
+ const timeout = 1000;
1199
+ const atDomainEmail = "@spammydomain.com";
1200
+ const challengeRecord = {
1201
+ challenge,
1202
+ difficulty: 4,
1203
+ dappAccount,
1204
+ userAccount,
1205
+ requestedAtTimestamp: new Date(timestamp),
1206
+ result: { status: CaptchaStatus.approved },
1207
+ userSubmitted: true,
1208
+ serverChecked: false,
1209
+ ipAddress: getCompositeIpAddress(getIPAddress("1.1.1.1")),
1210
+ headers: {},
1211
+ ja4: "ja4",
1212
+ providerSignature: "testSignature",
1213
+ lastUpdatedTimestamp: new Date(),
1214
+ };
1215
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
1216
+ verifyRecency.mockImplementation(() => true);
1217
+ db.markDappUserPoWCommitmentsChecked.mockResolvedValue(undefined);
1218
+ db.updatePowCaptchaRecord.mockResolvedValue(undefined);
1219
+ db.getSpamEmailDomain.mockResolvedValue({
1220
+ domain: "spammydomain.com",
1221
+ });
1222
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, timeout, mockEnv, undefined, undefined, atDomainEmail, true);
1223
+ expect(result.verified).toBe(false);
1224
+ expect(db.getSpamEmailDomain).toHaveBeenCalledWith("spammydomain.com");
1225
+ expect(db.updatePowCaptchaRecord).toHaveBeenCalledWith(challengeRecord.challenge, {
1226
+ result: {
1227
+ status: CaptchaStatus.disapproved,
1228
+ reason: "API.SPAM_EMAIL_DOMAIN",
1229
+ },
1230
+ });
1231
+ });
1232
+ it("should handle domain-only format correctly", async () => {
1233
+ const dappAccount = "dappAccount";
1234
+ const timestamp = 123456789;
1235
+ const userAccount = "testUserAccount";
1236
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${dappAccount}`;
1237
+ const timeout = 1000;
1238
+ const domainOnly = "spammydomain.com";
1239
+ const challengeRecord = {
1240
+ challenge,
1241
+ difficulty: 4,
1242
+ dappAccount,
1243
+ userAccount,
1244
+ requestedAtTimestamp: new Date(timestamp),
1245
+ result: { status: CaptchaStatus.approved },
1246
+ userSubmitted: true,
1247
+ serverChecked: false,
1248
+ ipAddress: getCompositeIpAddress(getIPAddress("1.1.1.1")),
1249
+ headers: {},
1250
+ ja4: "ja4",
1251
+ providerSignature: "testSignature",
1252
+ lastUpdatedTimestamp: new Date(),
1253
+ };
1254
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
1255
+ verifyRecency.mockImplementation(() => true);
1256
+ db.markDappUserPoWCommitmentsChecked.mockResolvedValue(undefined);
1257
+ db.updatePowCaptchaRecord.mockResolvedValue(undefined);
1258
+ db.getSpamEmailDomain.mockResolvedValue({
1259
+ domain: "spammydomain.com",
1260
+ });
1261
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, timeout, mockEnv, undefined, undefined, domainOnly, true);
1262
+ expect(result.verified).toBe(false);
1263
+ expect(db.getSpamEmailDomain).toHaveBeenCalledWith("spammydomain.com");
1264
+ });
1265
+ it("should continue verification if spam check fails (fail-safe)", async () => {
1266
+ const dappAccount = "dappAccount";
1267
+ const timestamp = 123456789;
1268
+ const userAccount = "testUserAccount";
1269
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${dappAccount}`;
1270
+ const timeout = 1000;
1271
+ const email = "user@example.com";
1272
+ const challengeRecord = {
1273
+ challenge,
1274
+ difficulty: 4,
1275
+ dappAccount,
1276
+ userAccount,
1277
+ requestedAtTimestamp: new Date(timestamp),
1278
+ result: { status: CaptchaStatus.approved },
1279
+ userSubmitted: true,
1280
+ serverChecked: false,
1281
+ ipAddress: getCompositeIpAddress(getIPAddress("1.1.1.1")),
1282
+ headers: {},
1283
+ ja4: "ja4",
1284
+ providerSignature: "testSignature",
1285
+ lastUpdatedTimestamp: new Date(),
1286
+ };
1287
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
1288
+ verifyRecency.mockImplementation(() => true);
1289
+ db.markDappUserPoWCommitmentsChecked.mockResolvedValue(undefined);
1290
+ db.getSpamEmailDomain.mockRejectedValue(new Error("Database connection error"));
1291
+ mockDecisionMachine(async () => ({
1292
+ decision: "allow",
1293
+ reason: "Passed all checks",
1294
+ score: 1,
1295
+ tags: [],
1296
+ }));
1297
+ try {
1298
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, timeout, mockEnv, undefined, undefined, email, true);
1299
+ expect(result.verified).toBe(true);
1300
+ expect(db.getSpamEmailDomain).toHaveBeenCalled();
1301
+ }
1302
+ finally {
1303
+ restoreDecisionMachine();
1304
+ }
1305
+ });
1306
+ });
1307
+ describe("session result tracking", () => {
1308
+ const ipAddress = getIPAddress("1.1.1.1");
1309
+ const headers = { a: "1", b: "2", c: "3" };
1310
+ const sessionId = "test-session-for-result";
1311
+ it("should update session with approved result after successful user submission", async () => {
1312
+ const timestamp = 123456789;
1313
+ const userAccount = "testUserAccount";
1314
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${pair.address}`;
1315
+ const challengeRecord = {
1316
+ challenge,
1317
+ difficulty: 4,
1318
+ dappAccount: pair.address,
1319
+ userAccount,
1320
+ requestedAtTimestamp: new Date(timestamp),
1321
+ result: { status: CaptchaStatus.pending },
1322
+ userSubmitted: false,
1323
+ serverChecked: false,
1324
+ ipAddress: getCompositeIpAddress(ipAddress),
1325
+ headers,
1326
+ ja4: "ja4",
1327
+ providerSignature: "sig",
1328
+ lastUpdatedTimestamp: new Date(),
1329
+ sessionId,
1330
+ };
1331
+ verifyRecency.mockImplementation(() => true);
1332
+ checkPowSignature.mockImplementation(() => true);
1333
+ validateSolution.mockImplementation(() => true);
1334
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
1335
+ db.updatePowCaptchaRecordResult.mockResolvedValue(undefined);
1336
+ db.updateSessionRecord = vi.fn().mockResolvedValue(undefined);
1337
+ await powCaptchaManager.verifyPowCaptchaSolution(challenge, "sig", 12345, 1000, "userSig", ipAddress, headers);
1338
+ expect(db.updateSessionRecord).toHaveBeenCalledWith(sessionId, {
1339
+ userSubmitted: true,
1340
+ result: { status: CaptchaStatus.approved },
1341
+ });
1342
+ });
1343
+ it("should update session with disapproved result on invalid solution", async () => {
1344
+ const timestamp = 123456789;
1345
+ const userAccount = "testUserAccount";
1346
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${pair.address}`;
1347
+ const challengeRecord = {
1348
+ challenge,
1349
+ difficulty: 4,
1350
+ dappAccount: pair.address,
1351
+ userAccount,
1352
+ requestedAtTimestamp: new Date(timestamp),
1353
+ result: { status: CaptchaStatus.pending },
1354
+ userSubmitted: false,
1355
+ serverChecked: false,
1356
+ ipAddress: getCompositeIpAddress(ipAddress),
1357
+ headers,
1358
+ ja4: "ja4",
1359
+ providerSignature: "sig",
1360
+ lastUpdatedTimestamp: new Date(),
1361
+ sessionId,
1362
+ };
1363
+ verifyRecency.mockImplementation(() => true);
1364
+ checkPowSignature.mockImplementation(() => true);
1365
+ validateSolution.mockImplementation(() => false);
1366
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
1367
+ db.updatePowCaptchaRecordResult.mockResolvedValue(undefined);
1368
+ db.updateSessionRecord = vi.fn().mockResolvedValue(undefined);
1369
+ await powCaptchaManager.verifyPowCaptchaSolution(challenge, "sig", 12345, 1000, "userSig", ipAddress, headers);
1370
+ expect(db.updateSessionRecord).toHaveBeenCalledWith(sessionId, {
1371
+ userSubmitted: true,
1372
+ result: {
1373
+ status: CaptchaStatus.disapproved,
1374
+ reason: "CAPTCHA.INVALID_SOLUTION",
1375
+ },
1376
+ });
1377
+ });
1378
+ it("should update session with disapproved result on timeout during user submission", async () => {
1379
+ const timestamp = 123456789;
1380
+ const userAccount = "testUserAccount";
1381
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${pair.address}`;
1382
+ const challengeRecord = {
1383
+ challenge,
1384
+ difficulty: 4,
1385
+ dappAccount: pair.address,
1386
+ userAccount,
1387
+ requestedAtTimestamp: new Date(timestamp),
1388
+ result: { status: CaptchaStatus.pending },
1389
+ userSubmitted: false,
1390
+ serverChecked: false,
1391
+ ipAddress: getCompositeIpAddress(ipAddress),
1392
+ headers,
1393
+ ja4: "ja4",
1394
+ providerSignature: "sig",
1395
+ lastUpdatedTimestamp: new Date(),
1396
+ sessionId,
1397
+ };
1398
+ verifyRecency.mockImplementation(() => false);
1399
+ checkPowSignature.mockImplementation(() => true);
1400
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
1401
+ db.updatePowCaptchaRecordResult.mockResolvedValue(undefined);
1402
+ db.updateSessionRecord = vi.fn().mockResolvedValue(undefined);
1403
+ await powCaptchaManager.verifyPowCaptchaSolution(challenge, "sig", 12345, 1000, "userSig", ipAddress, headers);
1404
+ expect(db.updateSessionRecord).toHaveBeenCalledWith(sessionId, {
1405
+ userSubmitted: true,
1406
+ result: {
1407
+ status: CaptchaStatus.disapproved,
1408
+ reason: "CAPTCHA.INVALID_TIMESTAMP",
1409
+ },
1410
+ });
1411
+ });
1412
+ it("should not update session when challengeRecord has no sessionId", async () => {
1413
+ const timestamp = 123456789;
1414
+ const userAccount = "testUserAccount";
1415
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${pair.address}`;
1416
+ const challengeRecord = {
1417
+ challenge,
1418
+ difficulty: 4,
1419
+ dappAccount: pair.address,
1420
+ userAccount,
1421
+ requestedAtTimestamp: new Date(timestamp),
1422
+ result: { status: CaptchaStatus.pending },
1423
+ userSubmitted: false,
1424
+ serverChecked: false,
1425
+ ipAddress: getCompositeIpAddress(ipAddress),
1426
+ headers,
1427
+ ja4: "ja4",
1428
+ providerSignature: "sig",
1429
+ lastUpdatedTimestamp: new Date(),
1430
+ };
1431
+ verifyRecency.mockImplementation(() => true);
1432
+ checkPowSignature.mockImplementation(() => true);
1433
+ validateSolution.mockImplementation(() => true);
1434
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
1435
+ db.updatePowCaptchaRecordResult.mockResolvedValue(undefined);
1436
+ db.updateSessionRecord = vi.fn().mockResolvedValue(undefined);
1437
+ await powCaptchaManager.verifyPowCaptchaSolution(challenge, "sig", 12345, 1000, "userSig", ipAddress, headers);
1438
+ expect(db.updateSessionRecord).not.toHaveBeenCalled();
1439
+ });
1440
+ it("should update session as serverChecked and approved on successful server verification", async () => {
1441
+ const dappAccount = "dappAccount";
1442
+ const timestamp = 123456789;
1443
+ const userAccount = "testUserAccount";
1444
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${dappAccount}`;
1445
+ const challengeRecord = {
1446
+ challenge,
1447
+ dappAccount,
1448
+ userAccount,
1449
+ requestedAtTimestamp: new Date(timestamp),
1450
+ serverChecked: false,
1451
+ result: { status: CaptchaStatus.approved },
1452
+ sessionId,
1453
+ ipAddress: getCompositeIpAddress(getIPAddress("1.1.1.1")),
1454
+ };
1455
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
1456
+ verifyRecency.mockImplementation(() => true);
1457
+ db.updateSessionRecord = vi.fn().mockResolvedValue(undefined);
1458
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, 1000, mockEnv);
1459
+ expect(result.verified).toBe(true);
1460
+ expect(db.updateSessionRecord).toHaveBeenCalledWith(sessionId, {
1461
+ serverChecked: true,
1462
+ result: { status: CaptchaStatus.approved },
1463
+ }, true);
1464
+ });
1465
+ it("should update session as serverChecked and disapproved on recency failure", async () => {
1466
+ const dappAccount = "dappAccount";
1467
+ const timestamp = 123456789;
1468
+ const userAccount = "testUserAccount";
1469
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${dappAccount}`;
1470
+ const challengeRecord = {
1471
+ challenge,
1472
+ dappAccount,
1473
+ userAccount,
1474
+ requestedAtTimestamp: new Date(timestamp),
1475
+ serverChecked: false,
1476
+ result: { status: CaptchaStatus.approved },
1477
+ sessionId,
1478
+ };
1479
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
1480
+ verifyRecency.mockImplementation(() => false);
1481
+ db.updatePowCaptchaRecord.mockResolvedValue(undefined);
1482
+ db.updateSessionRecord = vi.fn().mockResolvedValue(undefined);
1483
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, 1000, mockEnv);
1484
+ expect(result.verified).toBe(false);
1485
+ expect(db.updateSessionRecord).toHaveBeenCalledWith(sessionId, {
1486
+ serverChecked: true,
1487
+ result: {
1488
+ status: CaptchaStatus.disapproved,
1489
+ reason: "API.TIMESTAMP_TOO_OLD",
1490
+ },
1491
+ }, true);
1492
+ });
1493
+ });
1494
+ describe("Write batching optimizations", () => {
1495
+ describe("verifyPowCaptchaSolution parallel writes", () => {
1496
+ it("should write result and session update for valid solution with session", async () => {
1497
+ const timestamp = Date.now();
1498
+ const userAccount = "testUserAccount";
1499
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${pair.address}`;
1500
+ const sessionId = "test-session-id";
1501
+ const ipAddress = getIPAddress("1.1.1.1");
1502
+ const headers = { a: "1" };
1503
+ const challengeRecord = {
1504
+ challenge,
1505
+ difficulty: 4,
1506
+ dappAccount: pair.address,
1507
+ userAccount,
1508
+ requestedAtTimestamp: new Date(timestamp),
1509
+ result: { status: CaptchaStatus.pending },
1510
+ userSubmitted: false,
1511
+ serverChecked: false,
1512
+ ipAddress: getCompositeIpAddress(ipAddress),
1513
+ headers,
1514
+ ja4: "ja4",
1515
+ providerSignature: "sig",
1516
+ lastUpdatedTimestamp: new Date(),
1517
+ sessionId,
1518
+ };
1519
+ verifyRecency.mockReturnValue(true);
1520
+ checkPowSignature.mockImplementation(() => { });
1521
+ validateSolution.mockReturnValue(true);
1522
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
1523
+ db.updatePowCaptchaRecordResult.mockResolvedValue(undefined);
1524
+ db.updateSessionRecord.mockResolvedValue(undefined);
1525
+ await powCaptchaManager.verifyPowCaptchaSolution(challenge, "sig", 12345, 1000, "userSig", ipAddress, headers);
1526
+ expect(db.updatePowCaptchaRecordResult).toHaveBeenCalledOnce();
1527
+ expect(db.updateSessionRecord).toHaveBeenCalledWith(sessionId, {
1528
+ userSubmitted: true,
1529
+ result: { status: CaptchaStatus.approved },
1530
+ });
1531
+ });
1532
+ it("should write timeout result and session for timed-out solution with session", async () => {
1533
+ const timestamp = Date.now();
1534
+ const userAccount = "testUserAccount";
1535
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${pair.address}`;
1536
+ const sessionId = "test-session-id";
1537
+ const ipAddress = getIPAddress("1.1.1.1");
1538
+ const headers = {};
1539
+ const challengeRecord = {
1540
+ challenge,
1541
+ difficulty: 4,
1542
+ dappAccount: pair.address,
1543
+ userAccount,
1544
+ requestedAtTimestamp: new Date(timestamp),
1545
+ result: { status: CaptchaStatus.pending },
1546
+ userSubmitted: false,
1547
+ serverChecked: false,
1548
+ ipAddress: getCompositeIpAddress(ipAddress),
1549
+ headers,
1550
+ ja4: "ja4",
1551
+ providerSignature: "sig",
1552
+ lastUpdatedTimestamp: new Date(),
1553
+ sessionId,
1554
+ };
1555
+ verifyRecency.mockReturnValue(false);
1556
+ checkPowSignature.mockImplementation(() => { });
1557
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
1558
+ db.updatePowCaptchaRecordResult.mockResolvedValue(undefined);
1559
+ db.updateSessionRecord.mockResolvedValue(undefined);
1560
+ const result = await powCaptchaManager.verifyPowCaptchaSolution(challenge, "sig", 12345, 1000, "userSig", ipAddress, headers);
1561
+ expect(result.verified).toBe(false);
1562
+ expect(db.updatePowCaptchaRecordResult).toHaveBeenCalledOnce();
1563
+ expect(db.updateSessionRecord).toHaveBeenCalledWith(sessionId, {
1564
+ userSubmitted: true,
1565
+ result: {
1566
+ status: CaptchaStatus.disapproved,
1567
+ reason: "CAPTCHA.INVALID_TIMESTAMP",
1568
+ },
1569
+ });
1570
+ });
1571
+ });
1572
+ describe("serverVerifyPowCaptchaSolution batched writes", () => {
1573
+ it("should accumulate pow record updates and write once on success", async () => {
1574
+ const dappAccount = "dappAccount";
1575
+ const timestamp = Date.now();
1576
+ const userAccount = "testUserAccount";
1577
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${dappAccount}`;
1578
+ const sessionId = "test-session-id";
1579
+ const ipAddress = getIPAddress("1.1.1.1");
1580
+ const challengeRecord = {
1581
+ challenge,
1582
+ difficulty: 4,
1583
+ dappAccount,
1584
+ userAccount,
1585
+ requestedAtTimestamp: new Date(timestamp),
1586
+ result: { status: CaptchaStatus.approved },
1587
+ userSubmitted: true,
1588
+ serverChecked: false,
1589
+ ipAddress: getCompositeIpAddress(ipAddress),
1590
+ headers: {},
1591
+ ja4: "ja4",
1592
+ providerSignature: "sig",
1593
+ lastUpdatedTimestamp: new Date(),
1594
+ sessionId,
1595
+ };
1596
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
1597
+ verifyRecency.mockReturnValue(true);
1598
+ db.markDappUserPoWCommitmentsChecked.mockResolvedValue(undefined);
1599
+ db.updatePowCaptchaRecord.mockResolvedValue(undefined);
1600
+ db.updateSessionRecord.mockResolvedValue(undefined);
1601
+ db.getSessionRecordBySessionId.mockResolvedValue(undefined);
1602
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, 1000, mockEnv);
1603
+ expect(result.verified).toBe(true);
1604
+ expect(db.markDappUserPoWCommitmentsChecked).toHaveBeenCalledWith([
1605
+ challenge,
1606
+ ]);
1607
+ expect(db.updateSessionRecord).toHaveBeenCalledWith(sessionId, {
1608
+ serverChecked: true,
1609
+ result: { status: CaptchaStatus.approved },
1610
+ }, true);
1611
+ });
1612
+ it("should accumulate providedIp and write in single batch on success with IP", async () => {
1613
+ const dappAccount = "dappAccount";
1614
+ const timestamp = Date.now();
1615
+ const userAccount = "testUserAccount";
1616
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${dappAccount}`;
1617
+ const sessionId = "test-session-id";
1618
+ const ipAddress = getIPAddress("1.1.1.1");
1619
+ const challengeRecord = {
1620
+ challenge,
1621
+ difficulty: 4,
1622
+ dappAccount,
1623
+ userAccount,
1624
+ requestedAtTimestamp: new Date(timestamp),
1625
+ result: { status: CaptchaStatus.approved },
1626
+ userSubmitted: true,
1627
+ serverChecked: false,
1628
+ ipAddress: getCompositeIpAddress(ipAddress),
1629
+ headers: {},
1630
+ ja4: "ja4",
1631
+ providerSignature: "sig",
1632
+ lastUpdatedTimestamp: new Date(),
1633
+ sessionId,
1634
+ };
1635
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
1636
+ verifyRecency.mockReturnValue(true);
1637
+ db.markDappUserPoWCommitmentsChecked.mockResolvedValue(undefined);
1638
+ db.getClientRecord.mockResolvedValue(undefined);
1639
+ db.updatePowCaptchaRecord.mockResolvedValue(undefined);
1640
+ db.updateSessionRecord.mockResolvedValue(undefined);
1641
+ db.getSessionRecordBySessionId.mockResolvedValue(undefined);
1642
+ await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, 1000, mockEnv, "2.2.2.2");
1643
+ expect(db.updatePowCaptchaRecord).toHaveBeenCalledOnce();
1644
+ const updateCall = vi.mocked(db.updatePowCaptchaRecord).mock
1645
+ .calls[0];
1646
+ expect(updateCall[0]).toBe(challenge);
1647
+ expect(updateCall[1]).toHaveProperty("providedIp");
1648
+ });
1649
+ it("should not run subsequent checks after first failure (recency)", async () => {
1650
+ const dappAccount = "dappAccount";
1651
+ const timestamp = Date.now();
1652
+ const userAccount = "testUserAccount";
1653
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${dappAccount}`;
1654
+ const challengeRecord = {
1655
+ challenge,
1656
+ difficulty: 4,
1657
+ dappAccount,
1658
+ userAccount,
1659
+ requestedAtTimestamp: new Date(timestamp),
1660
+ result: { status: CaptchaStatus.approved },
1661
+ userSubmitted: true,
1662
+ serverChecked: false,
1663
+ ipAddress: getCompositeIpAddress(getIPAddress("1.1.1.1")),
1664
+ headers: {},
1665
+ ja4: "ja4",
1666
+ providerSignature: "sig",
1667
+ lastUpdatedTimestamp: new Date(),
1668
+ };
1669
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
1670
+ verifyRecency.mockReturnValue(false);
1671
+ db.markDappUserPoWCommitmentsChecked.mockResolvedValue(undefined);
1672
+ db.updatePowCaptchaRecord.mockResolvedValue(undefined);
1673
+ const result = await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, 1000, mockEnv, undefined, undefined, "user@spam.com", true);
1674
+ expect(result.verified).toBe(false);
1675
+ expect(result.reason).toBe("API.TIMESTAMP_TOO_OLD");
1676
+ expect(db.getSpamEmailDomain).not.toHaveBeenCalled();
1677
+ });
1678
+ it("should write accumulated failure result in single batch", async () => {
1679
+ const dappAccount = "dappAccount";
1680
+ const timestamp = Date.now();
1681
+ const userAccount = "testUserAccount";
1682
+ const challenge = `${timestamp}${POW_SEPARATOR}${userAccount}${POW_SEPARATOR}${dappAccount}`;
1683
+ const challengeRecord = {
1684
+ challenge,
1685
+ difficulty: 4,
1686
+ dappAccount,
1687
+ userAccount,
1688
+ requestedAtTimestamp: new Date(timestamp),
1689
+ result: { status: CaptchaStatus.approved },
1690
+ userSubmitted: true,
1691
+ serverChecked: false,
1692
+ ipAddress: getCompositeIpAddress(getIPAddress("1.1.1.1")),
1693
+ headers: {},
1694
+ ja4: "ja4",
1695
+ providerSignature: "sig",
1696
+ lastUpdatedTimestamp: new Date(),
1697
+ };
1698
+ db.getPowCaptchaRecordByChallenge.mockResolvedValue(challengeRecord);
1699
+ verifyRecency.mockReturnValue(false);
1700
+ db.markDappUserPoWCommitmentsChecked.mockResolvedValue(undefined);
1701
+ db.updatePowCaptchaRecord.mockResolvedValue(undefined);
1702
+ await powCaptchaManager.serverVerifyPowCaptchaSolution(dappAccount, challenge, 1000, mockEnv);
1703
+ expect(db.updatePowCaptchaRecord).toHaveBeenCalledOnce();
1704
+ expect(db.updatePowCaptchaRecord).toHaveBeenCalledWith(challenge, {
1705
+ result: {
1706
+ status: CaptchaStatus.disapproved,
1707
+ reason: "API.TIMESTAMP_TOO_OLD",
1708
+ },
1709
+ });
1710
+ });
1711
+ });
1712
+ });
1713
+ });
1714
+ //# sourceMappingURL=powTasks.unit.test.js.map