@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,17 +1,20 @@
1
1
  import { encodeHoneypotQuestion } from "../../../utils/honeypot/encoders.js";
2
2
  import { getRandomPhrase } from "../../../utils/honeypot/phraseBank.js";
3
- const HONEYPOT_HEADER = "x-prosopo-meta";
4
- const attachHoneypot = (res, clientRecord) => {
5
- const cfg = clientRecord.settings?.honeypot;
6
- if (!cfg?.enabled) return;
7
- const question = cfg.question ?? getRandomPhrase();
8
- if (!question) return;
9
- res.setHeader(
10
- HONEYPOT_HEADER,
11
- encodeHoneypotQuestion(question, cfg.encodingType)
12
- );
13
- };
14
- export {
15
- HONEYPOT_HEADER,
16
- attachHoneypot
3
+ //#region src/api/captcha/getFrictionlessCaptchaChallenge/honeypotResponse.ts
4
+ var HONEYPOT_HEADER = "x-prosopo-meta";
5
+ /**
6
+ * Sets the honeypot header on the response when the client has honeypot
7
+ * enabled. Header value is base64-wrapped morse/semaphore so an inspector
8
+ * doesn't immediately recognise the encoding shape. Falls back to a random
9
+ * phrase from the bank when no custom question is configured; no-op when
10
+ * the bank is empty or honeypot is disabled.
11
+ */
12
+ var attachHoneypot = (res, clientRecord) => {
13
+ const cfg = clientRecord.settings?.honeypot;
14
+ if (!cfg?.enabled) return;
15
+ const question = cfg.question ?? getRandomPhrase();
16
+ if (!question) return;
17
+ res.setHeader(HONEYPOT_HEADER, encodeHoneypotQuestion(question, cfg.encodingType));
17
18
  };
19
+ //#endregion
20
+ export { HONEYPOT_HEADER, attachHoneypot };
@@ -1,28 +1,28 @@
1
- const resolveSessionDedup = async (tasks, token, userSitekeyIpHash, logger) => {
2
- const [existingToken, existingSession] = await Promise.all([
3
- tasks.db.getSessionRecordByToken(token),
4
- tasks.db.getSessionByuserSitekeyIpHash(userSitekeyIpHash)
5
- ]);
6
- const dedup = existingSession ? {
7
- sessionId: existingSession.sessionId,
8
- captchaType: existingSession.captchaType,
9
- session: existingSession
10
- } : null;
11
- if (!dedup && tasks.writeQueue) {
12
- const stalePointer = await tasks.writeQueue.getCachedSessionByHash(userSitekeyIpHash);
13
- if (stalePointer) {
14
- logger.warn(() => ({
15
- msg: "Evicting stale Redis dedup pointer",
16
- data: { userSitekeyIpHash, staleSessionId: stalePointer }
17
- }));
18
- await Promise.all([
19
- tasks.writeQueue.invalidateCachedSessionByHash(userSitekeyIpHash),
20
- tasks.writeQueue.invalidateCachedSession(stalePointer)
21
- ]);
22
- }
23
- }
24
- return { existingToken, dedup };
25
- };
26
- export {
27
- resolveSessionDedup
1
+ //#region src/api/captcha/getFrictionlessCaptchaChallenge/sessionDedup.ts
2
+ var resolveSessionDedup = async (tasks, token, userSitekeyIpHash, logger) => {
3
+ const [existingToken, existingSession] = await Promise.all([tasks.db.getSessionRecordByToken(token), tasks.db.getSessionByuserSitekeyIpHash(userSitekeyIpHash)]);
4
+ const dedup = existingSession ? {
5
+ sessionId: existingSession.sessionId,
6
+ captchaType: existingSession.captchaType,
7
+ session: existingSession
8
+ } : null;
9
+ if (!dedup && tasks.writeQueue) {
10
+ const stalePointer = await tasks.writeQueue.getCachedSessionByHash(userSitekeyIpHash);
11
+ if (stalePointer) {
12
+ logger.warn(() => ({
13
+ msg: "Evicting stale Redis dedup pointer",
14
+ data: {
15
+ userSitekeyIpHash,
16
+ staleSessionId: stalePointer
17
+ }
18
+ }));
19
+ await Promise.all([tasks.writeQueue.invalidateCachedSessionByHash(userSitekeyIpHash), tasks.writeQueue.invalidateCachedSession(stalePointer)]);
20
+ }
21
+ }
22
+ return {
23
+ existingToken,
24
+ dedup
25
+ };
28
26
  };
27
+ //#endregion
28
+ export { resolveSessionDedup };
@@ -1,65 +1,44 @@
1
- import { CaptchaType } from "@prosopo/types";
2
- import { DEFAULT_FRICTIONLESS_THRESHOLD } from "./constants.js";
3
1
  import { attachHoneypot } from "./honeypotResponse.js";
4
- const runConfiguredCaptchaTypeShortCircuit = async (input, res) => {
5
- const configuredType = input.clientRecord.settings?.captchaType;
6
- if (!configuredType || configuredType === CaptchaType.frictionless) {
7
- return null;
8
- }
9
- const sessionParams = {
10
- token: input.token,
11
- score: 0,
12
- threshold: input.clientRecord.settings?.frictionlessThreshold ?? DEFAULT_FRICTIONLESS_THRESHOLD,
13
- scoreComponents: { baseScore: 0 },
14
- ipAddress: input.ipAddress,
15
- webView: false,
16
- iFrame: false,
17
- decryptedHeadHash: "",
18
- siteKey: input.dapp,
19
- ipInfo: input.ipInfo,
20
- headers: input.flatHeaders,
21
- mode: input.sessionMode,
22
- userSitekeyIpHash: input.userSitekeyIpHash,
23
- ...input.tcpToChelloUs !== void 0 && {
24
- tcpToChelloUs: input.tcpToChelloUs
25
- },
26
- ...input.chelloToHandshakeUs !== void 0 && {
27
- chelloToHandshakeUs: input.chelloToHandshakeUs
28
- }
29
- };
30
- input.logger.info(() => ({
31
- msg: "Frictionless decision",
32
- data: {
33
- decision: "configured_captcha_type",
34
- captchaType: configuredType
35
- }
36
- }));
37
- attachHoneypot(res, input.clientRecord);
38
- switch (configuredType) {
39
- case CaptchaType.image:
40
- return res.json(
41
- await input.tasks.frictionlessManager.sendImageCaptcha({
42
- ...sessionParams,
43
- solvedImagesCount: Math.min(
44
- input.env.config.captchas.solved.count,
45
- input.clientRecord.settings.imageMaxRounds
46
- )
47
- })
48
- );
49
- case CaptchaType.pow:
50
- return res.json(
51
- await input.tasks.frictionlessManager.sendPowCaptcha(sessionParams)
52
- );
53
- case CaptchaType.puzzle:
54
- return res.json(
55
- await input.tasks.frictionlessManager.sendPuzzleCaptcha(sessionParams)
56
- );
57
- default:
58
- throw new Error(
59
- `Unhandled configured captchaType in /frictionless short-circuit: ${configuredType}`
60
- );
61
- }
62
- };
63
- export {
64
- runConfiguredCaptchaTypeShortCircuit
2
+ import "./constants.js";
3
+ import { CaptchaType } from "@prosopo/types";
4
+ //#region src/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.ts
5
+ var runConfiguredCaptchaTypeShortCircuit = async (input, res) => {
6
+ const configuredType = input.clientRecord.settings?.captchaType;
7
+ if (!configuredType || configuredType === CaptchaType.frictionless) return null;
8
+ const sessionParams = {
9
+ token: input.token,
10
+ score: 0,
11
+ threshold: input.clientRecord.settings?.frictionlessThreshold ?? .5,
12
+ scoreComponents: { baseScore: 0 },
13
+ ipAddress: input.ipAddress,
14
+ webView: false,
15
+ iFrame: false,
16
+ decryptedHeadHash: "",
17
+ siteKey: input.dapp,
18
+ ipInfo: input.ipInfo,
19
+ headers: input.flatHeaders,
20
+ mode: input.sessionMode,
21
+ userSitekeyIpHash: input.userSitekeyIpHash,
22
+ ...input.tcpToChelloUs !== void 0 && { tcpToChelloUs: input.tcpToChelloUs },
23
+ ...input.chelloToHandshakeUs !== void 0 && { chelloToHandshakeUs: input.chelloToHandshakeUs }
24
+ };
25
+ input.logger.info(() => ({
26
+ msg: "Frictionless decision",
27
+ data: {
28
+ decision: "configured_captcha_type",
29
+ captchaType: configuredType
30
+ }
31
+ }));
32
+ attachHoneypot(res, input.clientRecord);
33
+ switch (configuredType) {
34
+ case CaptchaType.image: return res.json(await input.tasks.frictionlessManager.sendImageCaptcha({
35
+ ...sessionParams,
36
+ solvedImagesCount: Math.min(input.env.config.captchas.solved.count, input.clientRecord.settings.imageMaxRounds)
37
+ }));
38
+ case CaptchaType.pow: return res.json(await input.tasks.frictionlessManager.sendPowCaptcha(sessionParams));
39
+ case CaptchaType.puzzle: return res.json(await input.tasks.frictionlessManager.sendPuzzleCaptcha(sessionParams));
40
+ default: throw new Error(`Unhandled configured captchaType in /frictionless short-circuit: ${configuredType}`);
41
+ }
65
42
  };
43
+ //#endregion
44
+ export { runConfiguredCaptchaTypeShortCircuit };
@@ -1,4 +1,2 @@
1
- import { default as default2 } from "./getFrictionlessCaptchaChallenge/handler.js";
2
- export {
3
- default2 as default
4
- };
1
+ import handler_default from "./getFrictionlessCaptchaChallenge/handler.js";
2
+ export { handler_default as default };
@@ -1,210 +1,139 @@
1
- import { ProsopoApiError } from "@prosopo/common";
2
- import { parseCaptchaAssets } from "@prosopo/datasets";
3
- import { CaptchaRequestBody, CaptchaType, imageMaxRoundsDefault, SimdReadingsStage, ApiParams } from "@prosopo/types";
4
- import { getIPAddress, flatten } from "@prosopo/util";
5
- import "../../tasks/index.js";
6
- import { normalizeRequestIp } from "../../utils/normalizeRequestIp.js";
7
- import { getMaintenanceMode } from "../admin/apiToggleMaintenanceModeEndpoint.js";
1
+ import { validateAddr, validateSiteKey } from "../validateAddress.js";
2
+ import { recordCaptchaIssueError, recordCaptchaIssued } from "../metrics.js";
8
3
  import { getRequestUserScope } from "../blacklistRequestInspector.js";
9
- import { recordCaptchaIssued, recordCaptchaIssueError } from "../metrics.js";
10
- import { validateSiteKey, validateAddr } from "../validateAddress.js";
11
- import { buildImageMaintenanceResponse } from "./maintenanceModeResponses.js";
12
4
  import { Tasks } from "../../tasks/tasks.js";
13
- const getImageCaptchaChallenge = (env, userAccessRulesStorage) => async (req, res, next) => {
14
- let parsed;
15
- const normalizedIp = normalizeRequestIp(req.ip, req.logger);
16
- if (!normalizedIp) {
17
- return next(
18
- new ProsopoApiError("API.BAD_REQUEST", {
19
- context: { code: 400, error: "IP address not found" },
20
- i18n: req.i18n,
21
- logger: req.logger
22
- })
23
- );
24
- }
25
- const ipAddress = getIPAddress(normalizedIp);
26
- try {
27
- parsed = CaptchaRequestBody.parse(req.body);
28
- } catch (err) {
29
- return next(
30
- new ProsopoApiError("CAPTCHA.PARSE_ERROR", {
31
- context: { code: 400, error: err },
32
- i18n: req.i18n,
33
- logger: req.logger
34
- })
35
- );
36
- }
37
- const {
38
- datasetId: clientDatasetId,
39
- user,
40
- dapp,
41
- sessionId,
42
- simdReadings
43
- } = parsed;
44
- const datasetId = clientDatasetId ?? env.datasetId;
45
- if (!datasetId) {
46
- return next(
47
- new ProsopoApiError("API.BAD_REQUEST", {
48
- context: {
49
- code: 400,
50
- error: "No dataset available. Please upload a dataset first."
51
- },
52
- i18n: req.i18n,
53
- logger: req.logger
54
- })
55
- );
56
- }
57
- validateSiteKey(dapp);
58
- validateAddr(user);
59
- if (getMaintenanceMode()) {
60
- req.logger.info(() => ({
61
- msg: "Maintenance mode active - returning dummy image challenge",
62
- data: { dapp, user, sessionId }
63
- }));
64
- return res.json(buildImageMaintenanceResponse());
65
- }
66
- const tasks = new Tasks(env, req.logger);
67
- try {
68
- const clientRecord = await tasks.db.getClientRecord(dapp);
69
- if (!clientRecord) {
70
- return next(
71
- new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
72
- context: { code: 400, siteKey: dapp },
73
- i18n: req.i18n,
74
- logger: req.logger
75
- })
76
- );
77
- }
78
- const countryCode = req.ipInfo && "isValid" in req.ipInfo && req.ipInfo.isValid ? req.ipInfo.countryCode : void 0;
79
- const asn = req.ipInfo && "isValid" in req.ipInfo && req.ipInfo.isValid ? req.ipInfo.asnNumber : void 0;
80
- const userScope = getRequestUserScope(
81
- flatten(req.headers),
82
- req.ja4,
83
- normalizedIp,
84
- user,
85
- void 0,
86
- // headHash
87
- void 0,
88
- // coords
89
- countryCode,
90
- asn
91
- );
92
- const userAccessPolicy = (await tasks.imgCaptchaManager.getPrioritisedAccessPolicies(
93
- userAccessRulesStorage,
94
- dapp,
95
- userScope
96
- )).find((p) => !p.deferToVerify);
97
- const {
98
- valid,
99
- reason,
100
- sessionId: validSessionId,
101
- solvedImagesCount
102
- } = await tasks.imgCaptchaManager.isValidRequest(
103
- clientRecord,
104
- CaptchaType.image,
105
- env,
106
- sessionId,
107
- userAccessPolicy,
108
- normalizedIp
109
- );
110
- if (!valid) {
111
- return next(
112
- new ProsopoApiError(reason || "API.BAD_REQUEST", {
113
- context: {
114
- code: 400,
115
- siteKey: dapp,
116
- user
117
- },
118
- i18n: req.i18n,
119
- logger: req.logger
120
- })
121
- );
122
- }
123
- const captchaConfig = {
124
- solved: {
125
- count: Math.min(
126
- solvedImagesCount || userAccessPolicy?.solvedImagesCount || env.config.captchas.solved.count,
127
- clientRecord.settings.imageMaxRounds ?? imageMaxRoundsDefault
128
- )
129
- },
130
- unsolved: {
131
- count: userAccessPolicy?.unsolvedImagesCount || env.config.captchas.unsolved.count
132
- }
133
- };
134
- if (validSessionId && simdReadings) {
135
- await tasks.frictionlessManager.decryptAndAttachSimdReadingsIfAbsent(
136
- validSessionId,
137
- simdReadings,
138
- SimdReadingsStage.challenge
139
- ).catch((updateErr) => {
140
- req.logger.warn(() => ({
141
- err: updateErr,
142
- msg: "Failed to patch session with SIMD readings on image challenge"
143
- }));
144
- });
145
- }
146
- const taskData = await tasks.imgCaptchaManager.getRandomCaptchasAndRequestHash(
147
- datasetId,
148
- user,
149
- ipAddress,
150
- captchaConfig,
151
- clientRecord.settings.imageThreshold ?? 0.8,
152
- validSessionId,
153
- // Persist the full ipinfo payload — consumers read
154
- // individual flags off this object instead of separate
155
- // flat fields.
156
- req.ipInfo
157
- );
158
- const captchaResponse = {
159
- [ApiParams.status]: "ok",
160
- [ApiParams.captchas]: taskData.captchas.map((captcha) => ({
161
- ...captcha,
162
- target: req.t(`TARGET.${captcha.target}`),
163
- items: captcha.items.map(
164
- (item) => parseCaptchaAssets(item, env.assetsResolver)
165
- )
166
- })),
167
- [ApiParams.requestHash]: taskData.requestHash,
168
- [ApiParams.timestamp]: taskData.timestamp.toString(),
169
- [ApiParams.signature]: {
170
- [ApiParams.provider]: {
171
- [ApiParams.requestHash]: taskData.signedRequestHash
172
- }
173
- }
174
- };
175
- req.logger.info(() => ({
176
- msg: "Image captcha challenge issued",
177
- data: {
178
- captchaType: CaptchaType.image,
179
- requestHash: taskData.requestHash,
180
- solvedImagesCount: captchaConfig.solved.count,
181
- user,
182
- dapp,
183
- sessionId
184
- }
185
- }));
186
- recordCaptchaIssued(CaptchaType.image);
187
- return res.json(captchaResponse);
188
- } catch (err) {
189
- recordCaptchaIssueError(CaptchaType.image);
190
- req.logger.error(() => ({
191
- err,
192
- data: req.params,
193
- msg: "Error in image captcha challenge request"
194
- }));
195
- return next(
196
- new ProsopoApiError("API.BAD_REQUEST", {
197
- context: {
198
- error: err,
199
- code: 500,
200
- params: req.params
201
- },
202
- i18n: req.i18n,
203
- logger: req.logger
204
- })
205
- );
206
- }
207
- };
208
- export {
209
- getImageCaptchaChallenge as default
5
+ import { getMaintenanceMode } from "../admin/apiToggleMaintenanceModeEndpoint.js";
6
+ import { normalizeRequestIp } from "../../utils/normalizeRequestIp.js";
7
+ import { buildImageMaintenanceResponse } from "./maintenanceModeResponses.js";
8
+ import { ProsopoApiError } from "@prosopo/common";
9
+ import { ApiParams, CaptchaRequestBody, CaptchaType, SimdReadingsStage, imageMaxRoundsDefault } from "@prosopo/types";
10
+ import { flatten, getIPAddress } from "@prosopo/util";
11
+ import { parseCaptchaAssets } from "@prosopo/datasets";
12
+ //#region src/api/captcha/getImageCaptchaChallenge.ts
13
+ var getImageCaptchaChallenge_default = (env, userAccessRulesStorage) => async (req, res, next) => {
14
+ let parsed;
15
+ const normalizedIp = normalizeRequestIp(req.ip, req.logger);
16
+ if (!normalizedIp) return next(new ProsopoApiError("API.BAD_REQUEST", {
17
+ context: {
18
+ code: 400,
19
+ error: "IP address not found"
20
+ },
21
+ i18n: req.i18n,
22
+ logger: req.logger
23
+ }));
24
+ const ipAddress = getIPAddress(normalizedIp);
25
+ try {
26
+ parsed = CaptchaRequestBody.parse(req.body);
27
+ } catch (err) {
28
+ return next(new ProsopoApiError("CAPTCHA.PARSE_ERROR", {
29
+ context: {
30
+ code: 400,
31
+ error: err
32
+ },
33
+ i18n: req.i18n,
34
+ logger: req.logger
35
+ }));
36
+ }
37
+ const { datasetId: clientDatasetId, user, dapp, sessionId, simdReadings } = parsed;
38
+ const datasetId = clientDatasetId ?? env.datasetId;
39
+ if (!datasetId) return next(new ProsopoApiError("API.BAD_REQUEST", {
40
+ context: {
41
+ code: 400,
42
+ error: "No dataset available. Please upload a dataset first."
43
+ },
44
+ i18n: req.i18n,
45
+ logger: req.logger
46
+ }));
47
+ validateSiteKey(dapp);
48
+ validateAddr(user);
49
+ if (getMaintenanceMode()) {
50
+ req.logger.info(() => ({
51
+ msg: "Maintenance mode active - returning dummy image challenge",
52
+ data: {
53
+ dapp,
54
+ user,
55
+ sessionId
56
+ }
57
+ }));
58
+ return res.json(buildImageMaintenanceResponse());
59
+ }
60
+ const tasks = new Tasks(env, req.logger);
61
+ try {
62
+ const clientRecord = await tasks.db.getClientRecord(dapp);
63
+ if (!clientRecord) return next(new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
64
+ context: {
65
+ code: 400,
66
+ siteKey: dapp
67
+ },
68
+ i18n: req.i18n,
69
+ logger: req.logger
70
+ }));
71
+ const countryCode = req.ipInfo && "isValid" in req.ipInfo && req.ipInfo.isValid ? req.ipInfo.countryCode : void 0;
72
+ const asn = req.ipInfo && "isValid" in req.ipInfo && req.ipInfo.isValid ? req.ipInfo.asnNumber : void 0;
73
+ const userScope = getRequestUserScope(flatten(req.headers), req.ja4, normalizedIp, user, void 0, void 0, countryCode, asn);
74
+ const userAccessPolicy = (await tasks.imgCaptchaManager.getPrioritisedAccessPolicies(userAccessRulesStorage, dapp, userScope)).find((p) => !p.deferToVerify);
75
+ const { valid, reason, sessionId: validSessionId, solvedImagesCount } = await tasks.imgCaptchaManager.isValidRequest(clientRecord, CaptchaType.image, env, sessionId, userAccessPolicy, normalizedIp);
76
+ if (!valid) return next(new ProsopoApiError(reason || "API.BAD_REQUEST", {
77
+ context: {
78
+ code: 400,
79
+ siteKey: dapp,
80
+ user
81
+ },
82
+ i18n: req.i18n,
83
+ logger: req.logger
84
+ }));
85
+ const captchaConfig = {
86
+ solved: { count: Math.min(solvedImagesCount || userAccessPolicy?.solvedImagesCount || env.config.captchas.solved.count, clientRecord.settings.imageMaxRounds ?? imageMaxRoundsDefault) },
87
+ unsolved: { count: userAccessPolicy?.unsolvedImagesCount || env.config.captchas.unsolved.count }
88
+ };
89
+ if (validSessionId && simdReadings) await tasks.frictionlessManager.decryptAndAttachSimdReadingsIfAbsent(validSessionId, simdReadings, SimdReadingsStage.challenge).catch((updateErr) => {
90
+ req.logger.warn(() => ({
91
+ err: updateErr,
92
+ msg: "Failed to patch session with SIMD readings on image challenge"
93
+ }));
94
+ });
95
+ const taskData = await tasks.imgCaptchaManager.getRandomCaptchasAndRequestHash(datasetId, user, ipAddress, captchaConfig, clientRecord.settings.imageThreshold ?? .8, validSessionId, req.ipInfo);
96
+ const captchaResponse = {
97
+ [ApiParams.status]: "ok",
98
+ [ApiParams.captchas]: taskData.captchas.map((captcha) => ({
99
+ ...captcha,
100
+ target: req.t(`TARGET.${captcha.target}`),
101
+ items: captcha.items.map((item) => parseCaptchaAssets(item, env.assetsResolver))
102
+ })),
103
+ [ApiParams.requestHash]: taskData.requestHash,
104
+ [ApiParams.timestamp]: taskData.timestamp.toString(),
105
+ [ApiParams.signature]: { [ApiParams.provider]: { [ApiParams.requestHash]: taskData.signedRequestHash } }
106
+ };
107
+ req.logger.info(() => ({
108
+ msg: "Image captcha challenge issued",
109
+ data: {
110
+ captchaType: CaptchaType.image,
111
+ requestHash: taskData.requestHash,
112
+ solvedImagesCount: captchaConfig.solved.count,
113
+ user,
114
+ dapp,
115
+ sessionId
116
+ }
117
+ }));
118
+ recordCaptchaIssued(CaptchaType.image);
119
+ return res.json(captchaResponse);
120
+ } catch (err) {
121
+ recordCaptchaIssueError(CaptchaType.image);
122
+ req.logger.error(() => ({
123
+ err,
124
+ data: req.params,
125
+ msg: "Error in image captcha challenge request"
126
+ }));
127
+ return next(new ProsopoApiError("API.BAD_REQUEST", {
128
+ context: {
129
+ error: err,
130
+ code: 500,
131
+ params: req.params
132
+ },
133
+ i18n: req.i18n,
134
+ logger: req.logger
135
+ }));
136
+ }
210
137
  };
138
+ //#endregion
139
+ export { getImageCaptchaChallenge_default as default };