@prosopo/provider 4.15.14 → 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 (232) 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 +49 -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/spam/checkTrafficFilter.unit.test.js +140 -0
  219. package/dist/tests/unit/tasks/spam/checkTrafficFilter.unit.test.js.map +1 -1
  220. package/dist/tests/unit/testUtils/mockProviderEnv.d.ts +7 -7
  221. package/dist/util/usageCounters.js +220 -212
  222. package/dist/util.d.ts.map +1 -1
  223. package/dist/util.js +327 -364
  224. package/dist/util.js.map +1 -1
  225. package/dist/utils/devicePlatform.js +15 -9
  226. package/dist/utils/dns.js +64 -77
  227. package/dist/utils/hashUserAgent.js +14 -7
  228. package/dist/utils/hashUserIp.js +14 -6
  229. package/dist/utils/honeypot/encoders.js +74 -80
  230. package/dist/utils/honeypot/phraseBank.js +49 -44
  231. package/dist/utils/normalizeRequestIp.js +34 -26
  232. package/package.json +24 -24
@@ -1,205 +1,136 @@
1
- import { ProsopoApiError } from "@prosopo/common";
2
- import { GetPuzzleCaptchaChallengeRequestBody, CaptchaType, SimdReadingsStage, ApiParams } from "@prosopo/types";
3
- import { flatten } from "@prosopo/util";
1
+ import { validateAddr, validateSiteKey } from "../validateAddress.js";
4
2
  import { getCompositeIpAddress } from "../../compositeIpAddress.js";
5
- import "../../tasks/index.js";
6
- import { normalizeRequestIp } from "../../utils/normalizeRequestIp.js";
7
- import { getMaintenanceMode } from "../admin/apiToggleMaintenanceModeEndpoint.js";
3
+ import { recordCaptchaIssueError, recordCaptchaIssued } from "../metrics.js";
8
4
  import { getRequestUserScope } from "../blacklistRequestInspector.js";
9
- import { recordCaptchaIssued, recordCaptchaIssueError } from "../metrics.js";
10
- import { validateSiteKey, validateAddr } from "../validateAddress.js";
11
- import { buildPuzzleMaintenanceResponse } from "./maintenanceModeResponses.js";
12
5
  import { Tasks } from "../../tasks/tasks.js";
13
- const getPuzzleCaptchaChallenge = (env, userAccessRulesStorage) => async (req, res, next) => {
14
- let parsed;
15
- try {
16
- parsed = GetPuzzleCaptchaChallengeRequestBody.parse(req.body);
17
- } catch (err) {
18
- return next(
19
- new ProsopoApiError("CAPTCHA.PARSE_ERROR", {
20
- context: { code: 400, error: err },
21
- i18n: req.i18n,
22
- logger: req.logger
23
- })
24
- );
25
- }
26
- const { user, dapp, sessionId, simdReadings } = parsed;
27
- validateSiteKey(dapp);
28
- validateAddr(user);
29
- if (getMaintenanceMode()) {
30
- req.logger.info(() => ({
31
- msg: "Maintenance mode active - returning dummy puzzle challenge",
32
- data: { dapp, user, sessionId }
33
- }));
34
- return res.json(buildPuzzleMaintenanceResponse(user, dapp));
35
- }
36
- const tasks = new Tasks(env, req.logger);
37
- try {
38
- const clientSettings = await tasks.db.getClientRecord(dapp);
39
- if (!clientSettings) {
40
- return next(
41
- new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
42
- context: { code: 400, siteKey: dapp },
43
- i18n: req.i18n,
44
- logger: req.logger
45
- })
46
- );
47
- }
48
- const normalizedIp = normalizeRequestIp(req.ip, req.logger);
49
- if (!normalizedIp) {
50
- req.logger.warn(() => ({
51
- msg: "Request missing IP; geoblocking will be skipped"
52
- }));
53
- }
54
- const countryCode = req.ipInfo && "isValid" in req.ipInfo && req.ipInfo.isValid ? req.ipInfo.countryCode : void 0;
55
- const asn = req.ipInfo && "isValid" in req.ipInfo && req.ipInfo.isValid ? req.ipInfo.asnNumber : void 0;
56
- const userScope = getRequestUserScope(
57
- flatten(req.headers),
58
- req.ja4,
59
- normalizedIp,
60
- user,
61
- void 0,
62
- // headHash
63
- void 0,
64
- // coords
65
- countryCode,
66
- asn
67
- );
68
- const userAccessPolicy = (await tasks.puzzleCaptchaManager.getPrioritisedAccessPolicies(
69
- userAccessRulesStorage,
70
- dapp,
71
- userScope
72
- )).find((p) => !p.deferToVerify);
73
- const {
74
- valid,
75
- reason,
76
- sessionId: validSessionId
77
- } = await tasks.puzzleCaptchaManager.isValidRequest(
78
- clientSettings,
79
- CaptchaType.puzzle,
80
- env,
81
- sessionId,
82
- userAccessPolicy,
83
- normalizedIp
84
- );
85
- if (!valid) {
86
- return next(
87
- new ProsopoApiError(reason || "API.BAD_REQUEST", {
88
- context: {
89
- code: 400,
90
- siteKey: dapp,
91
- user
92
- },
93
- i18n: req.i18n,
94
- logger: req.logger
95
- })
96
- );
97
- }
98
- const origin = req.headers.origin;
99
- if (!origin) {
100
- return next(
101
- new ProsopoApiError("API.BAD_REQUEST", {
102
- context: {
103
- error: "Origin header not found",
104
- code: 400,
105
- siteKey: dapp,
106
- user
107
- },
108
- i18n: req.i18n,
109
- logger: req.logger
110
- })
111
- );
112
- }
113
- const tolerance = clientSettings?.settings?.puzzleTolerance;
114
- const challenge = await tasks.puzzleCaptchaManager.getPuzzleCaptchaChallenge(
115
- user,
116
- dapp,
117
- origin,
118
- tolerance
119
- );
120
- if (validSessionId && simdReadings) {
121
- await tasks.frictionlessManager.decryptAndAttachSimdReadingsIfAbsent(
122
- validSessionId,
123
- simdReadings,
124
- SimdReadingsStage.challenge
125
- ).catch((updateErr) => {
126
- req.logger.warn(() => ({
127
- err: updateErr,
128
- msg: "Failed to patch session with SIMD readings on puzzle challenge"
129
- }));
130
- });
131
- }
132
- await tasks.db.storePuzzleCaptchaRecord(
133
- challenge.challenge,
134
- {
135
- requestedAtTimestamp: challenge.requestedAtTimestamp,
136
- userAccount: user,
137
- dappAccount: dapp
138
- },
139
- challenge.targetX,
140
- challenge.targetY,
141
- challenge.originX,
142
- challenge.originY,
143
- challenge.tolerance,
144
- challenge.providerSignature,
145
- getCompositeIpAddress(normalizedIp),
146
- flatten(req.headers),
147
- req.ja4,
148
- validSessionId,
149
- // Persist the full ipinfo payload — consumers read
150
- // individual flags off this object instead of separate
151
- // flat fields.
152
- req.ipInfo
153
- );
154
- const getPuzzleCaptchaResponse = {
155
- [ApiParams.status]: "ok",
156
- [ApiParams.challenge]: challenge.challenge,
157
- [ApiParams.targetX]: challenge.targetX,
158
- [ApiParams.targetY]: challenge.targetY,
159
- [ApiParams.originX]: challenge.originX,
160
- [ApiParams.originY]: challenge.originY,
161
- [ApiParams.tolerance]: challenge.tolerance,
162
- [ApiParams.timestamp]: challenge.requestedAtTimestamp.toString(),
163
- [ApiParams.signature]: {
164
- [ApiParams.provider]: {
165
- [ApiParams.challenge]: challenge.providerSignature
166
- }
167
- }
168
- };
169
- req.logger.info(() => ({
170
- msg: "Puzzle captcha challenge issued",
171
- data: {
172
- captchaType: CaptchaType.puzzle,
173
- challenge: challenge.challenge,
174
- tolerance: challenge.tolerance,
175
- user,
176
- dapp,
177
- session: sessionId
178
- }
179
- }));
180
- recordCaptchaIssued(CaptchaType.puzzle);
181
- return res.json(getPuzzleCaptchaResponse);
182
- } catch (err) {
183
- recordCaptchaIssueError(CaptchaType.puzzle);
184
- req.logger.error(() => ({
185
- err,
186
- body: req.body,
187
- msg: "Error in puzzle captcha challenge request"
188
- }));
189
- return next(
190
- new ProsopoApiError("API.BAD_REQUEST", {
191
- context: {
192
- code: 500,
193
- siteKey: req.body.dapp,
194
- user: req.body.user,
195
- error: err
196
- },
197
- i18n: req.i18n,
198
- logger: req.logger
199
- })
200
- );
201
- }
202
- };
203
- export {
204
- getPuzzleCaptchaChallenge as default
6
+ import { getMaintenanceMode } from "../admin/apiToggleMaintenanceModeEndpoint.js";
7
+ import { normalizeRequestIp } from "../../utils/normalizeRequestIp.js";
8
+ import { buildPuzzleMaintenanceResponse } from "./maintenanceModeResponses.js";
9
+ import { ProsopoApiError } from "@prosopo/common";
10
+ import { ApiParams, CaptchaType, GetPuzzleCaptchaChallengeRequestBody, SimdReadingsStage } from "@prosopo/types";
11
+ import { flatten } from "@prosopo/util";
12
+ //#region src/api/captcha/getPuzzleCaptchaChallenge.ts
13
+ var getPuzzleCaptchaChallenge_default = (env, userAccessRulesStorage) => async (req, res, next) => {
14
+ let parsed;
15
+ try {
16
+ parsed = GetPuzzleCaptchaChallengeRequestBody.parse(req.body);
17
+ } catch (err) {
18
+ return next(new ProsopoApiError("CAPTCHA.PARSE_ERROR", {
19
+ context: {
20
+ code: 400,
21
+ error: err
22
+ },
23
+ i18n: req.i18n,
24
+ logger: req.logger
25
+ }));
26
+ }
27
+ const { user, dapp, sessionId, simdReadings } = parsed;
28
+ validateSiteKey(dapp);
29
+ validateAddr(user);
30
+ if (getMaintenanceMode()) {
31
+ req.logger.info(() => ({
32
+ msg: "Maintenance mode active - returning dummy puzzle challenge",
33
+ data: {
34
+ dapp,
35
+ user,
36
+ sessionId
37
+ }
38
+ }));
39
+ return res.json(buildPuzzleMaintenanceResponse(user, dapp));
40
+ }
41
+ const tasks = new Tasks(env, req.logger);
42
+ try {
43
+ const clientSettings = await tasks.db.getClientRecord(dapp);
44
+ if (!clientSettings) return next(new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
45
+ context: {
46
+ code: 400,
47
+ siteKey: dapp
48
+ },
49
+ i18n: req.i18n,
50
+ logger: req.logger
51
+ }));
52
+ const normalizedIp = normalizeRequestIp(req.ip, req.logger);
53
+ if (!normalizedIp) req.logger.warn(() => ({ msg: "Request missing IP; geoblocking will be skipped" }));
54
+ const countryCode = req.ipInfo && "isValid" in req.ipInfo && req.ipInfo.isValid ? req.ipInfo.countryCode : void 0;
55
+ const asn = req.ipInfo && "isValid" in req.ipInfo && req.ipInfo.isValid ? req.ipInfo.asnNumber : void 0;
56
+ const userScope = getRequestUserScope(flatten(req.headers), req.ja4, normalizedIp, user, void 0, void 0, countryCode, asn);
57
+ const userAccessPolicy = (await tasks.puzzleCaptchaManager.getPrioritisedAccessPolicies(userAccessRulesStorage, dapp, userScope)).find((p) => !p.deferToVerify);
58
+ const { valid, reason, sessionId: validSessionId } = await tasks.puzzleCaptchaManager.isValidRequest(clientSettings, CaptchaType.puzzle, env, sessionId, userAccessPolicy, normalizedIp);
59
+ if (!valid) return next(new ProsopoApiError(reason || "API.BAD_REQUEST", {
60
+ context: {
61
+ code: 400,
62
+ siteKey: dapp,
63
+ user
64
+ },
65
+ i18n: req.i18n,
66
+ logger: req.logger
67
+ }));
68
+ const origin = req.headers.origin;
69
+ if (!origin) return next(new ProsopoApiError("API.BAD_REQUEST", {
70
+ context: {
71
+ error: "Origin header not found",
72
+ code: 400,
73
+ siteKey: dapp,
74
+ user
75
+ },
76
+ i18n: req.i18n,
77
+ logger: req.logger
78
+ }));
79
+ const tolerance = clientSettings?.settings?.puzzleTolerance;
80
+ const challenge = await tasks.puzzleCaptchaManager.getPuzzleCaptchaChallenge(user, dapp, origin, tolerance);
81
+ if (validSessionId && simdReadings) await tasks.frictionlessManager.decryptAndAttachSimdReadingsIfAbsent(validSessionId, simdReadings, SimdReadingsStage.challenge).catch((updateErr) => {
82
+ req.logger.warn(() => ({
83
+ err: updateErr,
84
+ msg: "Failed to patch session with SIMD readings on puzzle challenge"
85
+ }));
86
+ });
87
+ await tasks.db.storePuzzleCaptchaRecord(challenge.challenge, {
88
+ requestedAtTimestamp: challenge.requestedAtTimestamp,
89
+ userAccount: user,
90
+ dappAccount: dapp
91
+ }, challenge.targetX, challenge.targetY, challenge.originX, challenge.originY, challenge.tolerance, challenge.providerSignature, getCompositeIpAddress(normalizedIp), flatten(req.headers), req.ja4, validSessionId, req.ipInfo);
92
+ const getPuzzleCaptchaResponse = {
93
+ [ApiParams.status]: "ok",
94
+ [ApiParams.challenge]: challenge.challenge,
95
+ [ApiParams.targetX]: challenge.targetX,
96
+ [ApiParams.targetY]: challenge.targetY,
97
+ [ApiParams.originX]: challenge.originX,
98
+ [ApiParams.originY]: challenge.originY,
99
+ [ApiParams.tolerance]: challenge.tolerance,
100
+ [ApiParams.timestamp]: challenge.requestedAtTimestamp.toString(),
101
+ [ApiParams.signature]: { [ApiParams.provider]: { [ApiParams.challenge]: challenge.providerSignature } }
102
+ };
103
+ req.logger.info(() => ({
104
+ msg: "Puzzle captcha challenge issued",
105
+ data: {
106
+ captchaType: CaptchaType.puzzle,
107
+ challenge: challenge.challenge,
108
+ tolerance: challenge.tolerance,
109
+ user,
110
+ dapp,
111
+ session: sessionId
112
+ }
113
+ }));
114
+ recordCaptchaIssued(CaptchaType.puzzle);
115
+ return res.json(getPuzzleCaptchaResponse);
116
+ } catch (err) {
117
+ recordCaptchaIssueError(CaptchaType.puzzle);
118
+ req.logger.error(() => ({
119
+ err,
120
+ body: req.body,
121
+ msg: "Error in puzzle captcha challenge request"
122
+ }));
123
+ return next(new ProsopoApiError("API.BAD_REQUEST", {
124
+ context: {
125
+ code: 500,
126
+ siteKey: req.body.dapp,
127
+ user: req.body.user,
128
+ error: err
129
+ },
130
+ i18n: req.i18n,
131
+ logger: req.logger
132
+ }));
133
+ }
205
134
  };
135
+ //#endregion
136
+ export { getPuzzleCaptchaChallenge_default as default };
@@ -1,52 +1,43 @@
1
+ import { ApiParams, POW_SEPARATOR } from "@prosopo/types";
1
2
  import { randomUUID } from "node:crypto";
2
- import { POW_SEPARATOR, ApiParams } from "@prosopo/types";
3
- const sessionPrefix = (host) => host ? host.replace(".prosopo.io", "") : "local";
4
- const buildChallenge = (user, dapp) => `${Date.now()}${POW_SEPARATOR}${user}${POW_SEPARATOR}${dapp}${POW_SEPARATOR}0`;
5
- const buildFrictionlessMaintenanceResponse = (captchaType, host) => ({
6
- [ApiParams.captchaType]: captchaType,
7
- [ApiParams.sessionId]: `${sessionPrefix(host)}-${randomUUID()}`,
8
- [ApiParams.status]: "ok"
3
+ //#region src/api/captcha/maintenanceModeResponses.ts
4
+ var sessionPrefix = (host) => host ? host.replace(".prosopo.io", "") : "local";
5
+ var buildChallenge = (user, dapp) => `${Date.now()}${POW_SEPARATOR}${user}${POW_SEPARATOR}${dapp}${POW_SEPARATOR}0`;
6
+ var buildFrictionlessMaintenanceResponse = (captchaType, host) => ({
7
+ [ApiParams.captchaType]: captchaType,
8
+ [ApiParams.sessionId]: `${sessionPrefix(host)}-${randomUUID()}`,
9
+ [ApiParams.status]: "ok"
9
10
  });
10
- const buildPowMaintenanceResponse = (user, dapp) => {
11
- const timestamp = Date.now();
12
- return {
13
- [ApiParams.status]: "ok",
14
- [ApiParams.challenge]: buildChallenge(user, dapp),
15
- [ApiParams.difficulty]: 1,
16
- [ApiParams.timestamp]: timestamp.toString(),
17
- [ApiParams.signature]: {
18
- [ApiParams.provider]: { [ApiParams.challenge]: "" }
19
- }
20
- };
11
+ var buildPowMaintenanceResponse = (user, dapp) => {
12
+ const timestamp = Date.now();
13
+ return {
14
+ [ApiParams.status]: "ok",
15
+ [ApiParams.challenge]: buildChallenge(user, dapp),
16
+ [ApiParams.difficulty]: 1,
17
+ [ApiParams.timestamp]: timestamp.toString(),
18
+ [ApiParams.signature]: { [ApiParams.provider]: { [ApiParams.challenge]: "" } }
19
+ };
21
20
  };
22
- const buildImageMaintenanceResponse = () => ({
23
- [ApiParams.status]: "ok",
24
- [ApiParams.captchas]: [],
25
- [ApiParams.requestHash]: "",
26
- [ApiParams.timestamp]: Date.now().toString(),
27
- [ApiParams.signature]: {
28
- [ApiParams.provider]: { [ApiParams.requestHash]: "" }
29
- }
21
+ var buildImageMaintenanceResponse = () => ({
22
+ [ApiParams.status]: "ok",
23
+ [ApiParams.captchas]: [],
24
+ [ApiParams.requestHash]: "",
25
+ [ApiParams.timestamp]: Date.now().toString(),
26
+ [ApiParams.signature]: { [ApiParams.provider]: { [ApiParams.requestHash]: "" } }
30
27
  });
31
- const buildPuzzleMaintenanceResponse = (user, dapp) => {
32
- const timestamp = Date.now();
33
- return {
34
- [ApiParams.status]: "ok",
35
- [ApiParams.challenge]: buildChallenge(user, dapp),
36
- [ApiParams.targetX]: 100,
37
- [ApiParams.targetY]: 100,
38
- [ApiParams.originX]: 0,
39
- [ApiParams.originY]: 0,
40
- [ApiParams.tolerance]: 1e3,
41
- [ApiParams.timestamp]: timestamp.toString(),
42
- [ApiParams.signature]: {
43
- [ApiParams.provider]: { [ApiParams.challenge]: "" }
44
- }
45
- };
46
- };
47
- export {
48
- buildFrictionlessMaintenanceResponse,
49
- buildImageMaintenanceResponse,
50
- buildPowMaintenanceResponse,
51
- buildPuzzleMaintenanceResponse
28
+ var buildPuzzleMaintenanceResponse = (user, dapp) => {
29
+ const timestamp = Date.now();
30
+ return {
31
+ [ApiParams.status]: "ok",
32
+ [ApiParams.challenge]: buildChallenge(user, dapp),
33
+ [ApiParams.targetX]: 100,
34
+ [ApiParams.targetY]: 100,
35
+ [ApiParams.originX]: 0,
36
+ [ApiParams.originY]: 0,
37
+ [ApiParams.tolerance]: 1e3,
38
+ [ApiParams.timestamp]: timestamp.toString(),
39
+ [ApiParams.signature]: { [ApiParams.provider]: { [ApiParams.challenge]: "" } }
40
+ };
52
41
  };
42
+ //#endregion
43
+ export { buildFrictionlessMaintenanceResponse, buildImageMaintenanceResponse, buildPowMaintenanceResponse, buildPuzzleMaintenanceResponse };
@@ -1,104 +1,78 @@
1
- import { ProsopoApiError } from "@prosopo/common";
2
- import { CaptchaSolutionBody, ApiParams } from "@prosopo/types";
3
- import { getIPAddress, flatten } from "@prosopo/util";
4
- import "../../tasks/index.js";
1
+ import { validateAddr, validateSiteKey } from "../validateAddress.js";
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
- import { Tasks } from "../../tasks/tasks.js";
9
- const submitImageCaptchaSolution = (env) => async (req, res, next) => {
10
- if (getMaintenanceMode()) {
11
- req.logger.info(() => ({
12
- msg: "Maintenance mode active - returning verified for image captcha"
13
- }));
14
- const result = {
15
- status: "ok",
16
- captchas: [],
17
- verified: true
18
- };
19
- return res.json(result);
20
- }
21
- const tasks = new Tasks(env, req.logger);
22
- let parsed;
23
- try {
24
- parsed = CaptchaSolutionBody.parse(req.body);
25
- } catch (err) {
26
- return next(
27
- new ProsopoApiError("CAPTCHA.PARSE_ERROR", {
28
- context: { code: 400, error: err, body: req.body },
29
- i18n: req.i18n,
30
- logger: req.logger
31
- })
32
- );
33
- }
34
- const { user, dapp } = parsed;
35
- validateSiteKey(dapp);
36
- validateAddr(user);
37
- const testVerdict = resolveTestSiteKeyVerdict(dapp, req.logger);
38
- if (testVerdict !== null) {
39
- const result = {
40
- status: "ok",
41
- captchas: [],
42
- verified: testVerdict
43
- };
44
- return res.json(result);
45
- }
46
- try {
47
- const clientRecord = await tasks.db.getClientRecord(parsed.dapp);
48
- if (!clientRecord) {
49
- return next(
50
- new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
51
- context: { code: 400, siteKey: dapp },
52
- i18n: req.i18n,
53
- logger: req.logger
54
- })
55
- );
56
- }
57
- const result = await tasks.imgCaptchaManager.dappUserSolution(
58
- user,
59
- dapp,
60
- parsed[ApiParams.requestHash],
61
- parsed[ApiParams.captchas],
62
- parsed[ApiParams.signature].user.timestamp,
63
- Number.parseInt(parsed[ApiParams.timestamp]),
64
- parsed[ApiParams.signature].provider.requestHash,
65
- getIPAddress(req.ip || ""),
66
- flatten(req.headers),
67
- req.ja4,
68
- parsed[ApiParams.behavioralData],
69
- // Persist the full ipinfo payload — consumers read
70
- // individual flags off this object instead of separate
71
- // flat fields.
72
- req.ipInfo,
73
- parsed[ApiParams.simdReadings],
74
- parsed[ApiParams.clientMetaData]
75
- );
76
- const returnValue = {
77
- status: req.i18n.t(
78
- result.verified ? "API.CAPTCHA_PASSED" : "API.CAPTCHA_FAILED"
79
- ),
80
- ...result
81
- };
82
- return res.json(returnValue);
83
- } catch (err) {
84
- req.logger.error(() => ({
85
- err,
86
- body: req.body,
87
- msg: "Error in image captcha solution submission"
88
- }));
89
- return next(
90
- new ProsopoApiError("API.BAD_REQUEST", {
91
- context: {
92
- code: 500,
93
- siteKey: req.body.dapp,
94
- error: err
95
- },
96
- i18n: req.i18n,
97
- logger: req.logger
98
- })
99
- );
100
- }
101
- };
102
- export {
103
- submitImageCaptchaSolution as default
5
+ import { ProsopoApiError } from "@prosopo/common";
6
+ import { ApiParams, CaptchaSolutionBody } from "@prosopo/types";
7
+ import { flatten, getIPAddress } from "@prosopo/util";
8
+ //#region src/api/captcha/submitImageCaptchaSolution.ts
9
+ var submitImageCaptchaSolution_default = (env) => async (req, res, next) => {
10
+ if (getMaintenanceMode()) {
11
+ req.logger.info(() => ({ msg: "Maintenance mode active - returning verified for image captcha" }));
12
+ return res.json({
13
+ status: "ok",
14
+ captchas: [],
15
+ verified: true
16
+ });
17
+ }
18
+ const tasks = new Tasks(env, req.logger);
19
+ let parsed;
20
+ try {
21
+ parsed = CaptchaSolutionBody.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 { user, dapp } = parsed;
34
+ validateSiteKey(dapp);
35
+ validateAddr(user);
36
+ const testVerdict = resolveTestSiteKeyVerdict(dapp, req.logger);
37
+ if (testVerdict !== null) {
38
+ const result = {
39
+ status: "ok",
40
+ captchas: [],
41
+ verified: testVerdict
42
+ };
43
+ return res.json(result);
44
+ }
45
+ try {
46
+ if (!await tasks.db.getClientRecord(parsed.dapp)) 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 result = await tasks.imgCaptchaManager.dappUserSolution(user, dapp, parsed[ApiParams.requestHash], parsed[ApiParams.captchas], parsed[ApiParams.signature].user.timestamp, Number.parseInt(parsed[ApiParams.timestamp]), parsed[ApiParams.signature].provider.requestHash, getIPAddress(req.ip || ""), flatten(req.headers), req.ja4, parsed[ApiParams.behavioralData], req.ipInfo, parsed[ApiParams.simdReadings], parsed[ApiParams.clientMetaData]);
55
+ const returnValue = {
56
+ status: req.i18n.t(result.verified ? "API.CAPTCHA_PASSED" : "API.CAPTCHA_FAILED"),
57
+ ...result
58
+ };
59
+ return res.json(returnValue);
60
+ } catch (err) {
61
+ req.logger.error(() => ({
62
+ err,
63
+ body: req.body,
64
+ msg: "Error in image captcha solution submission"
65
+ }));
66
+ return next(new ProsopoApiError("API.BAD_REQUEST", {
67
+ context: {
68
+ code: 500,
69
+ siteKey: req.body.dapp,
70
+ error: err
71
+ },
72
+ i18n: req.i18n,
73
+ logger: req.logger
74
+ }));
75
+ }
104
76
  };
77
+ //#endregion
78
+ export { submitImageCaptchaSolution_default as default };