@prosopo/provider 4.15.13 → 4.15.15

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 (236) hide show
  1. package/.turbo/turbo-build$colon$cjs.log +106 -106
  2. package/.turbo/turbo-build$colon$tsc.log +24 -24
  3. package/.turbo/turbo-build.log +107 -104
  4. package/CHANGELOG.md +78 -0
  5. package/dist/_virtual/_rolldown/runtime.js +3 -0
  6. package/dist/api/admin/apiAdminRoutesProvider.js +27 -46
  7. package/dist/api/admin/apiClearAllCountersEndpoint.js +62 -57
  8. package/dist/api/admin/apiDnsEventEndpoint.js +79 -92
  9. package/dist/api/admin/apiGetAllDecisionMachinesEndpoint.js +43 -45
  10. package/dist/api/admin/apiGetDecisionMachineEndpoint.js +40 -40
  11. package/dist/api/admin/apiRegisterSiteKeyEndpoint.js +23 -22
  12. package/dist/api/admin/apiRegisterSiteKeysEndpoint.js +26 -30
  13. package/dist/api/admin/apiRemoveAllDecisionMachinesEndpoint.js +35 -37
  14. package/dist/api/admin/apiRemoveDecisionMachineEndpoint.js +40 -40
  15. package/dist/api/admin/apiRemoveDetectorKeyEndpoint.d.ts.map +1 -1
  16. package/dist/api/admin/apiRemoveDetectorKeyEndpoint.js +29 -31
  17. package/dist/api/admin/apiRemoveSiteKeyEndpoint.js +33 -29
  18. package/dist/api/admin/apiRemoveSiteKeysEndpoint.js +32 -31
  19. package/dist/api/admin/apiToggleMaintenanceModeEndpoint.js +43 -35
  20. package/dist/api/admin/apiUpdateDecisionMachineEndpoint.js +43 -63
  21. package/dist/api/admin/apiUpdateDetectorKeyEndpoint.js +36 -35
  22. package/dist/api/admin/createApiAdminRoutesProvider.js +6 -8
  23. package/dist/api/blacklistRequestInspector.js +285 -377
  24. package/dist/api/block.js +17 -30
  25. package/dist/api/captcha/checkSpamEmail.js +88 -98
  26. package/dist/api/captcha/contextAwareValidation.js +21 -15
  27. package/dist/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.js +126 -140
  28. package/dist/api/captcha/getFrictionlessCaptchaChallenge/constants.js +11 -12
  29. package/dist/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.js +236 -299
  30. package/dist/api/captcha/getFrictionlessCaptchaChallenge/decryptSimdReadings.js +10 -13
  31. package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js +336 -462
  32. package/dist/api/captcha/getFrictionlessCaptchaChallenge/honeypotResponse.js +17 -14
  33. package/dist/api/captcha/getFrictionlessCaptchaChallenge/sessionDedup.js +27 -27
  34. package/dist/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.js +42 -63
  35. package/dist/api/captcha/getFrictionlessCaptchaChallenge.js +2 -4
  36. package/dist/api/captcha/getImageCaptchaChallenge.js +136 -207
  37. package/dist/api/captcha/getPoWCaptchaChallenge.js +128 -198
  38. package/dist/api/captcha/getPuzzleCaptchaChallenge.js +132 -201
  39. package/dist/api/captcha/maintenanceModeResponses.js +38 -47
  40. package/dist/api/captcha/submitImageCaptchaSolution.js +75 -101
  41. package/dist/api/captcha/submitPoWCaptchaSolution.js +137 -207
  42. package/dist/api/captcha/submitPuzzleCaptchaSolution.js +73 -105
  43. package/dist/api/captcha.js +76 -58
  44. package/dist/api/dnsEventUrl.js +20 -22
  45. package/dist/api/domainMiddleware.js +104 -115
  46. package/dist/api/forwardVerify.js +62 -48
  47. package/dist/api/handshakeTimingMiddleware.js +43 -62
  48. package/dist/api/hardBlockVerdictCache.js +114 -117
  49. package/dist/api/headerCheckMiddleware.js +34 -34
  50. package/dist/api/ignoreMiddleware.js +13 -15
  51. package/dist/api/ipInfoMiddleware.js +17 -19
  52. package/dist/api/ja4.js +149 -188
  53. package/dist/api/ja4Middleware.js +39 -54
  54. package/dist/api/metrics.js +245 -257
  55. package/dist/api/public.js +52 -54
  56. package/dist/api/robotsMiddleware.js +10 -10
  57. package/dist/api/startProviderApi.js +205 -232
  58. package/dist/api/testSiteKey.js +16 -16
  59. package/dist/api/validateAddress.js +23 -21
  60. package/dist/api/verify.js +334 -377
  61. package/dist/cjs/_virtual/_rolldown/runtime.cjs +23 -0
  62. package/dist/cjs/api/admin/apiAdminRoutesProvider.cjs +40 -59
  63. package/dist/cjs/api/admin/apiClearAllCountersEndpoint.cjs +63 -58
  64. package/dist/cjs/api/admin/apiDnsEventEndpoint.cjs +82 -93
  65. package/dist/cjs/api/admin/apiGetAllDecisionMachinesEndpoint.cjs +46 -48
  66. package/dist/cjs/api/admin/apiGetDecisionMachineEndpoint.cjs +41 -41
  67. package/dist/cjs/api/admin/apiRegisterSiteKeyEndpoint.cjs +25 -24
  68. package/dist/cjs/api/admin/apiRegisterSiteKeysEndpoint.cjs +28 -32
  69. package/dist/cjs/api/admin/apiRemoveAllDecisionMachinesEndpoint.cjs +38 -40
  70. package/dist/cjs/api/admin/apiRemoveDecisionMachineEndpoint.cjs +41 -41
  71. package/dist/cjs/api/admin/apiRemoveDetectorKeyEndpoint.cjs +31 -33
  72. package/dist/cjs/api/admin/apiRemoveSiteKeyEndpoint.cjs +35 -31
  73. package/dist/cjs/api/admin/apiRemoveSiteKeysEndpoint.cjs +34 -33
  74. package/dist/cjs/api/admin/apiToggleMaintenanceModeEndpoint.cjs +45 -35
  75. package/dist/cjs/api/admin/apiUpdateDecisionMachineEndpoint.cjs +45 -65
  76. package/dist/cjs/api/admin/apiUpdateDetectorKeyEndpoint.cjs +38 -37
  77. package/dist/cjs/api/admin/createApiAdminRoutesProvider.cjs +7 -8
  78. package/dist/cjs/api/blacklistRequestInspector.cjs +288 -376
  79. package/dist/cjs/api/block.cjs +18 -30
  80. package/dist/cjs/api/captcha/checkSpamEmail.cjs +90 -100
  81. package/dist/cjs/api/captcha/contextAwareValidation.cjs +21 -14
  82. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.cjs +125 -140
  83. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/constants.cjs +10 -10
  84. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.cjs +238 -301
  85. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/decryptSimdReadings.cjs +9 -12
  86. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/handler.cjs +349 -473
  87. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/honeypotResponse.cjs +18 -14
  88. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/sessionDedup.cjs +26 -26
  89. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.cjs +42 -63
  90. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge.cjs +2 -3
  91. package/dist/cjs/api/captcha/getImageCaptchaChallenge.cjs +138 -208
  92. package/dist/cjs/api/captcha/getPoWCaptchaChallenge.cjs +131 -200
  93. package/dist/cjs/api/captcha/getPuzzleCaptchaChallenge.cjs +135 -203
  94. package/dist/cjs/api/captcha/maintenanceModeResponses.cjs +38 -44
  95. package/dist/cjs/api/captcha/submitImageCaptchaSolution.cjs +77 -102
  96. package/dist/cjs/api/captcha/submitPoWCaptchaSolution.cjs +140 -209
  97. package/dist/cjs/api/captcha/submitPuzzleCaptchaSolution.cjs +76 -107
  98. package/dist/cjs/api/captcha.cjs +79 -58
  99. package/dist/cjs/api/dnsEventUrl.cjs +20 -21
  100. package/dist/cjs/api/domainMiddleware.cjs +106 -117
  101. package/dist/cjs/api/forwardVerify.cjs +63 -49
  102. package/dist/cjs/api/handshakeTimingMiddleware.cjs +43 -61
  103. package/dist/cjs/api/hardBlockVerdictCache.cjs +114 -114
  104. package/dist/cjs/api/headerCheckMiddleware.cjs +34 -34
  105. package/dist/cjs/api/ignoreMiddleware.cjs +12 -14
  106. package/dist/cjs/api/ipInfoMiddleware.cjs +16 -18
  107. package/dist/cjs/api/ja4.cjs +149 -187
  108. package/dist/cjs/api/ja4Middleware.cjs +40 -53
  109. package/dist/cjs/api/metrics.cjs +229 -228
  110. package/dist/cjs/api/public.cjs +57 -57
  111. package/dist/cjs/api/robotsMiddleware.cjs +9 -9
  112. package/dist/cjs/api/startProviderApi.cjs +219 -237
  113. package/dist/cjs/api/testSiteKey.cjs +15 -14
  114. package/dist/cjs/api/validateAddress.cjs +24 -21
  115. package/dist/cjs/api/verify.cjs +338 -379
  116. package/dist/cjs/compositeIpAddress.cjs +40 -47
  117. package/dist/cjs/index.cjs +65 -71
  118. package/dist/cjs/pairs.cjs +31 -35
  119. package/dist/cjs/rules/lang.cjs +10 -14
  120. package/dist/cjs/schedulers/captchaScheduler.cjs +26 -32
  121. package/dist/cjs/schedulers/getClientList.cjs +35 -33
  122. package/dist/cjs/schedulers/setClientEntropy.cjs +35 -33
  123. package/dist/cjs/schedulers/updateSpamEmailDomains.cjs +39 -43
  124. package/dist/cjs/services/ipComparison.cjs +122 -119
  125. package/dist/cjs/tasks/captchaManager.cjs +407 -486
  126. package/dist/cjs/tasks/client/clientTasks.cjs +347 -470
  127. package/dist/cjs/tasks/dataset/datasetTasks.cjs +25 -29
  128. package/dist/cjs/tasks/dataset/datasetTasksUtils.cjs +16 -32
  129. package/dist/cjs/tasks/decisionMachine/decisionMachineRunner.cjs +220 -307
  130. package/dist/cjs/tasks/detection/decodeBehavior.cjs +408 -317
  131. package/dist/cjs/tasks/detection/decodePayload.cjs +686 -468
  132. package/dist/cjs/tasks/detection/decodeSimd.cjs +462 -304
  133. package/dist/cjs/tasks/detection/getBotScore.cjs +37 -43
  134. package/dist/cjs/tasks/dnsEvent/enrichDnsEvent.cjs +88 -71
  135. package/dist/cjs/tasks/frictionless/frictionlessTasks.cjs +348 -486
  136. package/dist/cjs/tasks/frictionless/frictionlessTasksUtils.cjs +13 -30
  137. package/dist/cjs/tasks/frictionless/routingMachine.cjs +71 -55
  138. package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasks.cjs +555 -806
  139. package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasksUtils.cjs +22 -23
  140. package/dist/cjs/tasks/index.cjs +2 -4
  141. package/dist/cjs/tasks/powCaptcha/powTasks.cjs +491 -687
  142. package/dist/cjs/tasks/powCaptcha/powTasksUtils.cjs +17 -29
  143. package/dist/cjs/tasks/puzzleCaptcha/puzzleTasks.cjs +422 -591
  144. package/dist/cjs/tasks/puzzleCaptcha/puzzleTasksUtils.cjs +8 -5
  145. package/dist/cjs/tasks/spam/checkSpamEmail.cjs +133 -141
  146. package/dist/cjs/tasks/spam/checkTrafficFilter.cjs +118 -76
  147. package/dist/cjs/tasks/spam/evaluateEmailSpamRules.cjs +75 -84
  148. package/dist/cjs/tasks/spam/updateSpamEmailDomains.cjs +40 -56
  149. package/dist/cjs/tasks/tasks.cjs +107 -158
  150. package/dist/cjs/util/usageCounters.cjs +220 -211
  151. package/dist/cjs/util.cjs +329 -359
  152. package/dist/cjs/utils/devicePlatform.cjs +14 -8
  153. package/dist/cjs/utils/dns.cjs +68 -93
  154. package/dist/cjs/utils/hashUserAgent.cjs +14 -7
  155. package/dist/cjs/utils/hashUserIp.cjs +14 -6
  156. package/dist/cjs/utils/honeypot/encoders.cjs +74 -80
  157. package/dist/cjs/utils/honeypot/phraseBank.cjs +51 -44
  158. package/dist/cjs/utils/normalizeRequestIp.cjs +33 -25
  159. package/dist/compositeIpAddress.js +38 -46
  160. package/dist/index.js +12 -56
  161. package/dist/pairs.js +31 -37
  162. package/dist/rules/lang.js +11 -15
  163. package/dist/schedulers/captchaScheduler.js +25 -31
  164. package/dist/schedulers/getClientList.js +34 -32
  165. package/dist/schedulers/setClientEntropy.js +34 -32
  166. package/dist/schedulers/updateSpamEmailDomains.js +38 -42
  167. package/dist/services/ipComparison.js +122 -119
  168. package/dist/tasks/captchaManager.js +403 -483
  169. package/dist/tasks/client/clientTasks.js +343 -467
  170. package/dist/tasks/dataset/datasetTasks.js +24 -28
  171. package/dist/tasks/dataset/datasetTasksUtils.js +15 -31
  172. package/dist/tasks/decisionMachine/decisionMachineRunner.js +217 -306
  173. package/dist/tasks/detection/getBotScore.js +37 -43
  174. package/dist/tasks/dnsEvent/enrichDnsEvent.d.ts.map +1 -1
  175. package/dist/tasks/dnsEvent/enrichDnsEvent.js +88 -74
  176. package/dist/tasks/dnsEvent/enrichDnsEvent.js.map +1 -1
  177. package/dist/tasks/frictionless/frictionlessTasks.js +342 -479
  178. package/dist/tasks/frictionless/frictionlessTasksUtils.js +14 -32
  179. package/dist/tasks/frictionless/routingMachine.js +72 -56
  180. package/dist/tasks/imgCaptcha/imgCaptchaTasks.js +550 -801
  181. package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.js +21 -22
  182. package/dist/tasks/index.js +1 -3
  183. package/dist/tasks/powCaptcha/powTasks.d.ts.map +1 -1
  184. package/dist/tasks/powCaptcha/powTasks.js +486 -682
  185. package/dist/tasks/powCaptcha/powTasks.js.map +1 -1
  186. package/dist/tasks/powCaptcha/powTasksUtils.js +15 -28
  187. package/dist/tasks/puzzleCaptcha/puzzleTasks.d.ts.map +1 -1
  188. package/dist/tasks/puzzleCaptcha/puzzleTasks.js +417 -586
  189. package/dist/tasks/puzzleCaptcha/puzzleTasks.js.map +1 -1
  190. package/dist/tasks/puzzleCaptcha/puzzleTasksUtils.js +9 -6
  191. package/dist/tasks/spam/checkSpamEmail.js +133 -141
  192. package/dist/tasks/spam/checkTrafficFilter.d.ts +1 -0
  193. package/dist/tasks/spam/checkTrafficFilter.d.ts.map +1 -1
  194. package/dist/tasks/spam/checkTrafficFilter.js +117 -77
  195. package/dist/tasks/spam/checkTrafficFilter.js.map +1 -1
  196. package/dist/tasks/spam/evaluateEmailSpamRules.js +75 -86
  197. package/dist/tasks/spam/updateSpamEmailDomains.js +36 -54
  198. package/dist/tasks/tasks.js +98 -150
  199. package/dist/tests/integration/api/blacklistRequestInspector.integration.test.js +4 -1
  200. package/dist/tests/integration/api/blacklistRequestInspector.integration.test.js.map +1 -1
  201. package/dist/tests/unit/api/block.unit.test.js +8 -4
  202. package/dist/tests/unit/api/block.unit.test.js.map +1 -1
  203. package/dist/tests/unit/api/captcha/maintenanceModeShortCircuit.unit.test.js +8 -6
  204. package/dist/tests/unit/api/captcha/maintenanceModeShortCircuit.unit.test.js.map +1 -1
  205. package/dist/tests/unit/api/forwardVerify.unit.test.js +3 -1
  206. package/dist/tests/unit/api/forwardVerify.unit.test.js.map +1 -1
  207. package/dist/tests/unit/api/getFrictionlessCaptchaChallenge.unit.test.js +15 -7
  208. package/dist/tests/unit/api/getFrictionlessCaptchaChallenge.unit.test.js.map +1 -1
  209. package/dist/tests/unit/schedulers/captchaScheduler.unit.test.js +24 -18
  210. package/dist/tests/unit/schedulers/captchaScheduler.unit.test.js.map +1 -1
  211. package/dist/tests/unit/schedulers/getClientList.unit.test.js +19 -13
  212. package/dist/tests/unit/schedulers/getClientList.unit.test.js.map +1 -1
  213. package/dist/tests/unit/schedulers/setClientEntropy.unit.test.js +19 -13
  214. package/dist/tests/unit/schedulers/setClientEntropy.unit.test.js.map +1 -1
  215. package/dist/tests/unit/tasks/client/clientTasks.unit.test.js.map +1 -1
  216. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.unit.test.js +22 -16
  217. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.unit.test.js.map +1 -1
  218. package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.js +6 -0
  219. package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.js.map +1 -1
  220. package/dist/tests/unit/tasks/puzzleCaptcha/puzzleTasks.unit.test.js +60 -1
  221. package/dist/tests/unit/tasks/puzzleCaptcha/puzzleTasks.unit.test.js.map +1 -1
  222. package/dist/tests/unit/tasks/spam/checkTrafficFilter.unit.test.js +140 -0
  223. package/dist/tests/unit/tasks/spam/checkTrafficFilter.unit.test.js.map +1 -1
  224. package/dist/tests/unit/testUtils/mockProviderEnv.d.ts +7 -7
  225. package/dist/util/usageCounters.js +220 -212
  226. package/dist/util.d.ts.map +1 -1
  227. package/dist/util.js +327 -364
  228. package/dist/util.js.map +1 -1
  229. package/dist/utils/devicePlatform.js +15 -9
  230. package/dist/utils/dns.js +64 -77
  231. package/dist/utils/hashUserAgent.js +14 -7
  232. package/dist/utils/hashUserIp.js +14 -6
  233. package/dist/utils/honeypot/encoders.js +74 -80
  234. package/dist/utils/honeypot/phraseBank.js +49 -44
  235. package/dist/utils/normalizeRequestIp.js +34 -26
  236. package/package.json +24 -24
@@ -1,212 +1,142 @@
1
- import { ProsopoApiError } from "@prosopo/common";
2
- import { SubmitPowCaptchaSolutionBody, CaptchaType } from "@prosopo/types";
3
- import { flatten, getIPAddress } from "@prosopo/util";
1
+ import { validateAddr, validateSiteKey } from "../validateAddress.js";
4
2
  import { Tasks } from "../../tasks/tasks.js";
5
- import { derivePlatform } from "../../utils/devicePlatform.js";
6
3
  import { getMaintenanceMode } from "../admin/apiToggleMaintenanceModeEndpoint.js";
4
+ import { derivePlatform } from "../../utils/devicePlatform.js";
7
5
  import { resolveTestSiteKeyVerdict } from "../testSiteKey.js";
8
- import { validateSiteKey, validateAddr } from "../validateAddress.js";
9
- const submitPoWCaptchaSolution = (env) => async (req, res, next) => {
10
- if (getMaintenanceMode()) {
11
- req.logger.info(() => ({
12
- msg: "Maintenance mode active - returning verified"
13
- }));
14
- const response = {
15
- status: "ok",
16
- verified: true
17
- };
18
- return res.json(response);
19
- }
20
- let parsed;
21
- const tasks = new Tasks(env, req.logger);
22
- try {
23
- parsed = SubmitPowCaptchaSolutionBody.parse(req.body);
24
- } catch (err) {
25
- return next(
26
- new ProsopoApiError("CAPTCHA.PARSE_ERROR", {
27
- context: { code: 400, error: err, body: req.body },
28
- i18n: req.i18n,
29
- logger: req.logger
30
- })
31
- );
32
- }
33
- const {
34
- challenge,
35
- signature,
36
- nonce,
37
- dapp,
38
- user,
39
- behavioralData,
40
- salt,
41
- simdReadings,
42
- clientMetaData,
43
- fingerprintProof
44
- } = parsed;
45
- validateSiteKey(dapp);
46
- validateAddr(user);
47
- const testVerdict = resolveTestSiteKeyVerdict(dapp, req.logger);
48
- if (testVerdict !== null) {
49
- const response = {
50
- status: "ok",
51
- verified: testVerdict
52
- };
53
- return res.json(response);
54
- }
55
- try {
56
- const clientRecord = await tasks.db.getClientRecord(dapp);
57
- if (!clientRecord) {
58
- return next(
59
- new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
60
- context: { code: 400, siteKey: dapp },
61
- i18n: req.i18n,
62
- logger: req.logger
63
- })
64
- );
65
- }
66
- const flatHeaders = flatten(req.headers);
67
- const userAgent = req.headers["user-agent"] ?? "";
68
- const countryCode = req.ipInfo && "isValid" in req.ipInfo && req.ipInfo.isValid ? req.ipInfo.countryCode : void 0;
69
- req.logger.info(() => ({
70
- msg: fingerprintProof ? "fingerprintProof detected" : "no fingerprintProof on PoW submission",
71
- data: {
72
- fingerprintProofPresent: fingerprintProof !== void 0,
73
- fingerprintProofBytes: fingerprintProof?.length ?? 0,
74
- challenge
75
- }
76
- }));
77
- tasks.powCaptchaManager.setPostPowContext({
78
- ip: req.ip || "",
79
- countryCode,
80
- // `false` here is a placeholder — `runPostPowRouting` overrides
81
- // isWebView with the value recorded on the originating session.
82
- platform: derivePlatform(userAgent, false),
83
- raw: {
84
- headers: flatHeaders,
85
- userAgent,
86
- ...req.ja4 && { ja4: req.ja4 },
87
- ...fingerprintProof && { fingerprintProof },
88
- ...req.tcpToChelloUs !== void 0 && {
89
- tcpToChelloUs: req.tcpToChelloUs
90
- },
91
- ...req.chelloToHandshakeUs !== void 0 && {
92
- chelloToHandshakeUs: req.chelloToHandshakeUs
93
- }
94
- }
95
- });
96
- const persistedSolutionTimeout = clientRecord.settings.solutionTimeout;
97
- const submitWindowMs = persistedSolutionTimeout ?? clientRecord.settings.verifiedTimeout;
98
- const result = await tasks.powCaptchaManager.verifyPowCaptchaSolution(
99
- challenge,
100
- signature.provider.challenge,
101
- nonce,
102
- submitWindowMs,
103
- signature.user.timestamp,
104
- getIPAddress(req.ip || ""),
105
- flatHeaders,
106
- behavioralData,
107
- salt,
108
- simdReadings,
109
- clientMetaData
110
- );
111
- if (result.routingOutput) {
112
- req.logger.info(() => ({
113
- msg: "post-PoW routing decision",
114
- data: {
115
- routedCaptchaType: result.routingOutput?.captchaType,
116
- routingReason: result.routingOutput?.reason,
117
- challenge
118
- }
119
- }));
120
- }
121
- const escalation = await buildEscalation(tasks, result, challenge, {
122
- tcpToChelloUs: req.tcpToChelloUs,
123
- chelloToHandshakeUs: req.chelloToHandshakeUs
124
- });
125
- const response = {
126
- status: "ok",
127
- // On escalation the user is not done — they still need to clear
128
- // the follow-up image/puzzle challenge before we hand them a token.
129
- verified: escalation ? false : result.verified,
130
- ...escalation && { escalation }
131
- };
132
- return res.json(response);
133
- } catch (err) {
134
- req.logger.error(() => ({
135
- err,
136
- body: req.body,
137
- msg: "Error in PoW captcha solution submission"
138
- }));
139
- return next(
140
- new ProsopoApiError("API.BAD_REQUEST", {
141
- context: {
142
- code: 500,
143
- siteKey: req.body.dapp,
144
- error: err
145
- },
146
- i18n: req.i18n,
147
- logger: req.logger
148
- })
149
- );
150
- }
151
- };
152
- const buildEscalation = async (tasks, result, challenge, handshakeTiming) => {
153
- if (!result.verified || !result.routingOutput) return void 0;
154
- const routedType = result.routingOutput.captchaType;
155
- if (routedType !== CaptchaType.image && routedType !== CaptchaType.puzzle) {
156
- return void 0;
157
- }
158
- const powRecord = await tasks.db.getPowCaptchaRecordByChallenge(challenge);
159
- if (!powRecord?.sessionId) return void 0;
160
- const originSession = await tasks.db.getSessionRecordBySessionId(
161
- powRecord.sessionId
162
- );
163
- if (!originSession) return void 0;
164
- const routed = result.routingOutput;
165
- const selectionReason = routed.reason ?? originSession.reason;
166
- const newSession = await tasks.frictionlessManager.createSession(
167
- originSession.token,
168
- originSession.score,
169
- originSession.threshold,
170
- originSession.scoreComponents,
171
- originSession.ipAddress,
172
- routed.captchaType,
173
- originSession.siteKey ?? powRecord.dappAccount,
174
- routed.captchaType === CaptchaType.image ? routed.solvedImagesCount ?? originSession.solvedImagesCount : void 0,
175
- void 0,
176
- originSession.userSitekeyIpHash,
177
- originSession.webView,
178
- originSession.iFrame,
179
- originSession.decryptedHeadHash,
180
- selectionReason,
181
- void 0,
182
- void 0,
183
- originSession.ipInfo,
184
- originSession.headers,
185
- originSession.mode,
186
- originSession.simdReadings,
187
- originSession.entropyMathRandomFingerprint,
188
- originSession.entropyCryptoFingerprint,
189
- originSession.entropyWallClockOffsetMs,
190
- originSession.entropyMathRandomFirst,
191
- originSession.currentUrl,
192
- handshakeTiming?.tcpToChelloUs,
193
- handshakeTiming?.chelloToHandshakeUs,
194
- true,
195
- originSession.iframeUrl,
196
- originSession.isProtect
197
- );
198
- if (tasks.writeQueue) {
199
- await tasks.writeQueue.cacheSessionEscalation(
200
- powRecord.sessionId,
201
- newSession.sessionId
202
- );
203
- }
204
- return {
205
- captchaType: routed.captchaType,
206
- sessionId: newSession.sessionId
207
- };
6
+ import { ProsopoApiError } from "@prosopo/common";
7
+ import { CaptchaType, SubmitPowCaptchaSolutionBody } from "@prosopo/types";
8
+ import { flatten, getIPAddress } from "@prosopo/util";
9
+ //#region src/api/captcha/submitPoWCaptchaSolution.ts
10
+ var submitPoWCaptchaSolution_default = (env) => async (req, res, next) => {
11
+ if (getMaintenanceMode()) {
12
+ req.logger.info(() => ({ msg: "Maintenance mode active - returning verified" }));
13
+ return res.json({
14
+ status: "ok",
15
+ verified: true
16
+ });
17
+ }
18
+ let parsed;
19
+ const tasks = new Tasks(env, req.logger);
20
+ try {
21
+ parsed = SubmitPowCaptchaSolutionBody.parse(req.body);
22
+ } catch (err) {
23
+ return next(new ProsopoApiError("CAPTCHA.PARSE_ERROR", {
24
+ context: {
25
+ code: 400,
26
+ error: err,
27
+ body: req.body
28
+ },
29
+ i18n: req.i18n,
30
+ logger: req.logger
31
+ }));
32
+ }
33
+ const { challenge, signature, nonce, dapp, user, behavioralData, salt, simdReadings, clientMetaData, fingerprintProof } = parsed;
34
+ validateSiteKey(dapp);
35
+ validateAddr(user);
36
+ const testVerdict = resolveTestSiteKeyVerdict(dapp, req.logger);
37
+ if (testVerdict !== null) {
38
+ const response = {
39
+ status: "ok",
40
+ verified: testVerdict
41
+ };
42
+ return res.json(response);
43
+ }
44
+ try {
45
+ const clientRecord = await tasks.db.getClientRecord(dapp);
46
+ if (!clientRecord) return next(new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
47
+ context: {
48
+ code: 400,
49
+ siteKey: dapp
50
+ },
51
+ i18n: req.i18n,
52
+ logger: req.logger
53
+ }));
54
+ const flatHeaders = flatten(req.headers);
55
+ const userAgent = req.headers["user-agent"] ?? "";
56
+ const countryCode = req.ipInfo && "isValid" in req.ipInfo && req.ipInfo.isValid ? req.ipInfo.countryCode : void 0;
57
+ req.logger.info(() => ({
58
+ msg: fingerprintProof ? "fingerprintProof detected" : "no fingerprintProof on PoW submission",
59
+ data: {
60
+ fingerprintProofPresent: fingerprintProof !== void 0,
61
+ fingerprintProofBytes: fingerprintProof?.length ?? 0,
62
+ challenge
63
+ }
64
+ }));
65
+ tasks.powCaptchaManager.setPostPowContext({
66
+ ip: req.ip || "",
67
+ countryCode,
68
+ platform: derivePlatform(userAgent, false),
69
+ raw: {
70
+ headers: flatHeaders,
71
+ userAgent,
72
+ ...req.ja4 && { ja4: req.ja4 },
73
+ ...fingerprintProof && { fingerprintProof },
74
+ ...req.tcpToChelloUs !== void 0 && { tcpToChelloUs: req.tcpToChelloUs },
75
+ ...req.chelloToHandshakeUs !== void 0 && { chelloToHandshakeUs: req.chelloToHandshakeUs }
76
+ }
77
+ });
78
+ const submitWindowMs = clientRecord.settings.solutionTimeout ?? clientRecord.settings.verifiedTimeout;
79
+ const result = await tasks.powCaptchaManager.verifyPowCaptchaSolution(challenge, signature.provider.challenge, nonce, submitWindowMs, signature.user.timestamp, getIPAddress(req.ip || ""), flatHeaders, behavioralData, salt, simdReadings, clientMetaData);
80
+ if (result.routingOutput) req.logger.info(() => ({
81
+ msg: "post-PoW routing decision",
82
+ data: {
83
+ routedCaptchaType: result.routingOutput?.captchaType,
84
+ routingReason: result.routingOutput?.reason,
85
+ challenge
86
+ }
87
+ }));
88
+ const escalation = await buildEscalation(tasks, result, challenge, {
89
+ tcpToChelloUs: req.tcpToChelloUs,
90
+ chelloToHandshakeUs: req.chelloToHandshakeUs
91
+ });
92
+ const response = {
93
+ status: "ok",
94
+ verified: escalation ? false : result.verified,
95
+ ...escalation && { escalation }
96
+ };
97
+ return res.json(response);
98
+ } catch (err) {
99
+ req.logger.error(() => ({
100
+ err,
101
+ body: req.body,
102
+ msg: "Error in PoW captcha solution submission"
103
+ }));
104
+ return next(new ProsopoApiError("API.BAD_REQUEST", {
105
+ context: {
106
+ code: 500,
107
+ siteKey: req.body.dapp,
108
+ error: err
109
+ },
110
+ i18n: req.i18n,
111
+ logger: req.logger
112
+ }));
113
+ }
208
114
  };
209
- export {
210
- buildEscalation,
211
- submitPoWCaptchaSolution as default
115
+ /**
116
+ * Translate the routing-machine output into an escalation by minting a fresh
117
+ * session of the chosen captcha type, carrying forward the originating
118
+ * session's risk profile (score, headers, IP, etc.). Returns undefined when
119
+ * the router decided to keep the user on PoW (i.e. no escalation needed).
120
+ *
121
+ * Exported for unit testing only — the handler above is the production entry
122
+ * point.
123
+ */
124
+ var buildEscalation = async (tasks, result, challenge, handshakeTiming) => {
125
+ if (!result.verified || !result.routingOutput) return void 0;
126
+ const routedType = result.routingOutput.captchaType;
127
+ if (routedType !== CaptchaType.image && routedType !== CaptchaType.puzzle) return;
128
+ const powRecord = await tasks.db.getPowCaptchaRecordByChallenge(challenge);
129
+ if (!powRecord?.sessionId) return void 0;
130
+ const originSession = await tasks.db.getSessionRecordBySessionId(powRecord.sessionId);
131
+ if (!originSession) return void 0;
132
+ const routed = result.routingOutput;
133
+ const selectionReason = routed.reason ?? originSession.reason;
134
+ const newSession = await tasks.frictionlessManager.createSession(originSession.token, originSession.score, originSession.threshold, originSession.scoreComponents, originSession.ipAddress, routed.captchaType, originSession.siteKey ?? powRecord.dappAccount, routed.captchaType === CaptchaType.image ? routed.solvedImagesCount ?? originSession.solvedImagesCount : void 0, void 0, originSession.userSitekeyIpHash, originSession.webView, originSession.iFrame, originSession.decryptedHeadHash, selectionReason, void 0, void 0, originSession.ipInfo, originSession.headers, originSession.mode, originSession.simdReadings, originSession.entropyMathRandomFingerprint, originSession.entropyCryptoFingerprint, originSession.entropyWallClockOffsetMs, originSession.entropyMathRandomFirst, originSession.currentUrl, handshakeTiming?.tcpToChelloUs, handshakeTiming?.chelloToHandshakeUs, true, originSession.iframeUrl, originSession.isProtect);
135
+ if (tasks.writeQueue) await tasks.writeQueue.cacheSessionEscalation(powRecord.sessionId, newSession.sessionId);
136
+ return {
137
+ captchaType: routed.captchaType,
138
+ sessionId: newSession.sessionId
139
+ };
212
140
  };
141
+ //#endregion
142
+ export { buildEscalation, submitPoWCaptchaSolution_default as default };
@@ -1,109 +1,77 @@
1
- import { ProsopoApiError } from "@prosopo/common";
2
- import { SubmitPuzzleCaptchaSolutionBody } from "@prosopo/types";
3
- import { getIPAddress, flatten } from "@prosopo/util";
1
+ import { validateAddr, validateSiteKey } from "../validateAddress.js";
4
2
  import { Tasks } from "../../tasks/tasks.js";
5
3
  import { getMaintenanceMode } from "../admin/apiToggleMaintenanceModeEndpoint.js";
6
4
  import { resolveTestSiteKeyVerdict } from "../testSiteKey.js";
7
- import { validateSiteKey, validateAddr } from "../validateAddress.js";
8
- const submitPuzzleCaptchaSolution = (env) => async (req, res, next) => {
9
- if (getMaintenanceMode()) {
10
- req.logger.info(() => ({
11
- msg: "Maintenance mode active - returning verified"
12
- }));
13
- const response = {
14
- status: "ok",
15
- verified: true
16
- };
17
- return res.json(response);
18
- }
19
- let parsed;
20
- const tasks = new Tasks(env, req.logger);
21
- try {
22
- parsed = SubmitPuzzleCaptchaSolutionBody.parse(req.body);
23
- } catch (err) {
24
- return next(
25
- new ProsopoApiError("CAPTCHA.PARSE_ERROR", {
26
- context: { code: 400, error: err, body: req.body },
27
- i18n: req.i18n,
28
- logger: req.logger
29
- })
30
- );
31
- }
32
- const {
33
- challenge,
34
- signature,
35
- finalX,
36
- finalY,
37
- puzzleEvents,
38
- dapp,
39
- user,
40
- behavioralData,
41
- salt,
42
- simdReadings,
43
- clientMetaData
44
- } = parsed;
45
- validateSiteKey(dapp);
46
- validateAddr(user);
47
- const testVerdict = resolveTestSiteKeyVerdict(dapp, req.logger);
48
- if (testVerdict !== null) {
49
- const response = {
50
- status: "ok",
51
- verified: testVerdict
52
- };
53
- return res.json(response);
54
- }
55
- try {
56
- const clientRecord = await tasks.db.getClientRecord(dapp);
57
- if (!clientRecord) {
58
- return next(
59
- new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
60
- context: { code: 400, siteKey: dapp },
61
- i18n: req.i18n,
62
- logger: req.logger
63
- })
64
- );
65
- }
66
- const persistedSolutionTimeout = clientRecord.settings.solutionTimeout;
67
- const submitWindowMs = persistedSolutionTimeout ?? clientRecord.settings.verifiedTimeout;
68
- const verified = await tasks.puzzleCaptchaManager.verifyPuzzleCaptchaSolution(
69
- challenge,
70
- signature.provider.challenge,
71
- finalX,
72
- finalY,
73
- puzzleEvents,
74
- submitWindowMs,
75
- signature.user.timestamp,
76
- getIPAddress(req.ip || ""),
77
- flatten(req.headers),
78
- behavioralData,
79
- salt,
80
- simdReadings,
81
- clientMetaData
82
- );
83
- const response = {
84
- status: "ok",
85
- verified
86
- };
87
- return res.json(response);
88
- } catch (err) {
89
- req.logger.error(() => ({
90
- err,
91
- body: req.body,
92
- msg: "Error in puzzle captcha solution submission"
93
- }));
94
- return next(
95
- new ProsopoApiError("API.BAD_REQUEST", {
96
- context: {
97
- code: 500,
98
- siteKey: req.body.dapp,
99
- error: err
100
- },
101
- i18n: req.i18n,
102
- logger: req.logger
103
- })
104
- );
105
- }
106
- };
107
- export {
108
- submitPuzzleCaptchaSolution as default
5
+ import { ProsopoApiError } from "@prosopo/common";
6
+ import { SubmitPuzzleCaptchaSolutionBody } from "@prosopo/types";
7
+ import { flatten, getIPAddress } from "@prosopo/util";
8
+ //#region src/api/captcha/submitPuzzleCaptchaSolution.ts
9
+ var submitPuzzleCaptchaSolution_default = (env) => async (req, res, next) => {
10
+ if (getMaintenanceMode()) {
11
+ req.logger.info(() => ({ msg: "Maintenance mode active - returning verified" }));
12
+ return res.json({
13
+ status: "ok",
14
+ verified: true
15
+ });
16
+ }
17
+ let parsed;
18
+ const tasks = new Tasks(env, req.logger);
19
+ try {
20
+ parsed = SubmitPuzzleCaptchaSolutionBody.parse(req.body);
21
+ } catch (err) {
22
+ return next(new ProsopoApiError("CAPTCHA.PARSE_ERROR", {
23
+ context: {
24
+ code: 400,
25
+ error: err,
26
+ body: req.body
27
+ },
28
+ i18n: req.i18n,
29
+ logger: req.logger
30
+ }));
31
+ }
32
+ const { challenge, signature, finalX, finalY, puzzleEvents, dapp, user, behavioralData, salt, simdReadings, clientMetaData } = parsed;
33
+ validateSiteKey(dapp);
34
+ validateAddr(user);
35
+ const testVerdict = resolveTestSiteKeyVerdict(dapp, req.logger);
36
+ if (testVerdict !== null) {
37
+ const response = {
38
+ status: "ok",
39
+ verified: testVerdict
40
+ };
41
+ return res.json(response);
42
+ }
43
+ try {
44
+ const clientRecord = await tasks.db.getClientRecord(dapp);
45
+ if (!clientRecord) return next(new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
46
+ context: {
47
+ code: 400,
48
+ siteKey: dapp
49
+ },
50
+ i18n: req.i18n,
51
+ logger: req.logger
52
+ }));
53
+ const submitWindowMs = clientRecord.settings.solutionTimeout ?? clientRecord.settings.verifiedTimeout;
54
+ const response = {
55
+ status: "ok",
56
+ verified: await tasks.puzzleCaptchaManager.verifyPuzzleCaptchaSolution(challenge, signature.provider.challenge, finalX, finalY, puzzleEvents, submitWindowMs, signature.user.timestamp, getIPAddress(req.ip || ""), flatten(req.headers), behavioralData, salt, simdReadings, clientMetaData)
57
+ };
58
+ return res.json(response);
59
+ } catch (err) {
60
+ req.logger.error(() => ({
61
+ err,
62
+ body: req.body,
63
+ msg: "Error in puzzle captcha solution submission"
64
+ }));
65
+ return next(new ProsopoApiError("API.BAD_REQUEST", {
66
+ context: {
67
+ code: 500,
68
+ siteKey: req.body.dapp,
69
+ error: err
70
+ },
71
+ i18n: req.i18n,
72
+ logger: req.logger
73
+ }));
74
+ }
109
75
  };
76
+ //#endregion
77
+ export { submitPuzzleCaptchaSolution_default as default };