@prosopo/provider 4.15.14 → 4.15.16

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 +63 -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,63 +1,81 @@
1
- import { handleErrors } from "@prosopo/api-express-router";
1
+ import checkSpamEmail_default from "./captcha/checkSpamEmail.js";
2
+ import handler_default from "./captcha/getFrictionlessCaptchaChallenge/handler.js";
3
+ import getImageCaptchaChallenge_default from "./captcha/getImageCaptchaChallenge.js";
4
+ import getPoWCaptchaChallenge_default from "./captcha/getPoWCaptchaChallenge.js";
5
+ import getPuzzleCaptchaChallenge_default from "./captcha/getPuzzleCaptchaChallenge.js";
6
+ import submitImageCaptchaSolution_default from "./captcha/submitImageCaptchaSolution.js";
7
+ import submitPoWCaptchaSolution_default from "./captcha/submitPoWCaptchaSolution.js";
8
+ import submitPuzzleCaptchaSolution_default from "./captcha/submitPuzzleCaptchaSolution.js";
2
9
  import { ClientApiPaths } from "@prosopo/types";
10
+ import { handleErrors } from "@prosopo/api-express-router";
3
11
  import express from "express";
4
- import checkSpamEmail from "./captcha/checkSpamEmail.js";
5
- import "./captcha/getFrictionlessCaptchaChallenge.js";
6
- import getImageCaptchaChallenge from "./captcha/getImageCaptchaChallenge.js";
7
- import getPoWCaptchaChallenge from "./captcha/getPoWCaptchaChallenge.js";
8
- import getPuzzleCaptchaChallenge from "./captcha/getPuzzleCaptchaChallenge.js";
9
- import submitImageCaptchaSolution from "./captcha/submitImageCaptchaSolution.js";
10
- import submitPoWCaptchaSolution from "./captcha/submitPoWCaptchaSolution.js";
11
- import submitPuzzleCaptchaSolution from "./captcha/submitPuzzleCaptchaSolution.js";
12
- import getFrictionlessCaptchaChallenge from "./captcha/getFrictionlessCaptchaChallenge/handler.js";
13
- const asyncHandler = (handler) => (req, res, next) => {
14
- Promise.resolve().then(() => handler(req, res, next)).catch(next);
12
+ //#region src/api/captcha.ts
13
+ /**
14
+ * Wraps an async route handler so that any rejection - including synchronous
15
+ * throws (e.g. from validateSiteKey/validateAddr) that become rejected
16
+ * promises inside the async handler - is forwarded to Express's error handler.
17
+ * Without this, Express 4 does not observe the rejection and the request hangs
18
+ * instead of returning the intended 4xx response.
19
+ *
20
+ * The handler is invoked inside `Promise.resolve().then(...)` so that even a
21
+ * truly synchronous throw (from a non-async handler) is captured as a rejected
22
+ * promise and forwarded to `next`, rather than propagating synchronously.
23
+ */
24
+ var asyncHandler = (handler) => (req, res, next) => {
25
+ Promise.resolve().then(() => handler(req, res, next)).catch(next);
15
26
  };
27
+ /**
28
+ * Returns a router connected to the database which can interact with the Proposo protocol
29
+ *
30
+ * @return {Router} - A middleware router that can interact with the Prosopo protocol
31
+ * @param {ProviderEnvironment} env - The Prosopo environment
32
+ */
16
33
  function prosopoRouter(env) {
17
- const router = express.Router();
18
- let userAccessRulesStorage;
19
- try {
20
- userAccessRulesStorage = env.getDb().getUserAccessRulesStorage();
21
- } catch (err) {
22
- env.logger.warn(() => ({
23
- msg: "User access rules storage unavailable; captcha endpoints will skip access-policy checks",
24
- err
25
- }));
26
- userAccessRulesStorage = void 0;
27
- }
28
- router.post(
29
- ClientApiPaths.GetImageCaptchaChallenge,
30
- asyncHandler(getImageCaptchaChallenge(env, userAccessRulesStorage))
31
- );
32
- router.post(
33
- ClientApiPaths.SubmitImageCaptchaSolution,
34
- asyncHandler(submitImageCaptchaSolution(env))
35
- );
36
- router.post(
37
- ClientApiPaths.GetPowCaptchaChallenge,
38
- asyncHandler(getPoWCaptchaChallenge(env, userAccessRulesStorage))
39
- );
40
- router.post(
41
- ClientApiPaths.SubmitPowCaptchaSolution,
42
- asyncHandler(submitPoWCaptchaSolution(env))
43
- );
44
- router.post(
45
- ClientApiPaths.GetFrictionlessCaptchaChallenge,
46
- asyncHandler(getFrictionlessCaptchaChallenge(env, userAccessRulesStorage))
47
- );
48
- router.post(
49
- ClientApiPaths.GetPuzzleCaptchaChallenge,
50
- asyncHandler(getPuzzleCaptchaChallenge(env, userAccessRulesStorage))
51
- );
52
- router.post(
53
- ClientApiPaths.SubmitPuzzleCaptchaSolution,
54
- asyncHandler(submitPuzzleCaptchaSolution(env))
55
- );
56
- router.post(ClientApiPaths.CheckSpamEmail, asyncHandler(checkSpamEmail(env)));
57
- router.use(handleErrors);
58
- return router;
34
+ const router = express.Router();
35
+ let userAccessRulesStorage;
36
+ try {
37
+ userAccessRulesStorage = env.getDb().getUserAccessRulesStorage();
38
+ } catch (err) {
39
+ env.logger.warn(() => ({
40
+ msg: "User access rules storage unavailable; captcha endpoints will skip access-policy checks",
41
+ err
42
+ }));
43
+ userAccessRulesStorage = void 0;
44
+ }
45
+ /**
46
+ * Provides a Captcha puzzle to a Dapp User
47
+ */
48
+ router.post(ClientApiPaths.GetImageCaptchaChallenge, asyncHandler(getImageCaptchaChallenge_default(env, userAccessRulesStorage)));
49
+ /**
50
+ * Receives solved CAPTCHA challenges from the user, stores to database, and checks against solution commitment
51
+ */
52
+ router.post(ClientApiPaths.SubmitImageCaptchaSolution, asyncHandler(submitImageCaptchaSolution_default(env)));
53
+ /**
54
+ * Supplies a PoW challenge to a Dapp User
55
+ */
56
+ router.post(ClientApiPaths.GetPowCaptchaChallenge, asyncHandler(getPoWCaptchaChallenge_default(env, userAccessRulesStorage)));
57
+ /**
58
+ * Verifies a user's PoW solution as being approved or not
59
+ */
60
+ router.post(ClientApiPaths.SubmitPowCaptchaSolution, asyncHandler(submitPoWCaptchaSolution_default(env)));
61
+ /**
62
+ * Gets a frictionless captcha challenge
63
+ */
64
+ router.post(ClientApiPaths.GetFrictionlessCaptchaChallenge, asyncHandler(handler_default(env, userAccessRulesStorage)));
65
+ /**
66
+ * Supplies a Puzzle challenge to a Dapp User
67
+ */
68
+ router.post(ClientApiPaths.GetPuzzleCaptchaChallenge, asyncHandler(getPuzzleCaptchaChallenge_default(env, userAccessRulesStorage)));
69
+ /**
70
+ * Verifies a user's Puzzle solution as being approved or not
71
+ */
72
+ router.post(ClientApiPaths.SubmitPuzzleCaptchaSolution, asyncHandler(submitPuzzleCaptchaSolution_default(env)));
73
+ /**
74
+ * Checks if an email domain is spam
75
+ */
76
+ router.post(ClientApiPaths.CheckSpamEmail, asyncHandler(checkSpamEmail_default(env)));
77
+ router.use(handleErrors);
78
+ return router;
59
79
  }
60
- export {
61
- asyncHandler,
62
- prosopoRouter
63
- };
80
+ //#endregion
81
+ export { asyncHandler, prosopoRouter };
@@ -1,25 +1,23 @@
1
1
  import { createHmac } from "node:crypto";
2
- const EXTENSIONS = ["gif", "png", "webp"];
3
- const buildDnsEventUrl = (sessionId) => {
4
- const subzone = process.env.DNS_EVENT_SUBZONE;
5
- const secret = process.env.DNS_EVENT_HMAC_SECRET;
6
- if (!subzone || !secret) {
7
- return void 0;
8
- }
9
- const path = derivePath(sessionId, secret);
10
- const port = process.env.DNS_EVENT_PORT;
11
- const host = port ? `${sessionId}.${subzone}:${port}` : `${sessionId}.${subzone}`;
12
- return `https://${host}${path}?sid=${sessionId}`;
2
+ //#region src/api/dnsEventUrl.ts
3
+ var EXTENSIONS = [
4
+ "gif",
5
+ "png",
6
+ "webp"
7
+ ];
8
+ var buildDnsEventUrl = (sessionId) => {
9
+ const subzone = process.env.DNS_EVENT_SUBZONE;
10
+ const secret = process.env.DNS_EVENT_HMAC_SECRET;
11
+ if (!subzone || !secret) return;
12
+ const path = derivePath(sessionId, secret);
13
+ const port = process.env.DNS_EVENT_PORT;
14
+ return `https://${port ? `${sessionId}.${subzone}:${port}` : `${sessionId}.${subzone}`}${path}?sid=${sessionId}`;
13
15
  };
14
- const derivePath = (sessionId, secret) => {
15
- const mac = createHmac("sha256", secret);
16
- mac.update(sessionId);
17
- const digest = mac.digest();
18
- const name = digest.subarray(0, 8).toString("hex");
19
- const ext = EXTENSIONS[(digest.at(8) ?? 0) % EXTENSIONS.length];
20
- return `/${name}.${ext}`;
21
- };
22
- export {
23
- buildDnsEventUrl,
24
- derivePath
16
+ var derivePath = (sessionId, secret) => {
17
+ const mac = createHmac("sha256", secret);
18
+ mac.update(sessionId);
19
+ const digest = mac.digest();
20
+ return `/${digest.subarray(0, 8).toString("hex")}.${EXTENSIONS[(digest.at(8) ?? 0) % EXTENSIONS.length]}`;
25
21
  };
22
+ //#endregion
23
+ export { buildDnsEventUrl, derivePath };
@@ -1,124 +1,113 @@
1
- import { handleErrors } from "@prosopo/api-express-router";
1
+ import { recordDomainValidation } from "./metrics.js";
2
+ import { Tasks } from "../tasks/tasks.js";
3
+ import { getMaintenanceMode } from "./admin/apiToggleMaintenanceModeEndpoint.js";
4
+ import { isReservedTestSiteKey } from "./testSiteKey.js";
2
5
  import { ProsopoApiError } from "@prosopo/common";
3
- import { parseUrl, decodeGoogleTranslateHost } from "@prosopo/util";
6
+ import { decodeGoogleTranslateHost, parseUrl } from "@prosopo/util";
4
7
  import { validateAddress } from "@prosopo/util-crypto";
5
8
  import { ZodError } from "zod";
6
- import "../tasks/index.js";
7
- import { getMaintenanceMode } from "./admin/apiToggleMaintenanceModeEndpoint.js";
8
- import { recordDomainValidation } from "./metrics.js";
9
- import { isReservedTestSiteKey } from "./testSiteKey.js";
10
- import { Tasks } from "../tasks/tasks.js";
11
- const domainMiddleware = (env) => {
12
- let cachedTasks;
13
- return async (req, res, next) => {
14
- try {
15
- if (getMaintenanceMode()) {
16
- req.logger.info(() => ({
17
- msg: "Maintenance mode active - skipping domain validation"
18
- }));
19
- next();
20
- return;
21
- }
22
- if (!cachedTasks) {
23
- cachedTasks = new Tasks(env);
24
- }
25
- const tasks = cachedTasks;
26
- const siteKey = req.headers["prosopo-site-key"];
27
- if (!siteKey)
28
- throw siteKeyNotRegisteredError(
29
- req.i18n,
30
- "No sitekey provided",
31
- req.logger
32
- );
33
- try {
34
- validateAddress(siteKey, false, 42);
35
- } catch (err) {
36
- throw invalidSiteKeyError(req.i18n, siteKey, req.logger);
37
- }
38
- if (isReservedTestSiteKey(siteKey)) {
39
- recordDomainValidation("test_key");
40
- next();
41
- return;
42
- }
43
- const clientSettings = await tasks.db.getClientRecord(siteKey);
44
- if (!clientSettings)
45
- throw siteKeyNotRegisteredError(req.i18n, siteKey, req.logger);
46
- const allowedDomains = clientSettings.settings?.domains;
47
- if (!allowedDomains)
48
- throw siteKeyInvalidDomainError(
49
- req.i18n,
50
- siteKey,
51
- req.hostname,
52
- req.logger
53
- );
54
- const origin = req.headers.origin;
55
- if (!origin)
56
- throw unauthorizedOriginError(req.i18n, void 0, req.logger);
57
- const candidateOrigins = [origin, ...googleTranslateOrigins(origin)];
58
- for (const candidate of candidateOrigins) {
59
- for (const domain of allowedDomains) {
60
- if (tasks.clientTaskManager.domainPatternMatcher(candidate, domain)) {
61
- recordDomainValidation("allowed");
62
- next();
63
- return;
64
- }
65
- }
66
- }
67
- throw unauthorizedOriginError(req.i18n, origin, req.logger);
68
- } catch (err) {
69
- recordDomainValidation("rejected");
70
- if (err instanceof ProsopoApiError || err instanceof ZodError || err instanceof SyntaxError) {
71
- handleErrors(err, req, res, next);
72
- } else {
73
- res.status(401).json({ error: "Unauthorized", message: err });
74
- return;
75
- }
76
- }
77
- };
78
- };
79
- const googleTranslateOrigins = (origin) => {
80
- try {
81
- const url = parseUrl(origin);
82
- const decodedHost = decodeGoogleTranslateHost(url.hostname);
83
- if (!decodedHost) return [];
84
- return [`${url.protocol}//${decodedHost}`];
85
- } catch {
86
- return [];
87
- }
9
+ import { handleErrors } from "@prosopo/api-express-router";
10
+ //#region src/api/domainMiddleware.ts
11
+ var domainMiddleware = (env) => {
12
+ let cachedTasks;
13
+ return async (req, res, next) => {
14
+ try {
15
+ if (getMaintenanceMode()) {
16
+ req.logger.info(() => ({ msg: "Maintenance mode active - skipping domain validation" }));
17
+ next();
18
+ return;
19
+ }
20
+ if (!cachedTasks) cachedTasks = new Tasks(env);
21
+ const tasks = cachedTasks;
22
+ const siteKey = req.headers["prosopo-site-key"];
23
+ if (!siteKey) throw siteKeyNotRegisteredError(req.i18n, "No sitekey provided", req.logger);
24
+ try {
25
+ validateAddress(siteKey, false, 42);
26
+ } catch (err) {
27
+ throw invalidSiteKeyError(req.i18n, siteKey, req.logger);
28
+ }
29
+ if (isReservedTestSiteKey(siteKey)) {
30
+ recordDomainValidation("test_key");
31
+ next();
32
+ return;
33
+ }
34
+ const clientSettings = await tasks.db.getClientRecord(siteKey);
35
+ if (!clientSettings) throw siteKeyNotRegisteredError(req.i18n, siteKey, req.logger);
36
+ const allowedDomains = clientSettings.settings?.domains;
37
+ if (!allowedDomains) throw siteKeyInvalidDomainError(req.i18n, siteKey, req.hostname, req.logger);
38
+ const origin = req.headers.origin;
39
+ if (!origin) throw unauthorizedOriginError(req.i18n, void 0, req.logger);
40
+ const candidateOrigins = [origin, ...googleTranslateOrigins(origin)];
41
+ for (const candidate of candidateOrigins) for (const domain of allowedDomains) if (tasks.clientTaskManager.domainPatternMatcher(candidate, domain)) {
42
+ recordDomainValidation("allowed");
43
+ next();
44
+ return;
45
+ }
46
+ throw unauthorizedOriginError(req.i18n, origin, req.logger);
47
+ } catch (err) {
48
+ recordDomainValidation("rejected");
49
+ if (err instanceof ProsopoApiError || err instanceof ZodError || err instanceof SyntaxError) handleErrors(err, req, res, next);
50
+ else {
51
+ res.status(401).json({
52
+ error: "Unauthorized",
53
+ message: err
54
+ });
55
+ return;
56
+ }
57
+ }
58
+ };
88
59
  };
89
- const siteKeyNotRegisteredError = (i18n, dapp, logger) => {
90
- return new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
91
- context: { code: 400, siteKey: dapp },
92
- i18n,
93
- logger
94
- });
60
+ var googleTranslateOrigins = (origin) => {
61
+ try {
62
+ const url = parseUrl(origin);
63
+ const decodedHost = decodeGoogleTranslateHost(url.hostname);
64
+ if (!decodedHost) return [];
65
+ return [`${url.protocol}//${decodedHost}`];
66
+ } catch {
67
+ return [];
68
+ }
95
69
  };
96
- const invalidSiteKeyError = (i18n, siteKey, logger) => {
97
- return new ProsopoApiError("API.INVALID_SITE_KEY", {
98
- context: { code: 400, siteKey },
99
- i18n,
100
- logger
101
- });
70
+ var siteKeyNotRegisteredError = (i18n, dapp, logger) => {
71
+ return new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
72
+ context: {
73
+ code: 400,
74
+ siteKey: dapp
75
+ },
76
+ i18n,
77
+ logger
78
+ });
102
79
  };
103
- const unauthorizedOriginError = (i18n, origin, logger) => {
104
- return new ProsopoApiError("API.UNAUTHORIZED_ORIGIN_URL", {
105
- context: { code: 400, origin },
106
- i18n,
107
- logger
108
- });
80
+ var invalidSiteKeyError = (i18n, siteKey, logger) => {
81
+ return new ProsopoApiError("API.INVALID_SITE_KEY", {
82
+ context: {
83
+ code: 400,
84
+ siteKey
85
+ },
86
+ i18n,
87
+ logger
88
+ });
109
89
  };
110
- const siteKeyInvalidDomainError = (i18n, dapp, domain, logger) => {
111
- return new ProsopoApiError("API.UNAUTHORIZED_ORIGIN_URL", {
112
- context: {
113
- code: 400,
114
- message: "No domains are allowed for this site key. Please fix in the Procaptcha Portal",
115
- siteKey: dapp,
116
- domain
117
- },
118
- i18n,
119
- logger
120
- });
90
+ var unauthorizedOriginError = (i18n, origin, logger) => {
91
+ return new ProsopoApiError("API.UNAUTHORIZED_ORIGIN_URL", {
92
+ context: {
93
+ code: 400,
94
+ origin
95
+ },
96
+ i18n,
97
+ logger
98
+ });
121
99
  };
122
- export {
123
- domainMiddleware
100
+ var siteKeyInvalidDomainError = (i18n, dapp, domain, logger) => {
101
+ return new ProsopoApiError("API.UNAUTHORIZED_ORIGIN_URL", {
102
+ context: {
103
+ code: 400,
104
+ message: "No domains are allowed for this site key. Please fix in the Procaptcha Portal",
105
+ siteKey: dapp,
106
+ domain
107
+ },
108
+ i18n,
109
+ logger
110
+ });
124
111
  };
112
+ //#endregion
113
+ export { domainMiddleware };
@@ -1,54 +1,68 @@
1
1
  import { ProviderApi } from "@prosopo/api";
2
2
  import { getProviders } from "@prosopo/load-balancer";
3
+ //#region src/api/forwardVerify.ts
4
+ /**
5
+ * A client can send a verify request to *any* pronode, not necessarily the
6
+ * provider that issued the token. This determines the issuing provider from the
7
+ * `providerUrl` embedded in the token and, when it is a *different* provider to
8
+ * this one, forwards the verification request to it and returns its response —
9
+ * mirroring the AWS Lambda verify endpoint, but running on a provider.
10
+ *
11
+ * Returns `null` when this provider is the issuer (or the issuer cannot be
12
+ * determined), signalling the caller to verify locally as before.
13
+ *
14
+ * @param env - The provider environment
15
+ * @param logger - Request-scoped logger
16
+ * @param path - The verify path to forward to (kept identical, so the issuer
17
+ * routes the request to the correct captcha-type verification)
18
+ * @param providerUrl - The issuing provider url, decoded from the token
19
+ * @param dapp - The site key (used as the `Prosopo-Site-Key` header)
20
+ * @param user - The user account (used as the `Prosopo-User` header)
21
+ * @param body - The original, already-parsed request body, forwarded verbatim
22
+ */
3
23
  function normalizeProviderUrl(url) {
4
- try {
5
- const parsed = new URL(url);
6
- parsed.hostname = parsed.hostname.replace(/^(ipv4|ipv6)\./, "");
7
- return parsed.toString().replace(/\/$/, "");
8
- } catch {
9
- return url.replace(/\/$/, "");
10
- }
24
+ try {
25
+ const parsed = new URL(url);
26
+ parsed.hostname = parsed.hostname.replace(/^(ipv4|ipv6)\./, "");
27
+ return parsed.toString().replace(/\/$/, "");
28
+ } catch {
29
+ return url.replace(/\/$/, "");
30
+ }
11
31
  }
12
32
  async function forwardVerifyIfNotIssuer(args) {
13
- const { env, logger, path, providerUrl, dapp, user, body, alreadyForwarded } = args;
14
- if (alreadyForwarded) {
15
- return null;
16
- }
17
- if (!providerUrl) {
18
- return null;
19
- }
20
- let providers;
21
- try {
22
- providers = await getProviders(env.defaultEnvironment);
23
- } catch (error) {
24
- logger.warn(() => ({
25
- msg: "Failed to load provider list; verifying locally",
26
- err: error instanceof Error ? error : new Error(String(error)),
27
- data: { providerUrl }
28
- }));
29
- return null;
30
- }
31
- const normalizedProviderUrl = normalizeProviderUrl(providerUrl);
32
- const issuer = providers.find(
33
- (provider) => normalizeProviderUrl(provider.url) === normalizedProviderUrl
34
- );
35
- if (!issuer) {
36
- logger.warn(() => ({
37
- msg: "Token providerUrl is not a known provider; verifying locally",
38
- data: { providerUrl }
39
- }));
40
- return null;
41
- }
42
- if (env.pair?.address && issuer.address === env.pair.address) {
43
- return null;
44
- }
45
- logger.info(() => ({
46
- msg: "Forwarding verify request to issuing provider",
47
- data: { providerUrl, path, dapp }
48
- }));
49
- const providerApi = new ProviderApi(issuer.url, dapp);
50
- return providerApi.forwardVerify(path, body, user);
33
+ const { env, logger, path, providerUrl, dapp, user, body, alreadyForwarded } = args;
34
+ if (alreadyForwarded) return null;
35
+ if (!providerUrl) return null;
36
+ let providers;
37
+ try {
38
+ providers = await getProviders(env.defaultEnvironment);
39
+ } catch (error) {
40
+ logger.warn(() => ({
41
+ msg: "Failed to load provider list; verifying locally",
42
+ err: error instanceof Error ? error : new Error(String(error)),
43
+ data: { providerUrl }
44
+ }));
45
+ return null;
46
+ }
47
+ const normalizedProviderUrl = normalizeProviderUrl(providerUrl);
48
+ const issuer = providers.find((provider) => normalizeProviderUrl(provider.url) === normalizedProviderUrl);
49
+ if (!issuer) {
50
+ logger.warn(() => ({
51
+ msg: "Token providerUrl is not a known provider; verifying locally",
52
+ data: { providerUrl }
53
+ }));
54
+ return null;
55
+ }
56
+ if (env.pair?.address && issuer.address === env.pair.address) return null;
57
+ logger.info(() => ({
58
+ msg: "Forwarding verify request to issuing provider",
59
+ data: {
60
+ providerUrl,
61
+ path,
62
+ dapp
63
+ }
64
+ }));
65
+ return new ProviderApi(issuer.url, dapp).forwardVerify(path, body, user);
51
66
  }
52
- export {
53
- forwardVerifyIfNotIssuer
54
- };
67
+ //#endregion
68
+ export { forwardVerifyIfNotIssuer };
@@ -1,66 +1,47 @@
1
- import { handleErrors } from "@prosopo/api-express-router";
2
1
  import { getLogger } from "@prosopo/logger";
3
- const HEADER_TCP_TO_CHELLO = "x-tls-tcp-to-chello-us";
4
- const HEADER_CHELLO_TO_HANDSHAKE = "x-tls-chello-to-handshake-us";
5
- const parseTimingHeader = (raw, logger, headerName) => {
6
- if (raw === void 0) {
7
- return void 0;
8
- }
9
- const value = Array.isArray(raw) ? raw[0] : raw;
10
- if (value === void 0) {
11
- return void 0;
12
- }
13
- const parsed = Number.parseInt(value, 10);
14
- if (Number.isNaN(parsed) || parsed < 0) {
15
- logger.debug(() => ({
16
- msg: "Ignoring malformed handshake timing header",
17
- data: { header: headerName, raw: value }
18
- }));
19
- return void 0;
20
- }
21
- return parsed;
22
- };
23
- const getHandshakeTiming = (headers, logger) => {
24
- const log = logger ?? getLogger("info", "provider:handshake-timing");
25
- return {
26
- tcpToChelloUs: parseTimingHeader(
27
- headers[HEADER_TCP_TO_CHELLO],
28
- log,
29
- HEADER_TCP_TO_CHELLO
30
- ),
31
- chelloToHandshakeUs: parseTimingHeader(
32
- headers[HEADER_CHELLO_TO_HANDSHAKE],
33
- log,
34
- HEADER_CHELLO_TO_HANDSHAKE
35
- )
36
- };
2
+ import { handleErrors } from "@prosopo/api-express-router";
3
+ //#region src/api/handshakeTimingMiddleware.ts
4
+ var HEADER_TCP_TO_CHELLO = "x-tls-tcp-to-chello-us";
5
+ var HEADER_CHELLO_TO_HANDSHAKE = "x-tls-chello-to-handshake-us";
6
+ var parseTimingHeader = (raw, logger, headerName) => {
7
+ if (raw === void 0) return;
8
+ const value = Array.isArray(raw) ? raw[0] : raw;
9
+ if (value === void 0) return;
10
+ const parsed = Number.parseInt(value, 10);
11
+ if (Number.isNaN(parsed) || parsed < 0) {
12
+ logger.debug(() => ({
13
+ msg: "Ignoring malformed handshake timing header",
14
+ data: {
15
+ header: headerName,
16
+ raw: value
17
+ }
18
+ }));
19
+ return;
20
+ }
21
+ return parsed;
37
22
  };
38
- const handshakeTimingMiddleware = (env) => {
39
- return async (req, res, next) => {
40
- try {
41
- const timing = getHandshakeTiming(req.headers, req.logger);
42
- if (timing.tcpToChelloUs !== void 0) {
43
- req.tcpToChelloUs = timing.tcpToChelloUs;
44
- }
45
- if (timing.chelloToHandshakeUs !== void 0) {
46
- req.chelloToHandshakeUs = timing.chelloToHandshakeUs;
47
- }
48
- if (timing.tcpToChelloUs !== void 0 || timing.chelloToHandshakeUs !== void 0) {
49
- req.logger = req.logger.with(
50
- {
51
- tcpToChelloUs: timing.tcpToChelloUs,
52
- chelloToHandshakeUs: timing.chelloToHandshakeUs
53
- },
54
- "handshakeTiming"
55
- );
56
- }
57
- next();
58
- } catch (err) {
59
- return handleErrors(err, req, res, next);
60
- }
61
- };
23
+ var getHandshakeTiming = (headers, logger) => {
24
+ const log = logger ?? getLogger("info", "provider:handshake-timing");
25
+ return {
26
+ tcpToChelloUs: parseTimingHeader(headers[HEADER_TCP_TO_CHELLO], log, HEADER_TCP_TO_CHELLO),
27
+ chelloToHandshakeUs: parseTimingHeader(headers[HEADER_CHELLO_TO_HANDSHAKE], log, HEADER_CHELLO_TO_HANDSHAKE)
28
+ };
62
29
  };
63
- export {
64
- getHandshakeTiming,
65
- handshakeTimingMiddleware
30
+ var handshakeTimingMiddleware = (env) => {
31
+ return async (req, res, next) => {
32
+ try {
33
+ const timing = getHandshakeTiming(req.headers, req.logger);
34
+ if (timing.tcpToChelloUs !== void 0) req.tcpToChelloUs = timing.tcpToChelloUs;
35
+ if (timing.chelloToHandshakeUs !== void 0) req.chelloToHandshakeUs = timing.chelloToHandshakeUs;
36
+ if (timing.tcpToChelloUs !== void 0 || timing.chelloToHandshakeUs !== void 0) req.logger = req.logger.with({
37
+ tcpToChelloUs: timing.tcpToChelloUs,
38
+ chelloToHandshakeUs: timing.chelloToHandshakeUs
39
+ }, "handshakeTiming");
40
+ next();
41
+ } catch (err) {
42
+ return handleErrors(err, req, res, next);
43
+ }
44
+ };
66
45
  };
46
+ //#endregion
47
+ export { getHandshakeTiming, handshakeTimingMiddleware };