@prosopo/provider 5.15.0 → 5.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/.turbo/turbo-build$colon$cjs.log +50 -32
  2. package/.turbo/turbo-build$colon$tsc.log +36 -30
  3. package/.turbo/turbo-build.log +51 -33
  4. package/CHANGELOG.md +65 -0
  5. package/dist/api/admin/apiToggleMaintenanceModeEndpoint.d.ts +4 -0
  6. package/dist/api/admin/apiToggleMaintenanceModeEndpoint.d.ts.map +1 -1
  7. package/dist/api/admin/apiToggleMaintenanceModeEndpoint.js +63 -3
  8. package/dist/api/admin/apiToggleMaintenanceModeEndpoint.js.map +1 -1
  9. package/dist/api/blacklistRequestInspector.js +2 -2
  10. package/dist/api/captcha/getAudioCaptchaChallenge.d.ts +5 -0
  11. package/dist/api/captcha/getAudioCaptchaChallenge.d.ts.map +1 -0
  12. package/dist/api/captcha/getAudioCaptchaChallenge.js +37 -0
  13. package/dist/api/captcha/getAudioCaptchaChallenge.js.map +1 -0
  14. package/dist/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.js +1 -1
  15. package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.d.ts.map +1 -1
  16. package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js +11 -7
  17. package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js.map +1 -1
  18. package/dist/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.d.ts.map +1 -1
  19. package/dist/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.js +1 -0
  20. package/dist/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.js.map +1 -1
  21. package/dist/api/captcha/getIconOrderCaptchaChallenge.d.ts +5 -0
  22. package/dist/api/captcha/getIconOrderCaptchaChallenge.d.ts.map +1 -0
  23. package/dist/api/captcha/getIconOrderCaptchaChallenge.js +37 -0
  24. package/dist/api/captcha/getIconOrderCaptchaChallenge.js.map +1 -0
  25. package/dist/api/captcha/getImageCaptchaChallenge.js +2 -2
  26. package/dist/api/captcha/getPoWCaptchaChallenge.js +4 -4
  27. package/dist/api/captcha/getPuzzleCaptchaChallenge.d.ts +1 -3
  28. package/dist/api/captcha/getPuzzleCaptchaChallenge.d.ts.map +1 -1
  29. package/dist/api/captcha/getPuzzleCaptchaChallenge.js +52 -169
  30. package/dist/api/captcha/getPuzzleCaptchaChallenge.js.map +1 -1
  31. package/dist/api/captcha/interactiveCaptchaChallenge.d.ts +42 -0
  32. package/dist/api/captcha/interactiveCaptchaChallenge.d.ts.map +1 -0
  33. package/dist/api/captcha/interactiveCaptchaChallenge.js +154 -0
  34. package/dist/api/captcha/interactiveCaptchaChallenge.js.map +1 -0
  35. package/dist/api/captcha/interactiveCaptchaSolution.d.ts +14 -0
  36. package/dist/api/captcha/interactiveCaptchaSolution.d.ts.map +1 -0
  37. package/dist/api/captcha/interactiveCaptchaSolution.js +77 -0
  38. package/dist/api/captcha/interactiveCaptchaSolution.js.map +1 -0
  39. package/dist/api/captcha/maintenanceModeResponses.d.ts +4 -2
  40. package/dist/api/captcha/maintenanceModeResponses.d.ts.map +1 -1
  41. package/dist/api/captcha/maintenanceModeResponses.js +28 -1
  42. package/dist/api/captcha/maintenanceModeResponses.js.map +1 -1
  43. package/dist/api/captcha/submitAudioCaptchaSolution.d.ts +4 -0
  44. package/dist/api/captcha/submitAudioCaptchaSolution.d.ts.map +1 -0
  45. package/dist/api/captcha/submitAudioCaptchaSolution.js +11 -0
  46. package/dist/api/captcha/submitAudioCaptchaSolution.js.map +1 -0
  47. package/dist/api/captcha/submitIconOrderCaptchaSolution.d.ts +4 -0
  48. package/dist/api/captcha/submitIconOrderCaptchaSolution.d.ts.map +1 -0
  49. package/dist/api/captcha/submitIconOrderCaptchaSolution.js +11 -0
  50. package/dist/api/captcha/submitIconOrderCaptchaSolution.js.map +1 -0
  51. package/dist/api/captcha/submitPoWCaptchaSolution.js +2 -2
  52. package/dist/api/captcha/submitPuzzleCaptchaSolution.d.ts +1 -3
  53. package/dist/api/captcha/submitPuzzleCaptchaSolution.d.ts.map +1 -1
  54. package/dist/api/captcha/submitPuzzleCaptchaSolution.js +6 -73
  55. package/dist/api/captcha/submitPuzzleCaptchaSolution.js.map +1 -1
  56. package/dist/api/captcha/trafficFilterRequestTime.d.ts +4 -1
  57. package/dist/api/captcha/trafficFilterRequestTime.d.ts.map +1 -1
  58. package/dist/api/captcha/trafficFilterRequestTime.js +3 -0
  59. package/dist/api/captcha/trafficFilterRequestTime.js.map +1 -1
  60. package/dist/api/captcha.d.ts.map +1 -1
  61. package/dist/api/captcha.js +8 -0
  62. package/dist/api/captcha.js.map +1 -1
  63. package/dist/api/domainMiddleware.js +1 -1
  64. package/dist/api/ja4Middleware.js +1 -1
  65. package/dist/api/startProviderApi.js +1 -1
  66. package/dist/api/verify.d.ts.map +1 -1
  67. package/dist/api/verify.js +153 -96
  68. package/dist/api/verify.js.map +1 -1
  69. package/dist/cjs/api/admin/apiToggleMaintenanceModeEndpoint.cjs +65 -2
  70. package/dist/cjs/api/blacklistRequestInspector.cjs +2 -2
  71. package/dist/cjs/api/captcha/getAudioCaptchaChallenge.cjs +37 -0
  72. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.cjs +1 -1
  73. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/handler.cjs +11 -7
  74. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.cjs +1 -0
  75. package/dist/cjs/api/captcha/getIconOrderCaptchaChallenge.cjs +37 -0
  76. package/dist/cjs/api/captcha/getImageCaptchaChallenge.cjs +2 -2
  77. package/dist/cjs/api/captcha/getPoWCaptchaChallenge.cjs +4 -4
  78. package/dist/cjs/api/captcha/getPuzzleCaptchaChallenge.cjs +51 -168
  79. package/dist/cjs/api/captcha/interactiveCaptchaChallenge.cjs +154 -0
  80. package/dist/cjs/api/captcha/interactiveCaptchaSolution.cjs +77 -0
  81. package/dist/cjs/api/captcha/maintenanceModeResponses.cjs +29 -0
  82. package/dist/cjs/api/captcha/submitAudioCaptchaSolution.cjs +11 -0
  83. package/dist/cjs/api/captcha/submitIconOrderCaptchaSolution.cjs +11 -0
  84. package/dist/cjs/api/captcha/submitPoWCaptchaSolution.cjs +2 -2
  85. package/dist/cjs/api/captcha/submitPuzzleCaptchaSolution.cjs +6 -73
  86. package/dist/cjs/api/captcha/trafficFilterRequestTime.cjs +3 -0
  87. package/dist/cjs/api/captcha.cjs +8 -0
  88. package/dist/cjs/api/domainMiddleware.cjs +1 -1
  89. package/dist/cjs/api/ja4Middleware.cjs +1 -1
  90. package/dist/cjs/api/startProviderApi.cjs +1 -1
  91. package/dist/cjs/api/verify.cjs +152 -95
  92. package/dist/cjs/index.cjs +1 -1
  93. package/dist/cjs/tasks/audio/audioBuffer.cjs +72 -0
  94. package/dist/cjs/tasks/audio/audioRenderer.cjs +32 -0
  95. package/dist/cjs/tasks/audioAlternative.cjs +23 -0
  96. package/dist/cjs/tasks/audioCaptcha/audioTasks.cjs +65 -0
  97. package/dist/cjs/tasks/audioCaptcha/audioTasksUtils.cjs +19 -0
  98. package/dist/cjs/tasks/captchaManager.cjs +14 -2
  99. package/dist/cjs/tasks/captchaTypeSelection.cjs +10 -3
  100. package/dist/cjs/tasks/client/clientTasks.cjs +2 -2
  101. package/dist/cjs/tasks/frictionless/frictionlessTasks.cjs +26 -6
  102. package/dist/cjs/tasks/iconOrder/iconOrderRenderer.cjs +42 -0
  103. package/dist/cjs/tasks/iconOrderCaptcha/iconOrderTasks.cjs +69 -0
  104. package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasks.cjs +8 -8
  105. package/dist/cjs/tasks/interactiveCaptcha/interactiveCaptchaManager.cjs +423 -0
  106. package/dist/cjs/tasks/powCaptcha/powTasks.cjs +8 -8
  107. package/dist/cjs/tasks/powCaptcha/powTasksUtils.cjs +1 -1
  108. package/dist/cjs/tasks/puzzleCaptcha/puzzleTasks.cjs +40 -486
  109. package/dist/cjs/tasks/spam/checkTrafficFilter.cjs +18 -1
  110. package/dist/cjs/tasks/tasks.cjs +6 -0
  111. package/dist/cjs/util.cjs +1 -1
  112. package/dist/index.js +2 -2
  113. package/dist/tasks/audio/audioBuffer.d.ts +23 -0
  114. package/dist/tasks/audio/audioBuffer.d.ts.map +1 -0
  115. package/dist/tasks/audio/audioBuffer.js +70 -0
  116. package/dist/tasks/audio/audioBuffer.js.map +1 -0
  117. package/dist/tasks/audio/audioRenderer.d.ts +11 -0
  118. package/dist/tasks/audio/audioRenderer.d.ts.map +1 -0
  119. package/dist/tasks/audio/audioRenderer.js +31 -0
  120. package/dist/tasks/audio/audioRenderer.js.map +1 -0
  121. package/dist/tasks/audioAlternative.d.ts +8 -0
  122. package/dist/tasks/audioAlternative.d.ts.map +1 -0
  123. package/dist/tasks/audioAlternative.js +20 -0
  124. package/dist/tasks/audioAlternative.js.map +1 -0
  125. package/dist/tasks/audioCaptcha/audioTasks.d.ts +19 -0
  126. package/dist/tasks/audioCaptcha/audioTasks.d.ts.map +1 -0
  127. package/dist/tasks/audioCaptcha/audioTasks.js +65 -0
  128. package/dist/tasks/audioCaptcha/audioTasks.js.map +1 -0
  129. package/dist/tasks/audioCaptcha/audioTasksUtils.d.ts +3 -0
  130. package/dist/tasks/audioCaptcha/audioTasksUtils.d.ts.map +1 -0
  131. package/dist/tasks/audioCaptcha/audioTasksUtils.js +18 -0
  132. package/dist/tasks/audioCaptcha/audioTasksUtils.js.map +1 -0
  133. package/dist/tasks/captchaManager.d.ts +9 -3
  134. package/dist/tasks/captchaManager.d.ts.map +1 -1
  135. package/dist/tasks/captchaManager.js +15 -3
  136. package/dist/tasks/captchaManager.js.map +1 -1
  137. package/dist/tasks/captchaTypeSelection.d.ts +1 -1
  138. package/dist/tasks/captchaTypeSelection.d.ts.map +1 -1
  139. package/dist/tasks/captchaTypeSelection.js +11 -4
  140. package/dist/tasks/captchaTypeSelection.js.map +1 -1
  141. package/dist/tasks/client/clientTasks.js +2 -2
  142. package/dist/tasks/frictionless/frictionlessTasks.d.ts +3 -0
  143. package/dist/tasks/frictionless/frictionlessTasks.d.ts.map +1 -1
  144. package/dist/tasks/frictionless/frictionlessTasks.js +26 -6
  145. package/dist/tasks/frictionless/frictionlessTasks.js.map +1 -1
  146. package/dist/tasks/iconOrder/iconOrderRenderer.d.ts +12 -0
  147. package/dist/tasks/iconOrder/iconOrderRenderer.d.ts.map +1 -0
  148. package/dist/tasks/iconOrder/iconOrderRenderer.js +40 -0
  149. package/dist/tasks/iconOrder/iconOrderRenderer.js.map +1 -0
  150. package/dist/tasks/iconOrderCaptcha/iconOrderTasks.d.ts +22 -0
  151. package/dist/tasks/iconOrderCaptcha/iconOrderTasks.d.ts.map +1 -0
  152. package/dist/tasks/iconOrderCaptcha/iconOrderTasks.js +69 -0
  153. package/dist/tasks/iconOrderCaptcha/iconOrderTasks.js.map +1 -0
  154. package/dist/tasks/imgCaptcha/imgCaptchaTasks.js +8 -8
  155. package/dist/tasks/interactiveCaptcha/interactiveCaptchaManager.d.ts +52 -0
  156. package/dist/tasks/interactiveCaptcha/interactiveCaptchaManager.d.ts.map +1 -0
  157. package/dist/tasks/interactiveCaptcha/interactiveCaptchaManager.js +423 -0
  158. package/dist/tasks/interactiveCaptcha/interactiveCaptchaManager.js.map +1 -0
  159. package/dist/tasks/powCaptcha/powTasks.js +8 -8
  160. package/dist/tasks/powCaptcha/powTasksUtils.js +1 -1
  161. package/dist/tasks/puzzleCaptcha/puzzleTasks.d.ts +14 -16
  162. package/dist/tasks/puzzleCaptcha/puzzleTasks.d.ts.map +1 -1
  163. package/dist/tasks/puzzleCaptcha/puzzleTasks.js +41 -487
  164. package/dist/tasks/puzzleCaptcha/puzzleTasks.js.map +1 -1
  165. package/dist/tasks/spam/checkTrafficFilter.d.ts +4 -1
  166. package/dist/tasks/spam/checkTrafficFilter.d.ts.map +1 -1
  167. package/dist/tasks/spam/checkTrafficFilter.js +18 -1
  168. package/dist/tasks/spam/checkTrafficFilter.js.map +1 -1
  169. package/dist/tasks/tasks.d.ts +4 -0
  170. package/dist/tasks/tasks.d.ts.map +1 -1
  171. package/dist/tasks/tasks.js +6 -0
  172. package/dist/tasks/tasks.js.map +1 -1
  173. package/dist/tests/integration/api/admin/apiRegisterSiteKeyEndpoint.integration.test.js +7 -3
  174. package/dist/tests/integration/api/admin/apiRegisterSiteKeyEndpoint.integration.test.js.map +1 -1
  175. package/dist/tests/integration/clientSettingsPersistence.integration.test.js +43 -3
  176. package/dist/tests/integration/clientSettingsPersistence.integration.test.js.map +1 -1
  177. package/dist/tests/unit/api/admin/apiRegisterSiteKeyEndpoint.unit.test.js +4 -2
  178. package/dist/tests/unit/api/admin/apiRegisterSiteKeyEndpoint.unit.test.js.map +1 -1
  179. package/dist/tests/unit/api/admin/apiToggleMaintenanceModeEndpoint.unit.test.js +76 -1
  180. package/dist/tests/unit/api/admin/apiToggleMaintenanceModeEndpoint.unit.test.js.map +1 -1
  181. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/constants.unit.test.js +27 -8
  182. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/constants.unit.test.js.map +1 -1
  183. package/dist/tests/unit/api/captcha/maintenanceModeShortCircuit.unit.test.js +20 -0
  184. package/dist/tests/unit/api/captcha/maintenanceModeShortCircuit.unit.test.js.map +1 -1
  185. package/dist/tests/unit/api/getFrictionlessCaptchaChallenge.unit.test.js +28 -2
  186. package/dist/tests/unit/api/getFrictionlessCaptchaChallenge.unit.test.js.map +1 -1
  187. package/dist/tests/unit/tasks/audio/audioBuffer.unit.test.d.ts +2 -0
  188. package/dist/tests/unit/tasks/audio/audioBuffer.unit.test.d.ts.map +1 -0
  189. package/dist/tests/unit/tasks/audio/audioBuffer.unit.test.js +105 -0
  190. package/dist/tests/unit/tasks/audio/audioBuffer.unit.test.js.map +1 -0
  191. package/dist/tests/unit/tasks/audioAlternative.unit.test.d.ts +2 -0
  192. package/dist/tests/unit/tasks/audioAlternative.unit.test.d.ts.map +1 -0
  193. package/dist/tests/unit/tasks/audioAlternative.unit.test.js +76 -0
  194. package/dist/tests/unit/tasks/audioAlternative.unit.test.js.map +1 -0
  195. package/dist/tests/unit/tasks/audioCaptcha/audioTasks.unit.test.d.ts +2 -0
  196. package/dist/tests/unit/tasks/audioCaptcha/audioTasks.unit.test.d.ts.map +1 -0
  197. package/dist/tests/unit/tasks/audioCaptcha/audioTasks.unit.test.js +259 -0
  198. package/dist/tests/unit/tasks/audioCaptcha/audioTasks.unit.test.js.map +1 -0
  199. package/dist/tests/unit/tasks/audioCaptcha/audioTasksUtils.unit.test.d.ts +2 -0
  200. package/dist/tests/unit/tasks/audioCaptcha/audioTasksUtils.unit.test.d.ts.map +1 -0
  201. package/dist/tests/unit/tasks/audioCaptcha/audioTasksUtils.unit.test.js +52 -0
  202. package/dist/tests/unit/tasks/audioCaptcha/audioTasksUtils.unit.test.js.map +1 -0
  203. package/dist/tests/unit/tasks/captchaManager.unit.test.js +120 -2
  204. package/dist/tests/unit/tasks/captchaManager.unit.test.js.map +1 -1
  205. package/dist/tests/unit/tasks/captchaTypeSelection.unit.test.js +48 -5
  206. package/dist/tests/unit/tasks/captchaTypeSelection.unit.test.js.map +1 -1
  207. package/dist/tests/unit/tasks/frictionless/allowedCaptchaTypes.unit.test.js +29 -0
  208. package/dist/tests/unit/tasks/frictionless/allowedCaptchaTypes.unit.test.js.map +1 -1
  209. package/dist/tests/unit/tasks/frictionless/frictionlessTasks.unit.test.js +31 -0
  210. package/dist/tests/unit/tasks/frictionless/frictionlessTasks.unit.test.js.map +1 -1
  211. package/dist/tests/unit/tasks/frictionless/refreshSwitch.unit.test.js +1 -1
  212. package/dist/tests/unit/tasks/frictionless/refreshSwitch.unit.test.js.map +1 -1
  213. package/dist/tests/unit/tasks/frictionless/routerPuzzleOverrides.unit.test.js +28 -0
  214. package/dist/tests/unit/tasks/frictionless/routerPuzzleOverrides.unit.test.js.map +1 -1
  215. package/dist/tests/unit/tasks/iconOrder/iconOrderRenderer.unit.test.d.ts +2 -0
  216. package/dist/tests/unit/tasks/iconOrder/iconOrderRenderer.unit.test.d.ts.map +1 -0
  217. package/dist/tests/unit/tasks/iconOrder/iconOrderRenderer.unit.test.js +60 -0
  218. package/dist/tests/unit/tasks/iconOrder/iconOrderRenderer.unit.test.js.map +1 -0
  219. package/dist/tests/unit/tasks/iconOrderCaptcha/iconOrderTasks.unit.test.d.ts +2 -0
  220. package/dist/tests/unit/tasks/iconOrderCaptcha/iconOrderTasks.unit.test.d.ts.map +1 -0
  221. package/dist/tests/unit/tasks/iconOrderCaptcha/iconOrderTasks.unit.test.js +225 -0
  222. package/dist/tests/unit/tasks/iconOrderCaptcha/iconOrderTasks.unit.test.js.map +1 -0
  223. package/dist/util.js +1 -1
  224. package/package.json +19 -17
@@ -0,0 +1,423 @@
1
+ const require_usageCounters = require("../../util/usageCounters.cjs");
2
+ const require_rawTlsSignalsMiddleware = require("../../api/rawTlsSignalsMiddleware.cjs");
3
+ const require_compositeIpAddress = require("../../compositeIpAddress.cjs");
4
+ const require_util = require("../../util.cjs");
5
+ const require_clientMetaData = require("../../utils/clientMetaData.cjs");
6
+ const require_devicePlatform = require("../../utils/devicePlatform.cjs");
7
+ const require_enrichDnsEvent = require("../dnsEvent/enrichDnsEvent.cjs");
8
+ const require_captchaManager = require("../captchaManager.cjs");
9
+ const require_decisionMachineRunner = require("../decisionMachine/decisionMachineRunner.cjs");
10
+ const require_frictionlessTasksUtils = require("../frictionless/frictionlessTasksUtils.cjs");
11
+ const require_powTasksUtils = require("../powCaptcha/powTasksUtils.cjs");
12
+ const require_evaluateEmailSpamRules = require("../spam/evaluateEmailSpamRules.cjs");
13
+ let _prosopo_common = require("@prosopo/common");
14
+ let _prosopo_types = require("@prosopo/types");
15
+ let _polkadot_util = require("@polkadot/util");
16
+ let _prosopo_user_access_policy = require("@prosopo/user-access-policy");
17
+ let _prosopo_util = require("@prosopo/util");
18
+ //#region src/tasks/interactiveCaptcha/interactiveCaptchaManager.ts
19
+ /**
20
+ * Shared pipeline for the captcha types a human solves on screen (puzzle,
21
+ * icon-order). Subclasses supply the record accessors and grading; minting,
22
+ * submission bookkeeping and server verification live here.
23
+ */
24
+ var InteractiveCaptchaManager = class extends require_captchaManager.CaptchaManager {
25
+ constructor(db, pair, config, logger, usageCounters) {
26
+ super(db, pair, config, logger);
27
+ this.POW_SEPARATOR = _prosopo_types.POW_SEPARATOR;
28
+ this.decisionMachineRunner = new require_decisionMachineRunner.DecisionMachineRunner(db);
29
+ this.usageCounters = usageCounters ?? null;
30
+ }
31
+ mintChallenge(userAccount, dappAccount) {
32
+ const requestedAtTimestamp = Date.now();
33
+ const challenge = `${requestedAtTimestamp}___${userAccount}___${dappAccount}___${Math.floor(Math.random() * 1e6)}`;
34
+ return {
35
+ challenge,
36
+ providerSignature: (0, _polkadot_util.u8aToHex)(this.pair.sign((0, _polkadot_util.stringToHex)(challenge))),
37
+ requestedAtTimestamp
38
+ };
39
+ }
40
+ /**
41
+ * Grades a user's submission and records the outcome on the captcha and
42
+ * session records. Returns whether the solution was correct.
43
+ */
44
+ async submitInteractiveCaptchaSolution(submission) {
45
+ const { challenge, userTimestampSignature, behavioralData, simdReadings } = submission;
46
+ require_powTasksUtils.checkPowSignature(challenge, submission.providerChallengeSignature, this.pair.address, _prosopo_types.ApiParams.challenge);
47
+ const challengeSplit = challenge.split(this.POW_SEPARATOR);
48
+ const timestamp = Number.parseInt((0, _prosopo_util.at)(challengeSplit, 0));
49
+ const userAccount = (0, _prosopo_util.at)(challengeSplit, 1);
50
+ require_powTasksUtils.checkPowSignature(timestamp.toString(), userTimestampSignature, userAccount, _prosopo_types.ApiParams.timestamp);
51
+ const challengeRecord = await this.getRecordByChallenge(challenge);
52
+ if (!challengeRecord) {
53
+ this.logger.debug(() => ({ msg: `No record of this challenge: ${challenge}` }));
54
+ return false;
55
+ }
56
+ const { coords, saltDecodeError } = this.decodeSaltCoords(submission.salt);
57
+ if (!await this.claimSubmission(challengeRecord)) {
58
+ this.logger.debug(() => ({ msg: `Challenge already submitted: ${challenge}` }));
59
+ return false;
60
+ }
61
+ if (saltDecodeError) {
62
+ await this.recordSubmissionResult(challengeRecord, {
63
+ status: _prosopo_types.CaptchaStatus.disapproved,
64
+ reason: _prosopo_types.ResultReason.CAPTCHA_INVALID_SALT
65
+ }, userTimestampSignature, void 0);
66
+ return false;
67
+ }
68
+ if (!(0, _prosopo_util.verifyRecency)(challenge, submission.timeout)) {
69
+ await this.recordSubmissionResult(challengeRecord, {
70
+ status: _prosopo_types.CaptchaStatus.disapproved,
71
+ reason: _prosopo_types.ResultReason.CAPTCHA_INVALID_TIMESTAMP
72
+ }, userTimestampSignature, coords);
73
+ return false;
74
+ }
75
+ const correct = submission.isCorrect(challengeRecord);
76
+ const result = correct ? { status: _prosopo_types.CaptchaStatus.approved } : {
77
+ status: _prosopo_types.CaptchaStatus.disapproved,
78
+ reason: _prosopo_types.ResultReason.CAPTCHA_INVALID_SOLUTION
79
+ };
80
+ if (correct && this.usageCounters) this.usageCounters.incrManyAsync((0, _prosopo_util.at)(challengeSplit, 2), require_usageCounters.buildAllWindowIncrements("solved", this.captchaType, submission.ipAddress.address, userAccount));
81
+ await submission.persistInteraction();
82
+ const decodedPayloads = await this.decodeSubmissionPayloads(challengeRecord.sessionId, {
83
+ behavioural: behavioralData,
84
+ simd: simdReadings
85
+ });
86
+ if (behavioralData) try {
87
+ const decryptedData = decodedPayloads.behavioural;
88
+ if (decryptedData) {
89
+ this.logger?.info(() => ({
90
+ msg: "Behavioral analysis completed",
91
+ data: {
92
+ userAccount,
93
+ dappAccount: (0, _prosopo_util.at)(challengeSplit, 2),
94
+ challenge,
95
+ mouseEventsCount: decryptedData.collector1?.length || 0,
96
+ touchEventsCount: decryptedData.collector2?.length || 0,
97
+ clickEventsCount: decryptedData.collector3?.length || 0,
98
+ scrollEventsCount: decryptedData.collector4?.length || 0,
99
+ deviceCapability: decryptedData.deviceCapability,
100
+ captchaResult: correct ? "passed" : "failed"
101
+ }
102
+ }));
103
+ const packedData = {
104
+ c1: decryptedData.collector1 || [],
105
+ c2: decryptedData.collector2 || [],
106
+ c3: decryptedData.collector3 || [],
107
+ c4: decryptedData.collector4 || [],
108
+ d: decryptedData.deviceCapability
109
+ };
110
+ await this.updateRecord(challenge, {
111
+ behavioralDataPacked: packedData,
112
+ deviceCapability: decryptedData.deviceCapability
113
+ });
114
+ }
115
+ } catch (error) {
116
+ this.logger?.error(() => ({
117
+ msg: "Failed to process behavioral data",
118
+ err: error
119
+ }));
120
+ }
121
+ const storedClientMetaData = require_clientMetaData.toStoredClientMetaData(submission.clientMetaData);
122
+ if (storedClientMetaData) await this.updateRecord(challenge, { clientMetaData: storedClientMetaData });
123
+ await this.recordSubmissionResult(challengeRecord, result, userTimestampSignature, coords, storedClientMetaData);
124
+ if (challengeRecord.sessionId && decodedPayloads.simd) await this.recordSessionSimdReadingsIfAbsentWithCache(challengeRecord.sessionId, decodedPayloads.simd, _prosopo_types.SimdReadingsStage.submit);
125
+ return correct;
126
+ }
127
+ /** Invalid salt disapproves the submission rather than being ignored. */
128
+ decodeSaltCoords(salt) {
129
+ if (!salt) return {};
130
+ try {
131
+ const extractedData = (0, _prosopo_util.extractData)(salt);
132
+ if (extractedData.length < 2) return {};
133
+ const coords = [[[extractedData[0], extractedData[1]]]];
134
+ (0, _prosopo_util.assertCoordsSafe)(coords, "coords");
135
+ return { coords };
136
+ } catch (error) {
137
+ this.logger.warn(() => ({
138
+ msg: "Failed to extract coordinates from salt",
139
+ error,
140
+ salt
141
+ }));
142
+ return { saltDecodeError: error };
143
+ }
144
+ }
145
+ async recordSubmissionResult(challengeRecord, result, userSignature, coords, clientMetaData) {
146
+ await this.updateSubmissionResult(challengeRecord.challenge, result, userSignature, coords);
147
+ if (challengeRecord.sessionId) await this.updateSessionRecordWithCache(challengeRecord.sessionId, {
148
+ userSubmitted: true,
149
+ result,
150
+ ...(0, _prosopo_types.isBlockingCaptchaResult)(this.captchaType, result) && { blocked: true },
151
+ ...clientMetaData && { clientMetaData }
152
+ });
153
+ }
154
+ /**
155
+ * The post-solve gate the site's server calls: decides whether the token
156
+ * the widget was given is honoured.
157
+ *
158
+ * @param dappAccount - the dapp that is requesting the captcha
159
+ * @param challenge - the challenge string
160
+ * @param timeout - milliseconds allowed between submit and verify
161
+ * @param env - provider environment
162
+ * @param ip - optional IP address for validation
163
+ * @param userAccessRulesStorage - storage for querying user access policies
164
+ * @param email
165
+ * @param spamEmailDomainCheckingEnabled
166
+ * @param spamFilter
167
+ * @param trafficFilter
168
+ * @param storeMetadata - when true, persists the dapp-server-provided
169
+ * `email` on the captcha record for spam-rate analysis.
170
+ * @param clientSessionId - the session id the site rendered the widget
171
+ * with. When supplied, the solve must carry the same value in its
172
+ * `clientMetaData` or it is disapproved.
173
+ */
174
+ async serverVerifyInteractiveCaptchaSolution(dappAccount, challenge, timeout, env, ip, userAccessRulesStorage, email, spamEmailDomainCheckingEnabled = false, spamFilter, trafficFilter, storeMetadata = false, clientSessionId) {
175
+ const notVerifiedResponse = { verified: false };
176
+ const logger = this.logger.with({
177
+ challenge,
178
+ dappAccount
179
+ });
180
+ const challengeRecord = await this.getRecordByChallenge(challenge);
181
+ if (!challengeRecord) {
182
+ logger.debug(() => ({ msg: `No record of this challenge: ${challenge}` }));
183
+ return notVerifiedResponse;
184
+ }
185
+ notVerifiedResponse.sessionId = challengeRecord.sessionId;
186
+ if (challengeRecord.result.status !== _prosopo_types.CaptchaStatus.approved) throw new _prosopo_common.ProsopoApiError("CAPTCHA.INVALID_SOLUTION", { context: {
187
+ code: 400,
188
+ failedFuncName: this.serverVerifyInteractiveCaptchaSolution.name,
189
+ challenge
190
+ } });
191
+ if (challengeRecord.serverChecked) return notVerifiedResponse;
192
+ const challengeDappAccount = challengeRecord.dappAccount;
193
+ if (dappAccount !== challengeDappAccount) throw new _prosopo_common.ProsopoEnvError("CAPTCHA.DAPP_USER_SOLUTION_NOT_FOUND", { context: {
194
+ failedFuncName: this.serverVerifyInteractiveCaptchaSolution.name,
195
+ dappAccount,
196
+ challengeDappAccount
197
+ } });
198
+ if (!await this.markRecordChecked(challengeRecord.challenge)) return notVerifiedResponse;
199
+ const submittedAt = challengeRecord.submittedAtTimestamp;
200
+ if ((submittedAt instanceof Date ? Date.now() - submittedAt.getTime() : Number.POSITIVE_INFINITY) > timeout) {
201
+ await this.disapproveVerification(challengeRecord, _prosopo_types.ResultReason.TIMESTAMP_TOO_OLD);
202
+ return notVerifiedResponse;
203
+ }
204
+ if (require_clientMetaData.isClientSessionMismatch(clientSessionId, challengeRecord.clientMetaData?.clientSessionId)) {
205
+ logger.info(() => ({
206
+ msg: `Client session mismatch in server ${this.logLabel} verification`,
207
+ data: { hasRecordedClientSessionId: Boolean(challengeRecord.clientMetaData?.clientSessionId) }
208
+ }));
209
+ await this.disapproveVerification(challengeRecord, _prosopo_types.ResultReason.CLIENT_SESSION_MISMATCH);
210
+ return notVerifiedResponse;
211
+ }
212
+ if (userAccessRulesStorage) try {
213
+ const blockPolicy = await this.checkForHardBlock(userAccessRulesStorage, challengeRecord, challengeRecord.userAccount, challengeRecord.headers, challengeRecord.coords, challengeRecord.ipInfo?.isValid ? challengeRecord.ipInfo.countryCode : void 0, challengeRecord.ipInfo?.isValid ? challengeRecord.ipInfo.asnNumber : void 0);
214
+ if (blockPolicy) {
215
+ logger.info(() => ({
216
+ msg: `User blocked by access policy in server ${this.logLabel} verification`,
217
+ data: {
218
+ userAccount: challengeRecord.userAccount,
219
+ policy: blockPolicy
220
+ }
221
+ }));
222
+ await this.disapproveVerification(challengeRecord, _prosopo_types.ResultReason.ACCESS_POLICY_BLOCK, { matchedRule: (0, _prosopo_user_access_policy.describeMatchedRule)(blockPolicy) });
223
+ return notVerifiedResponse;
224
+ }
225
+ } catch (error) {
226
+ logger.warn(() => ({
227
+ msg: `Failed to check user access policies in server ${this.logLabel} verification`,
228
+ error
229
+ }));
230
+ }
231
+ if (email && spamEmailDomainCheckingEnabled) try {
232
+ if (await this.checkSpamEmail(email)) {
233
+ const emailDomain = email.split("@")[1] || "unknown";
234
+ logger.info(() => ({
235
+ msg: `Spam email domain detected in server ${this.logLabel} verification`,
236
+ data: { emailDomain }
237
+ }));
238
+ await this.disapproveVerification(challengeRecord, _prosopo_types.ResultReason.SPAM_EMAIL_DOMAIN, null);
239
+ return notVerifiedResponse;
240
+ }
241
+ } catch (error) {
242
+ logger.warn(() => ({
243
+ msg: `Failed to check spam email domain in server ${this.logLabel} verification`,
244
+ error
245
+ }));
246
+ }
247
+ const maxEmailSubmissionCount = spamFilter?.enabled && spamFilter.emailRules?.enabled ? spamFilter.emailRules.maxEmailSubmissionCount : void 0;
248
+ let emailNormalised;
249
+ if (maxEmailSubmissionCount !== void 0 && email && storeMetadata) {
250
+ emailNormalised = require_evaluateEmailSpamRules.normaliseEmailForMatching(email);
251
+ if (emailNormalised) try {
252
+ const priorCount = await this.db.countCommitmentsByNormalisedEmail(dappAccount, emailNormalised);
253
+ if (priorCount >= maxEmailSubmissionCount) {
254
+ logger.info(() => ({
255
+ msg: `Email submission count exceeded in server ${this.logLabel} verification`,
256
+ data: {
257
+ priorCount,
258
+ maxEmailSubmissionCount
259
+ }
260
+ }));
261
+ await this.disapproveVerification(challengeRecord, _prosopo_types.ResultReason.SPAM_EMAIL_COUNT_EXCEEDED, null);
262
+ return notVerifiedResponse;
263
+ }
264
+ } catch (error) {
265
+ logger.warn(() => ({
266
+ msg: `Failed to check email submission count in server ${this.logLabel} verification`,
267
+ error
268
+ }));
269
+ }
270
+ }
271
+ const sessionRecord = challengeRecord.sessionId ? await this.getSessionRecordWithOriginFallback(challengeRecord.sessionId) : void 0;
272
+ const enrichedDnsEvent = await require_enrichDnsEvent.enrichDnsEvent(sessionRecord?.dnsEvent, env.ipInfoService, ip ?? challengeRecord.ipInfo?.ip);
273
+ {
274
+ const check = await this.resolveTrafficFilterCheck(env, challengeRecord.ipInfo, trafficFilter, ip, enrichedDnsEvent);
275
+ if (check.isBlocked) {
276
+ logger.info(() => ({
277
+ msg: `Traffic filter rejected request in ${this.logLabel} verification`,
278
+ data: {
279
+ ip,
280
+ reason: check.reason,
281
+ dnsPeerIp: enrichedDnsEvent?.peerIp,
282
+ dnsResolverIp: enrichedDnsEvent?.resolverIp,
283
+ dnsPeerAsn: require_enrichDnsEvent.getIpInfoAsn(enrichedDnsEvent?.peerIpInfo),
284
+ dnsResolverAsn: require_enrichDnsEvent.getIpInfoAsn(enrichedDnsEvent?.resolverIpInfo),
285
+ dnsPathValid: enrichedDnsEvent?.pathValid
286
+ }
287
+ }));
288
+ await this.disapproveVerification(challengeRecord, check.reason);
289
+ return notVerifiedResponse;
290
+ }
291
+ }
292
+ if (storeMetadata && email) await this.updateRecord(challengeRecord.challenge, { metadata: {
293
+ email,
294
+ emailNormalised: emailNormalised ?? require_evaluateEmailSpamRules.normaliseEmailForMatching(email)
295
+ } });
296
+ if (ip) {
297
+ const challengeIpAddress = require_compositeIpAddress.getIpAddressFromComposite(challengeRecord.ipAddress);
298
+ const ipValidationRules = (await this.db.getClientRecord(dappAccount))?.settings?.ipValidationRules;
299
+ await this.updateRecord(challengeRecord.challenge, { providedIp: require_compositeIpAddress.getCompositeIpAddress(ip) });
300
+ if (ipValidationRules?.enabled === true) {
301
+ const ipValidation = await require_util.deepValidateIpAddress(ip, challengeIpAddress, logger, env.ipInfoService, ipValidationRules, enrichedDnsEvent?.peerIp);
302
+ if (!ipValidation.isValid) {
303
+ logger.error(() => ({
304
+ msg: `IP validation failed for ${this.logLabel} captcha`,
305
+ data: {
306
+ ip,
307
+ challengeIp: challengeIpAddress.address,
308
+ error: ipValidation.errorMessage,
309
+ distanceKm: ipValidation.distanceKm
310
+ }
311
+ }));
312
+ await this.disapproveVerification(challengeRecord, _prosopo_types.ResultReason.FAILED_IP_VALIDATION);
313
+ return notVerifiedResponse;
314
+ }
315
+ }
316
+ }
317
+ let score;
318
+ if (sessionRecord) {
319
+ const dnsAsymmetry = require_enrichDnsEvent.computeDnsAsymmetry(enrichedDnsEvent, challengeRecord.ipInfo, trafficFilter);
320
+ if (dnsAsymmetry > 0) sessionRecord.scoreComponents = {
321
+ ...sessionRecord.scoreComponents,
322
+ dnsAsymmetry
323
+ };
324
+ score = require_frictionlessTasksUtils.computeFrictionlessScore(sessionRecord?.scoreComponents);
325
+ logger.info(() => ({ data: {
326
+ scoreComponents: { ...sessionRecord?.scoreComponents || {} },
327
+ score,
328
+ dnsPeerAsn: require_enrichDnsEvent.getIpInfoAsn(enrichedDnsEvent?.peerIpInfo),
329
+ dnsResolverAsn: require_enrichDnsEvent.getIpInfoAsn(enrichedDnsEvent?.resolverIpInfo)
330
+ } }));
331
+ }
332
+ try {
333
+ const decisionInput = {
334
+ userAccount: challengeRecord.userAccount,
335
+ dappAccount: challengeRecord.dappAccount,
336
+ captchaResult: "passed",
337
+ headers: challengeRecord.headers,
338
+ captchaType: this.captchaType,
339
+ behavioralDataPacked: challengeRecord.behavioralDataPacked,
340
+ deviceCapability: challengeRecord.deviceCapability,
341
+ countryCode: challengeRecord.ipInfo?.isValid ? challengeRecord.ipInfo.countryCode : void 0,
342
+ ipInfo: challengeRecord.ipInfo,
343
+ dnsEvent: enrichedDnsEvent,
344
+ score,
345
+ threshold: sessionRecord?.threshold,
346
+ scoreComponents: sessionRecord?.scoreComponents,
347
+ decryptedHeadHash: sessionRecord?.decryptedHeadHash,
348
+ userSitekeyIpHash: sessionRecord?.userSitekeyIpHash,
349
+ simdReadings: sessionRecord?.simdReadings,
350
+ d: sessionRecord?.d,
351
+ frictionlessReason: sessionRecord?.reason,
352
+ ruleType: sessionRecord?.ruleType,
353
+ webView: sessionRecord?.webView,
354
+ iFrame: sessionRecord?.iFrame,
355
+ currentUrl: sessionRecord?.currentUrl,
356
+ iframeUrl: sessionRecord?.iframeUrl,
357
+ coords: challengeRecord.coords,
358
+ ...this.decisionMachineEventFields(challengeRecord),
359
+ ...require_rawTlsSignalsMiddleware.rawTlsSignalsFromRecord(sessionRecord),
360
+ trafficPolicies: require_devicePlatform.deriveTrafficPolicies(trafficFilter)
361
+ };
362
+ const decision = await this.decisionMachineRunner.decide(decisionInput, logger);
363
+ if (decision.decision === _prosopo_types.DecisionMachineDecision.Deny) {
364
+ logger.info(() => ({
365
+ msg: `Decision machine denied ${this.logLabel} captcha in server verification`,
366
+ data: {
367
+ userAccount: challengeRecord.userAccount,
368
+ reason: decision.reason,
369
+ score: decision.score,
370
+ tags: decision.tags
371
+ }
372
+ }));
373
+ await this.disapproveVerification(challengeRecord, decision.reason || _prosopo_types.ResultReason.CAPTCHA_DECISION_MACHINE_DENIED);
374
+ return notVerifiedResponse;
375
+ }
376
+ logger.debug(() => ({
377
+ msg: `Decision machine allowed ${this.logLabel} captcha`,
378
+ data: {
379
+ reason: decision.reason,
380
+ score: decision.score,
381
+ tags: decision.tags
382
+ }
383
+ }));
384
+ } catch (error) {
385
+ logger.error(() => ({
386
+ msg: `Failed to run decision machine in server ${this.logLabel} verification`,
387
+ err: error
388
+ }));
389
+ }
390
+ if (challengeRecord.sessionId) await this.updateSessionRecordWithCache(challengeRecord.sessionId, {
391
+ serverChecked: true,
392
+ result: { status: _prosopo_types.CaptchaStatus.approved }
393
+ });
394
+ return {
395
+ verified: true,
396
+ ...score ? { score } : {},
397
+ ...challengeRecord.sessionId && { sessionId: challengeRecord.sessionId }
398
+ };
399
+ }
400
+ /**
401
+ * Records a server-verification rejection on the captcha record and, unless
402
+ * `session` is null, on the linked session.
403
+ */
404
+ async disapproveVerification(challengeRecord, reason, session = {}) {
405
+ const result = {
406
+ status: _prosopo_types.CaptchaStatus.disapproved,
407
+ reason
408
+ };
409
+ const isBlocked = (0, _prosopo_types.isBlockingCaptchaResult)(this.captchaType, result);
410
+ await this.updateRecord(challengeRecord.challenge, {
411
+ result,
412
+ ...isBlocked && { blocked: true }
413
+ });
414
+ if (session && challengeRecord.sessionId) await this.updateSessionRecordWithCache(challengeRecord.sessionId, {
415
+ serverChecked: true,
416
+ result,
417
+ ...isBlocked && { blocked: true },
418
+ ...session
419
+ });
420
+ }
421
+ };
422
+ //#endregion
423
+ exports.InteractiveCaptchaManager = InteractiveCaptchaManager;
@@ -1,21 +1,21 @@
1
1
  const require_usageCounters = require("../../util/usageCounters.cjs");
2
- const require_decisionMachineRunner = require("../decisionMachine/decisionMachineRunner.cjs");
3
2
  const require_rawTlsSignalsMiddleware = require("../../api/rawTlsSignalsMiddleware.cjs");
4
- const require_clientMetaData = require("../../utils/clientMetaData.cjs");
5
3
  const require_compositeIpAddress = require("../../compositeIpAddress.cjs");
6
- const require_enrichDnsEvent = require("../dnsEvent/enrichDnsEvent.cjs");
7
- const require_captchaManager = require("../captchaManager.cjs");
8
- const require_routingMachine = require("../frictionless/routingMachine.cjs");
9
4
  const require_util = require("../../util.cjs");
5
+ const require_clientMetaData = require("../../utils/clientMetaData.cjs");
10
6
  const require_devicePlatform = require("../../utils/devicePlatform.cjs");
7
+ const require_enrichDnsEvent = require("../dnsEvent/enrichDnsEvent.cjs");
8
+ const require_captchaManager = require("../captchaManager.cjs");
9
+ const require_decisionMachineRunner = require("../decisionMachine/decisionMachineRunner.cjs");
11
10
  const require_frictionlessTasksUtils = require("../frictionless/frictionlessTasksUtils.cjs");
12
- const require_evaluateEmailSpamRules = require("../spam/evaluateEmailSpamRules.cjs");
13
11
  const require_powTasksUtils = require("./powTasksUtils.cjs");
12
+ const require_evaluateEmailSpamRules = require("../spam/evaluateEmailSpamRules.cjs");
13
+ const require_routingMachine = require("../frictionless/routingMachine.cjs");
14
14
  let _prosopo_common = require("@prosopo/common");
15
15
  let _prosopo_types = require("@prosopo/types");
16
- let _prosopo_util = require("@prosopo/util");
17
- let _prosopo_user_access_policy = require("@prosopo/user-access-policy");
18
16
  let _polkadot_util = require("@polkadot/util");
17
+ let _prosopo_user_access_policy = require("@prosopo/user-access-policy");
18
+ let _prosopo_util = require("@prosopo/util");
19
19
  //#region src/tasks/powCaptcha/powTasks.ts
20
20
  var DEFAULT_POW_DIFFICULTY = 4;
21
21
  var PowCaptchaManager = class extends require_captchaManager.CaptchaManager {
@@ -1,7 +1,7 @@
1
1
  let _prosopo_common = require("@prosopo/common");
2
+ let _polkadot_util = require("@polkadot/util");
2
3
  let _prosopo_util = require("@prosopo/util");
3
4
  let _prosopo_util_crypto = require("@prosopo/util-crypto");
4
- let _polkadot_util = require("@polkadot/util");
5
5
  let _noble_hashes_sha2_js = require("@noble/hashes/sha2.js");
6
6
  //#region src/tasks/powCaptcha/powTasksUtils.ts
7
7
  var validateSolution = (nonce, challenge, difficulty) => (0, _prosopo_util.hashMeetsDifficulty)((0, _noble_hashes_sha2_js.sha256)(new TextEncoder().encode(nonce + challenge)), difficulty);