@prosopo/provider 5.13.4 → 5.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/.turbo/turbo-build$colon$cjs.log +30 -30
  2. package/.turbo/turbo-build$colon$tsc.log +27 -30
  3. package/.turbo/turbo-build.log +30 -33
  4. package/CHANGELOG.md +233 -0
  5. package/LICENSE +201 -0
  6. package/dist/api/adminAuthOptions.d.ts +4 -0
  7. package/dist/api/adminAuthOptions.d.ts.map +1 -0
  8. package/dist/api/adminAuthOptions.js +34 -0
  9. package/dist/api/adminAuthOptions.js.map +1 -0
  10. package/dist/api/captcha/getFrictionlessCaptchaChallenge/constants.d.ts +2 -1
  11. package/dist/api/captcha/getFrictionlessCaptchaChallenge/constants.d.ts.map +1 -1
  12. package/dist/api/captcha/getFrictionlessCaptchaChallenge/constants.js +11 -2
  13. package/dist/api/captcha/getFrictionlessCaptchaChallenge/constants.js.map +1 -1
  14. package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.d.ts.map +1 -1
  15. package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js +16 -11
  16. package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js.map +1 -1
  17. package/dist/api/captcha/getFrictionlessCaptchaChallenge/refreshLineage.d.ts +5 -0
  18. package/dist/api/captcha/getFrictionlessCaptchaChallenge/refreshLineage.d.ts.map +1 -0
  19. package/dist/api/captcha/getFrictionlessCaptchaChallenge/refreshLineage.js +20 -0
  20. package/dist/api/captcha/getFrictionlessCaptchaChallenge/refreshLineage.js.map +1 -0
  21. package/dist/api/captcha/getPuzzleCaptchaChallenge.d.ts.map +1 -1
  22. package/dist/api/captcha/getPuzzleCaptchaChallenge.js +24 -0
  23. package/dist/api/captcha/getPuzzleCaptchaChallenge.js.map +1 -1
  24. package/dist/api/captcha/submitPoWCaptchaSolution.d.ts +4 -11
  25. package/dist/api/captcha/submitPoWCaptchaSolution.d.ts.map +1 -1
  26. package/dist/api/captcha/submitPoWCaptchaSolution.js +12 -17
  27. package/dist/api/captcha/submitPoWCaptchaSolution.js.map +1 -1
  28. package/dist/api/detectorPoolBodyParser.d.ts +4 -0
  29. package/dist/api/detectorPoolBodyParser.d.ts.map +1 -0
  30. package/dist/api/detectorPoolBodyParser.js +31 -0
  31. package/dist/api/detectorPoolBodyParser.js.map +1 -0
  32. package/dist/api/domainMiddleware.d.ts.map +1 -1
  33. package/dist/api/domainMiddleware.js +5 -1
  34. package/dist/api/domainMiddleware.js.map +1 -1
  35. package/dist/api/metrics.d.ts +2 -1
  36. package/dist/api/metrics.d.ts.map +1 -1
  37. package/dist/api/metrics.js +46 -4
  38. package/dist/api/metrics.js.map +1 -1
  39. package/dist/api/rawTlsSignalsMiddleware.d.ts +14 -1
  40. package/dist/api/rawTlsSignalsMiddleware.d.ts.map +1 -1
  41. package/dist/api/rawTlsSignalsMiddleware.js +111 -23
  42. package/dist/api/rawTlsSignalsMiddleware.js.map +1 -1
  43. package/dist/api/startProviderApi.d.ts.map +1 -1
  44. package/dist/api/startProviderApi.js +8 -5
  45. package/dist/api/startProviderApi.js.map +1 -1
  46. package/dist/api/verify.d.ts.map +1 -1
  47. package/dist/api/verify.js +50 -8
  48. package/dist/api/verify.js.map +1 -1
  49. package/dist/cjs/api/adminAuthOptions.cjs +35 -0
  50. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/constants.cjs +10 -0
  51. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/handler.cjs +14 -8
  52. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/refreshLineage.cjs +20 -0
  53. package/dist/cjs/api/captcha/getPuzzleCaptchaChallenge.cjs +24 -0
  54. package/dist/cjs/api/captcha/submitPoWCaptchaSolution.cjs +10 -15
  55. package/dist/cjs/api/detectorPoolBodyParser.cjs +33 -0
  56. package/dist/cjs/api/domainMiddleware.cjs +5 -1
  57. package/dist/cjs/api/metrics.cjs +46 -3
  58. package/dist/cjs/api/rawTlsSignalsMiddleware.cjs +111 -22
  59. package/dist/cjs/api/startProviderApi.cjs +8 -5
  60. package/dist/cjs/api/verify.cjs +50 -8
  61. package/dist/cjs/compositeIpAddress.cjs +21 -7
  62. package/dist/cjs/tasks/captchaManager.cjs +15 -0
  63. package/dist/cjs/tasks/captchaTypeSelection.cjs +23 -2
  64. package/dist/cjs/tasks/client/clientTasks.cjs +25 -4
  65. package/dist/cjs/tasks/dataset/datasetTasksUtils.cjs +1 -1
  66. package/dist/cjs/tasks/frictionless/frictionlessTasks.cjs +26 -23
  67. package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasks.cjs +4 -11
  68. package/dist/cjs/tasks/powCaptcha/powTasks.cjs +7 -11
  69. package/dist/cjs/tasks/powCaptcha/powTasksUtils.cjs +2 -0
  70. package/dist/cjs/tasks/puzzle/backgroundBuffer.cjs +3 -3
  71. package/dist/cjs/tasks/puzzle/nativeBackground.cjs +12 -5
  72. package/dist/cjs/tasks/puzzle/puzzleRenderer.cjs +4 -3
  73. package/dist/cjs/tasks/puzzleCaptcha/puzzleTasks.cjs +2 -9
  74. package/dist/cjs/utils/tarpitPadding.cjs +81 -10
  75. package/dist/compositeIpAddress.d.ts.map +1 -1
  76. package/dist/compositeIpAddress.js +21 -7
  77. package/dist/compositeIpAddress.js.map +1 -1
  78. package/dist/tasks/captchaManager.d.ts.map +1 -1
  79. package/dist/tasks/captchaManager.js +16 -1
  80. package/dist/tasks/captchaManager.js.map +1 -1
  81. package/dist/tasks/captchaTypeSelection.d.ts +3 -1
  82. package/dist/tasks/captchaTypeSelection.d.ts.map +1 -1
  83. package/dist/tasks/captchaTypeSelection.js +23 -3
  84. package/dist/tasks/captchaTypeSelection.js.map +1 -1
  85. package/dist/tasks/client/clientTasks.d.ts +1 -0
  86. package/dist/tasks/client/clientTasks.d.ts.map +1 -1
  87. package/dist/tasks/client/clientTasks.js +25 -5
  88. package/dist/tasks/client/clientTasks.js.map +1 -1
  89. package/dist/tasks/dataset/datasetTasksUtils.js +1 -1
  90. package/dist/tasks/dataset/datasetTasksUtils.js.map +1 -1
  91. package/dist/tasks/frictionless/frictionlessTasks.d.ts +3 -1
  92. package/dist/tasks/frictionless/frictionlessTasks.d.ts.map +1 -1
  93. package/dist/tasks/frictionless/frictionlessTasks.js +28 -25
  94. package/dist/tasks/frictionless/frictionlessTasks.js.map +1 -1
  95. package/dist/tasks/frictionless/routingMachine.d.ts +1 -2
  96. package/dist/tasks/frictionless/routingMachine.d.ts.map +1 -1
  97. package/dist/tasks/frictionless/routingMachine.js.map +1 -1
  98. package/dist/tasks/imgCaptcha/imgCaptchaTasks.d.ts.map +1 -1
  99. package/dist/tasks/imgCaptcha/imgCaptchaTasks.js +4 -11
  100. package/dist/tasks/imgCaptcha/imgCaptchaTasks.js.map +1 -1
  101. package/dist/tasks/powCaptcha/powTasks.d.ts.map +1 -1
  102. package/dist/tasks/powCaptcha/powTasks.js +8 -12
  103. package/dist/tasks/powCaptcha/powTasks.js.map +1 -1
  104. package/dist/tasks/powCaptcha/powTasksUtils.d.ts +1 -0
  105. package/dist/tasks/powCaptcha/powTasksUtils.d.ts.map +1 -1
  106. package/dist/tasks/powCaptcha/powTasksUtils.js +2 -1
  107. package/dist/tasks/powCaptcha/powTasksUtils.js.map +1 -1
  108. package/dist/tasks/puzzle/backgroundBuffer.d.ts +3 -2
  109. package/dist/tasks/puzzle/backgroundBuffer.d.ts.map +1 -1
  110. package/dist/tasks/puzzle/backgroundBuffer.js +3 -3
  111. package/dist/tasks/puzzle/backgroundBuffer.js.map +1 -1
  112. package/dist/tasks/puzzle/nativeBackground.d.ts +5 -1
  113. package/dist/tasks/puzzle/nativeBackground.d.ts.map +1 -1
  114. package/dist/tasks/puzzle/nativeBackground.js +12 -5
  115. package/dist/tasks/puzzle/nativeBackground.js.map +1 -1
  116. package/dist/tasks/puzzle/puzzleRenderer.d.ts +2 -0
  117. package/dist/tasks/puzzle/puzzleRenderer.d.ts.map +1 -1
  118. package/dist/tasks/puzzle/puzzleRenderer.js +4 -3
  119. package/dist/tasks/puzzle/puzzleRenderer.js.map +1 -1
  120. package/dist/tasks/puzzleCaptcha/puzzleTasks.d.ts.map +1 -1
  121. package/dist/tasks/puzzleCaptcha/puzzleTasks.js +2 -9
  122. package/dist/tasks/puzzleCaptcha/puzzleTasks.js.map +1 -1
  123. package/dist/tests/integration/clientSettingsPersistence.integration.test.js +2 -0
  124. package/dist/tests/integration/clientSettingsPersistence.integration.test.js.map +1 -1
  125. package/dist/tests/unit/api/adminAuthOptions.unit.test.d.ts +2 -0
  126. package/dist/tests/unit/api/adminAuthOptions.unit.test.d.ts.map +1 -0
  127. package/dist/tests/unit/api/adminAuthOptions.unit.test.js +38 -0
  128. package/dist/tests/unit/api/adminAuthOptions.unit.test.js.map +1 -0
  129. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/constants.unit.test.js +35 -2
  130. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/constants.unit.test.js.map +1 -1
  131. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/refreshLineage.unit.test.d.ts +2 -0
  132. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/refreshLineage.unit.test.d.ts.map +1 -0
  133. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/refreshLineage.unit.test.js +45 -0
  134. package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/refreshLineage.unit.test.js.map +1 -0
  135. package/dist/tests/unit/api/captcha/submitPoWCaptchaSolution.escalation.unit.test.js +15 -1
  136. package/dist/tests/unit/api/captcha/submitPoWCaptchaSolution.escalation.unit.test.js.map +1 -1
  137. package/dist/tests/unit/api/detectorPoolBodyParser.unit.test.d.ts +2 -0
  138. package/dist/tests/unit/api/detectorPoolBodyParser.unit.test.d.ts.map +1 -0
  139. package/dist/tests/unit/api/detectorPoolBodyParser.unit.test.js +64 -0
  140. package/dist/tests/unit/api/detectorPoolBodyParser.unit.test.js.map +1 -0
  141. package/dist/tests/unit/api/domainMiddlewareErrorBody.unit.test.d.ts +2 -0
  142. package/dist/tests/unit/api/domainMiddlewareErrorBody.unit.test.d.ts.map +1 -0
  143. package/dist/tests/unit/api/domainMiddlewareErrorBody.unit.test.js +67 -0
  144. package/dist/tests/unit/api/domainMiddlewareErrorBody.unit.test.js.map +1 -0
  145. package/dist/tests/unit/api/getFrictionlessCaptchaChallenge.unit.test.js +174 -23
  146. package/dist/tests/unit/api/getFrictionlessCaptchaChallenge.unit.test.js.map +1 -1
  147. package/dist/tests/unit/api/metricsAccess.unit.test.d.ts +2 -0
  148. package/dist/tests/unit/api/metricsAccess.unit.test.d.ts.map +1 -0
  149. package/dist/tests/unit/api/metricsAccess.unit.test.js +87 -0
  150. package/dist/tests/unit/api/metricsAccess.unit.test.js.map +1 -0
  151. package/dist/tests/unit/api/public.unit.test.js +4 -1
  152. package/dist/tests/unit/api/public.unit.test.js.map +1 -1
  153. package/dist/tests/unit/api/rawTlsSignalsMiddleware.unit.test.js +96 -1
  154. package/dist/tests/unit/api/rawTlsSignalsMiddleware.unit.test.js.map +1 -1
  155. package/dist/tests/unit/api/validateAddress.unit.test.js +1 -1
  156. package/dist/tests/unit/api/validateAddress.unit.test.js.map +1 -1
  157. package/dist/tests/unit/api/verifyBadDappSignature.unit.test.d.ts +2 -0
  158. package/dist/tests/unit/api/verifyBadDappSignature.unit.test.d.ts.map +1 -0
  159. package/dist/tests/unit/api/verifyBadDappSignature.unit.test.js +96 -0
  160. package/dist/tests/unit/api/verifyBadDappSignature.unit.test.js.map +1 -0
  161. package/dist/tests/unit/api/verifyMalformedToken.unit.test.d.ts +2 -0
  162. package/dist/tests/unit/api/verifyMalformedToken.unit.test.d.ts.map +1 -0
  163. package/dist/tests/unit/api/verifyMalformedToken.unit.test.js +89 -0
  164. package/dist/tests/unit/api/verifyMalformedToken.unit.test.js.map +1 -0
  165. package/dist/tests/unit/compositeIpAddress.unit.test.js +12 -3
  166. package/dist/tests/unit/compositeIpAddress.unit.test.js.map +1 -1
  167. package/dist/tests/unit/tasks/captchaManager.unit.test.js +51 -0
  168. package/dist/tests/unit/tasks/captchaManager.unit.test.js.map +1 -1
  169. package/dist/tests/unit/tasks/captchaTypeSelection.unit.test.js +20 -1
  170. package/dist/tests/unit/tasks/captchaTypeSelection.unit.test.js.map +1 -1
  171. package/dist/tests/unit/tasks/client/clientTasks.unit.test.js +33 -1
  172. package/dist/tests/unit/tasks/client/clientTasks.unit.test.js.map +1 -1
  173. package/dist/tests/unit/tasks/dataset/datasetTasksUtils.unit.test.js +14 -1
  174. package/dist/tests/unit/tasks/dataset/datasetTasksUtils.unit.test.js.map +1 -1
  175. package/dist/tests/unit/tasks/frictionless/allowedCaptchaTypes.unit.test.d.ts +2 -0
  176. package/dist/tests/unit/tasks/frictionless/allowedCaptchaTypes.unit.test.d.ts.map +1 -0
  177. package/dist/tests/unit/tasks/frictionless/allowedCaptchaTypes.unit.test.js +94 -0
  178. package/dist/tests/unit/tasks/frictionless/allowedCaptchaTypes.unit.test.js.map +1 -0
  179. package/dist/tests/unit/tasks/frictionless/refreshSwitch.unit.test.d.ts +2 -0
  180. package/dist/tests/unit/tasks/frictionless/refreshSwitch.unit.test.d.ts.map +1 -0
  181. package/dist/tests/unit/tasks/frictionless/refreshSwitch.unit.test.js +103 -0
  182. package/dist/tests/unit/tasks/frictionless/refreshSwitch.unit.test.js.map +1 -0
  183. package/dist/tests/unit/tasks/powCaptcha/powTasksUtils.unit.test.js +23 -1
  184. package/dist/tests/unit/tasks/powCaptcha/powTasksUtils.unit.test.js.map +1 -1
  185. package/dist/tests/unit/tasks/puzzle/backgroundBuffer.unit.test.js +1 -4
  186. package/dist/tests/unit/tasks/puzzle/backgroundBuffer.unit.test.js.map +1 -1
  187. package/dist/tests/unit/tasks/puzzle/nativeBackground.unit.test.js +10 -3
  188. package/dist/tests/unit/tasks/puzzle/nativeBackground.unit.test.js.map +1 -1
  189. package/dist/tests/unit/tasks/puzzle/puzzleRenderer.unit.test.d.ts +2 -0
  190. package/dist/tests/unit/tasks/puzzle/puzzleRenderer.unit.test.d.ts.map +1 -0
  191. package/dist/tests/unit/tasks/puzzle/puzzleRenderer.unit.test.js +35 -0
  192. package/dist/tests/unit/tasks/puzzle/puzzleRenderer.unit.test.js.map +1 -0
  193. package/dist/tests/unit/tasks/puzzleDifficultyDeviceCap.unit.test.d.ts +2 -0
  194. package/dist/tests/unit/tasks/puzzleDifficultyDeviceCap.unit.test.d.ts.map +1 -0
  195. package/dist/tests/unit/tasks/puzzleDifficultyDeviceCap.unit.test.js +49 -0
  196. package/dist/tests/unit/tasks/puzzleDifficultyDeviceCap.unit.test.js.map +1 -0
  197. package/dist/tests/unit/testUtils/mockProviderEnv.d.ts +17 -14
  198. package/dist/tests/unit/testUtils/mockProviderEnv.d.ts.map +1 -1
  199. package/dist/tests/unit/testUtils/mockProviderEnv.js.map +1 -1
  200. package/dist/tests/unit/utils/tarpitPadding.unit.test.js +118 -36
  201. package/dist/tests/unit/utils/tarpitPadding.unit.test.js.map +1 -1
  202. package/dist/utils/tarpitPadding.d.ts.map +1 -1
  203. package/dist/utils/tarpitPadding.js +81 -10
  204. package/dist/utils/tarpitPadding.js.map +1 -1
  205. package/package.json +105 -103
package/CHANGELOG.md CHANGED
@@ -1,5 +1,238 @@
1
1
  # @prosopo/provider
2
2
 
3
+ ## 5.15.0
4
+ ### Minor Changes
5
+
6
+ - 1c13037: Add `captchaTypeFeatureFlags` to site settings, starting with `puzzle`. Setting
7
+ `puzzle: false` stops a site being served the puzzle captcha by any path: its
8
+ configured `captchaType`, `frictionlessTypes`, access policies, the traffic
9
+ filter, routing machines, PoW escalation, and puzzle sessions that already
10
+ exist. A site pinned to puzzle gets an image captcha instead (or PoW if image is
11
+ also off). The field is optional and has no stored default, so sites that never
12
+ set it behave exactly as before. The flag is meant for Prosopo staff, not site
13
+ owners.
14
+ - f961dab: Store everything needed to redraw a puzzle, so the portal can show the exact challenge a user was served.
15
+
16
+ Until now a puzzle record kept the target, the origin and the accuracy required (`tolerance`), and
17
+ nothing else. The things that decide what the puzzle actually looked like — how many decoy shapes were
18
+ scattered on it, how dark they were, how big the piece was, and which background was used — were
19
+ picked per challenge and then thrown away. So you could see that someone failed by 11px and had no way
20
+ to know whether they had been handed an easy puzzle or a nasty one.
21
+
22
+ Puzzle records now carry a `render` object with all of it: the difficulty ladder level, the piece size,
23
+ the frame geometry, the five render settings, and the two 128-bit seeds that regenerate the imagery.
24
+ Given those, the portal reproduces the background and the piece byte for byte.
25
+
26
+ To make that possible the renderer had to become reproducible. `renderPuzzle` and `createBackground`
27
+ now take an optional seed and report the one they used, instead of generating one internally and
28
+ discarding it; the background pool hands out each image together with its seed. Default behaviour is
29
+ unchanged — omit the seed and you still get a fresh random one.
30
+
31
+ The ladder level is new on the session too (`puzzleLevel`), because it was computed when the puzzle
32
+ was escalated and never written down anywhere. It is absent when the ladder did not choose the
33
+ puzzle — a site-configured puzzle or a traffic-filter override has no level.
34
+
35
+ The record is patched after the imagery is produced rather than written with the rest of the record,
36
+ because the piece size and the seeds only exist once the render has run, and the target has to be
37
+ durable before it is expressed in pixels. If that patch fails it is logged and ignored: losing a
38
+ diagnostic must not cost the user their solve. Records written before this change, and any challenge
39
+ whose render threw, simply have no `render`.
40
+
41
+ **The seeds are secret.** `puzzle-assets/src/prng.ts` explains why: they regenerate the clean
42
+ background, and a clean background can be diffed against the composite to read the target position
43
+ straight off. They were previously barred from leaving the provider entirely. They may now sit on the
44
+ captcha record, and nowhere else — not in a response, not in a log, not on the session. Anything that
45
+ exposes a puzzle record to a caller has to drop `render.backgroundSeed` and `render.renderSeed`, and
46
+ must not serve them at all while the challenge is still live. The comment in `prng.ts` now says so.
47
+
48
+ Test coverage. Three guards, each confirmed to fail when the thing it protects is broken:
49
+ `renderReplay.unit.test.ts` re-renders from the stored fields and asserts the bytes match, with one
50
+ case per field asserting that changing *only* that field changes the output — so a record missing a
51
+ field cannot pass; `puzzleRenderer.unit.test.ts` replays through the same entry points the portal would
52
+ use, which catches a background paired with the wrong seed; and `schemas.test.ts` asserts every
53
+ sub-field survives a strict-mode mongoose write, since a field absent from the schema is dropped
54
+ silently and would store nothing at all.
55
+ - 1ce9482: Cap automatic puzzle escalation one rung lower on touch devices than on pointer devices.
56
+
57
+ The puzzle's `tolerance` is an absolute CSS-pixel target and the widget renders into a fixed 300x200 container on every device, so a difficulty level asks the same placement accuracy of a fingertip as it does of a mouse.
58
+
59
+ `MAX_AUTO_ESCALATION_LEVEL_TOUCH` and `resolveMaxEscalationLevel(siteMaxLevel, isTouch)` are new in `@prosopo/captcha-severity`. The resolver takes the stricter of the site's own `puzzleMaxDifficulty` and the device ceiling, so a site set to 3 or 4 cannot out-vote the touch ceiling and a site pinned to 0 is not raised by it.
60
+
61
+ The provider now calls it from both places a puzzle difficulty is chosen: `sendCaptcha`, which reads `routingContext.platform.isMobile`, and the post-PoW escalation in `submitPoWCaptchaSolution`, which derives the platform from the originating session. That second path previously ignored the site's `puzzleMaxDifficulty` entirely and always clamped to the global ceiling; it now respects both caps.
62
+
63
+ Desktop behaviour is unchanged, and L4 stays unreachable by automatic escalation on either device.
64
+ - dcb691b: Add a refresh control to the puzzle captcha.
65
+
66
+ A user who can't solve the puzzle they were given can now ask for a different one, from a button in the puzzle's header. The replacement comes through a new frictionless session, like a wrong answer already does.
67
+
68
+ The widget tells the provider which session was refreshed (`refreshOf`). The provider then records `refreshOf`, `refreshCount` and `refreshedAfterMs` on the new session, so refresh behaviour can be scored later. After three refreshes in a row it serves an image challenge instead, with reason `PUZZLE_REFRESH_LIMIT`. It only does this if the site has image enabled. The switch only goes from puzzle to image, so a client that lies about its refreshes can only make its own challenge harder. A client that leaves the field out gets a normal session, the same as reloading the page.
69
+
70
+ The image widget's reload button now reports itself as a refresh too. That way a user who was moved onto image isn't sent back to the puzzle by their next reload.
71
+
72
+ ### Patch Changes
73
+
74
+ - 7d57d2a: A proof-of-work solve is checked against the address the challenge was issued to, and a mismatch sends the user to an image captcha instead of approving them. That check compared the whole IPv4 address, so it fired for anyone whose address moved between asking for the challenge and solving it — which is normal behaviour on mobile and on carrier NAT, where the low octet gets reassigned mid-session. One EE subscriber was seen moving one address along in four seconds.
75
+
76
+ It now compares the /24, so a reassignment inside the same pool reads as the same place on the network. IPv6 already worked this way on the /64, for the same reason, and is unchanged. A solve arriving from a genuinely different network is still caught and still escalates, so the point of the check survives.
77
+
78
+ Unit tests cover a low-octet reassignment, both ends of a /24, two different /24s, and a pair of adjacent addresses that straddle the /24 boundary.
79
+ - 2118584: Trim the comments on the puzzle difficulty device ceiling. No behaviour change.
80
+ - 80b7780: Post-PoW routing now receives the widget mode from the originating session and whether the submitted click coords were all (0,0), so a routing rule can escalate visible-mode solves that carry no real click position.
81
+ - dd4c27c: Store the whole-SYN fields the tcp-probe sidecar now reports.
82
+
83
+ The sidecar's handshake record grew from 80 to 104 bytes and the tail was reordered (prosopo/Protect#1167). chaddy was updated to read both layouts and to forward the new fields as their own headers (prosopo/chaddy#16, #17); this is the provider side of that, so the values have somewhere to land instead of being dropped at the middleware.
84
+
85
+ Twelve new fields on `Session`, all optional: `tcpOptsKinds`, `tcpOptsPresent`, `tcpOptsCount`, `tcpTsval`, `tcpTsecr`, `tcpFlags`, `tcpDataOffsetResv`, `tcpUrgPtr`, `ipIdent`, `ipTotalLen`, `ipFragFlags`, `ipTos`. They are surfaced on the decision-machine input the same way the existing TCP fields are, so routing and verify rules can read them.
86
+
87
+ `tcpOptsKinds` is stored decoded — the IANA kind number of each TCP option in wire order, `[2, 4, 8, 1, 3]` on an ordinary Linux SYN. The probe emits it packed into a u64, and a u64 reaches 2^64 while a JS number is exact only to 2^53, so the packed form could not be held without rounding; the rounding would land in the low bytes, which are the option kinds themselves. The array holds the same information and is queryable per position. It also replaces the old `tcpOptsOrder`, which packed 4 bits per option and so could not tell MSS from TCP Fast Open, or Window Scale from MD5, and could not represent MPTCP at all.
88
+
89
+ `tcpOptsFlags` and `tcpOptsOrder` are kept and still read. A pronode running an older chaddy still sends them, and years of rows hold them, so removing them would both lose the rollout window and break any routing rule already reading them. They go undefined on new sessions once the fleet is rolled.
90
+
91
+ `tcpTsval` and `tcpTsecr` are written only when the Timestamps option was actually on the SYN. A TSval of 0 is legal and a TSecr of 0 is expected, so neither can use zero to mean absent.
92
+
93
+ Six places named every one of these fields by hand — the middleware's copy onto the request, the session write, and four task files building the decision-machine bag. They now all go through one list, with a compile-time check that every field of `RawTlsSignals` is on it. That is not tidying: the Session projection comment in `@prosopo/types-database` records a field being missed in exactly this way, after which the rules reading it got `undefined` and silently never fired against real traffic.
94
+
95
+ Separately, the provider compose no longer defaults the sidecar to `prosopo/tcp-probe:latest`. The probe's reply is a bare fixed-size struct with no version in it, so the image tag is the only statement of which layout a host serves, and publishing `:latest` would change that on every pronode at the next pull with nobody running a deploy. The default is now the `0.1.1` both Protect inventories already pin.
96
+
97
+ Tests: the new headers all parse; a packed value above 2^53 decodes without losing its low bytes; the kind pairs the old encoding aliased stay distinct; a trailing end-of-option-list terminates the list rather than appearing in it; an all-zero packed value reads as absent rather than as an empty list; malformed and out-of-range values are ignored; the legacy header pair is still read; and a zero is kept rather than dropped, since `ipIdent` 0 and `tcpTsecr` 0 are real readings.
98
+ - Updated dependencies [1c13037]
99
+ - Updated dependencies [f961dab]
100
+ - Updated dependencies [a17e8eb]
101
+ - Updated dependencies [7d57d2a]
102
+ - Updated dependencies [2118584]
103
+ - Updated dependencies [1ce9482]
104
+ - Updated dependencies [6e2eb55]
105
+ - Updated dependencies [dcb691b]
106
+ - Updated dependencies [80b7780]
107
+ - Updated dependencies [dd4c27c]
108
+ - @prosopo/types@5.13.0
109
+ - @prosopo/types-database@5.8.0
110
+ - @prosopo/puzzle-assets@0.2.0
111
+ - @prosopo/util@3.3.14
112
+ - @prosopo/captcha-severity@1.2.0
113
+ - @prosopo/api@4.4.0
114
+ - @prosopo/locale@3.7.0
115
+ - @prosopo/api-express-router@3.2.1
116
+ - @prosopo/common@3.1.62
117
+ - @prosopo/database@4.1.4
118
+ - @prosopo/datasets@3.2.1
119
+ - @prosopo/env@3.6.69
120
+ - @prosopo/ipinfo@0.4.14
121
+ - @prosopo/keyring@2.10.1
122
+ - @prosopo/load-balancer@2.11.5
123
+ - @prosopo/types-env@2.11.15
124
+ - @prosopo/user-access-policy@3.14.15
125
+ - @prosopo/api-route@2.6.63
126
+ - @prosopo/logger@2.1.3
127
+ - @prosopo/redis-client@1.0.40
128
+ - @prosopo/util-crypto@13.6.0
129
+ - @prosopo/web-bot-auth@0.1.3
130
+
131
+ ## 5.14.0
132
+ ### Minor Changes
133
+
134
+ - ede4352: Admin tokens can now be bound to one provider and used only once. `jwtVerify` takes optional checks for the `aud` claim and for the longest allowed lifetime, and the provider's admin check uses them. A token that names an audience must name this provider: its host, `https://` plus its host, or one of the values in `PROSOPO_ADMIN_JWT_AUDIENCE`. A token that carries a `jti` is accepted once per provider process. Tokens may live at most one hour (`PROSOPO_ADMIN_JWT_MAX_LIFETIME_SECONDS`).
135
+
136
+ Migration: tokens without `aud` or `jti` are still accepted, so current callers keep working. Callers should add both, for example `pair.jwtIssue({ expiresIn }, { aud: provider.url, jti: randomUUID() })`, minting one token per provider and per request. Callers that reuse one token for several requests to the same provider must mint a new one per request before they add `jti`. Once every caller sends `aud`, set `PROSOPO_ADMIN_JWT_REQUIRE_AUDIENCE=true` to refuse tokens without it.
137
+ - e62661c: `/metrics` is no longer open to everyone by default. Without `PROSOPO_METRICS_TOKEN`, only direct requests from loopback or private addresses are served, such as a scraper on the docker network. Requests relayed by a reverse proxy (they carry `X-Forwarded-For`, `Forwarded` or `X-Real-IP`) get 403. With `PROSOPO_METRICS_TOKEN` set, any caller with `Authorization: Bearer <token>` is served, as before. The token is now compared in constant time. Set `PROSOPO_METRICS_PUBLIC=true` to keep the old open endpoint. A scraper that reaches the provider through a proxy or from a public address needs either the token or that flag.
138
+
139
+ ### Patch Changes
140
+
141
+ - fc79e06: The provider now only accepts a detector-pool upload of up to 128 MB from a caller with a valid admin token. Before, the large upload limit applied to anyone who sent a request to that path, because the body was read and parsed before the admin check and the rate limits ran, so unauthenticated requests could make the provider buffer and parse up to 128 MB each. Other requests to that path now get the normal 1 MB limit and are then refused by the admin check.
142
+ - 382f96b: Allowed-domain patterns that contain `*` are now matched without a regular expression. A pattern such as `*a*a*a*a*a*a*a*a*a*b` used to take tens of seconds to check against a long hostname, blocking the provider for every other request while it ran. Matching now takes time proportional to the pattern and hostname lengths, with the same results as before.
143
+ - 9ed0afd: 401 responses no longer include server internals. The admin auth check returned the whole error object, which included the request's i18n instance with its settings and file paths (about 16 KB per response), or whatever a failing check had thrown. It now returns `{ error: { code: 401, key, message } }`, the same shape as other API errors. The domain check returned unexpected errors, such as database errors, in the response body. It now returns a fixed message and logs the error.
144
+ - 3fe7b19: Check the number of unsolved captchas in a dataset against the configured minimum, instead of checking the number of solved ones twice.
145
+
146
+ The unsolved check compared the solved count with `unsolved.count`, so a dataset where every captcha was solved passed even when unsolved captchas were required. It now counts the captchas without a solution. Production sets `unsolved.count` to 0, so its behaviour does not change.
147
+ - b1becee: Stop padding the `/frictionless` response. The tarpit pads the challenge, and that endpoint does not serve one.
148
+
149
+ `GetFrictionlessCaptchaResponse` is `{captchaType, sessionId, dns_url}` — a session envelope. The widget then fetches the challenge from `/captcha/pow`, `/captcha/image` or `/captcha/puzzle`, and each of those resolves its own traffic-filter verdict and pads there, which is where the original feature put it.
150
+
151
+ Padding the envelope as well charged a tarpitted session twice. Measured on one site over 6h: 418 of 868 padded responses (48%, 1.48 GB of 3.09 GB) were `/frictionless`, roughly one per padded challenge.
152
+
153
+ This restores the frictionless handler to its pre-tarpit state, so it also reverts the hoist that extended envelope padding to reused sessions and pinned-captcha-type sites. Nothing is lost from the feature: each of those sessions still fetches a challenge from a typed endpoint, which pads it.
154
+
155
+ Kept from the same work: `padBytes` on the Mongoose traffic-category schema, without which the setting could not persist at all, and the padding writer's handling of a fieldless or non-object body.
156
+ - 3e1053b: Pad the challenge on the two frictionless paths that return early: a reused session, and a site pinned to a concrete captcha type.
157
+
158
+ `applyTrafficFilterAtRequestTime` ran after both short-circuits, so neither resolved a verdict and neither response was padded. On a site whose traffic is mostly returning visitors that is most of the traffic — on Twickets, session reuse is 11–18% of decisions and rose to ~16% after their proxy pool consolidated onto fewer IPs.
159
+
160
+ The verdict now resolves once, before both, and the pad is attached from it. No extra work: the call is synchronous and reads `req.ipInfo`, which `ipInfoMiddleware` already populates for every request.
161
+
162
+ The pad is read live on every request rather than stored on the session, unlike `captchaType`, `powDifficulty` and `puzzleTolerance`. Those are frozen because they are part of the issued challenge — the PoW string was minted at that difficulty — so re-deriving them mid-session would invalidate work already handed out. Nothing verifies the pad, so it has no such constraint, and reading it live means a newly-configured pad applies on the next request instead of only after every live session has expired.
163
+
164
+ A reused session keeps its cached captcha type, and a pinned site keeps serving the type it pinned. Making a category's `captchaType` override win over a site-level pin is a larger behaviour change and is left on prosopo/captcha#3463.
165
+
166
+ Blocking was never affected by any of this: `resolveTrafficFilterCheck` runs at submit time from the site's current settings and a fresh IP lookup, so a blocked category was always rejected regardless of how the challenge was issued.
167
+
168
+ Tests: the pad is attached on a reused session without evicting it or changing its cached type, and on a site pinned to each of pow / image / puzzle without changing which challenge is sent.
169
+ - df06006: Stream the traffic-filter tarpit padding with backpressure, and spread it across the response body instead of a single leading field.
170
+
171
+ The padding is written in 64 KiB chunks that wait for the socket to drain, and is abandoned if the connection goes away, so provider memory stays flat while a padded response is in flight however slowly it is read.
172
+
173
+ It is also split into several members with generated keys, one before each real field, rather than one `pad` member at the front. The body is the same size and parses the same way; no consumer reads these fields.
174
+ - 06784d0: Translation keys are now a typed union built from the English catalogue, so calling the translator with a key that does not exist fails to compile. Error classes only pass a message to the translator when it is a real catalogue key, and free-text messages are shown as they are.
175
+ - 7e9cd1e: The verify endpoints (image, PoW, puzzle and authenticated session) now answer 400 `GENERAL.INVALID_SIGNATURE` when the `dappSignature` is not hex, is the wrong length, or does not match the site key. Before, the error from the signature check fell into each handler's catch-all and came back as a 500. Anyone could call these endpoints and push up the provider's 500 rate, which made it look unhealthy and skewed the error-rate metrics. Real server faults later in the handlers still return 500.
176
+ - f7cfa1c: Return 400, not 500, when a verify request carries an undecodable token.
177
+
178
+ Every verify endpoint (image, PoW, puzzle and authenticated-session) decodes
179
+ the hex `ProcaptchaToken` inside a `try` whose `catch` answers
180
+ `API.BAD_REQUEST` with HTTP 500. The body schema only checks that the token is
181
+ a bounded `0x`-prefixed string, so a value like `0x1234` passes validation and
182
+ then fails to decode into a `ProcaptchaToken` SCALE struct — a bad-input case,
183
+ not a server fault.
184
+
185
+ The endpoints are unauthenticated, so any caller could make the provider
186
+ answer 500 at will. That mis-signals provider health and inflates the
187
+ error-rate metrics that alerting and the http_requests_total status label feed
188
+ from, without leaking anything (the response body is unchanged).
189
+
190
+ Decoding now goes through a helper that maps a decode failure to a 400
191
+ `CAPTCHA.PARSE_ERROR`, matching the existing 400 the same endpoints already
192
+ return for a non-hex token. Genuine server errors later in each handler still
193
+ surface as 500.
194
+ - Updated dependencies [ede4352]
195
+ - Updated dependencies [f53ddb2]
196
+ - Updated dependencies [97d65fa]
197
+ - Updated dependencies [08daf16]
198
+ - Updated dependencies [1144e04]
199
+ - Updated dependencies [de6bb08]
200
+ - Updated dependencies [2145922]
201
+ - Updated dependencies [b017dfb]
202
+ - Updated dependencies [8ed0eb8]
203
+ - Updated dependencies [9ed0afd]
204
+ - Updated dependencies [70fcefd]
205
+ - Updated dependencies [06784d0]
206
+ - Updated dependencies [06784d0]
207
+ - Updated dependencies [8d7ba8c]
208
+ - Updated dependencies [995e954]
209
+ - Updated dependencies [97a799e]
210
+ - Updated dependencies [9fc1e8a]
211
+ - Updated dependencies [4461043]
212
+ - Updated dependencies [92edebd]
213
+ - Updated dependencies [54a07f3]
214
+ - Updated dependencies [b75e9b7]
215
+ - Updated dependencies [6d5b7f5]
216
+ - @prosopo/util-crypto@13.6.0
217
+ - @prosopo/keyring@2.10.0
218
+ - @prosopo/types@5.12.0
219
+ - @prosopo/api-express-router@3.2.0
220
+ - @prosopo/database@4.1.3
221
+ - @prosopo/datasets@3.2.0
222
+ - @prosopo/util@3.3.13
223
+ - @prosopo/locale@3.6.2
224
+ - @prosopo/load-balancer@2.11.4
225
+ - @prosopo/common@3.1.61
226
+ - @prosopo/types-database@5.7.3
227
+ - @prosopo/env@3.6.68
228
+ - @prosopo/user-access-policy@3.14.14
229
+ - @prosopo/types-env@2.11.14
230
+ - @prosopo/api@4.3.8
231
+ - @prosopo/ipinfo@0.4.13
232
+ - @prosopo/logger@2.1.2
233
+ - @prosopo/api-route@2.6.62
234
+ - @prosopo/redis-client@1.0.39
235
+
3
236
  ## 5.13.4
4
237
  ### Patch Changes
5
238
 
package/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright 2022 PROSOPO LIMITED
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
@@ -0,0 +1,4 @@
1
+ import { type AuthMiddlewareOptions } from "@prosopo/api-express-router";
2
+ export declare const DEFAULT_ADMIN_JWT_MAX_LIFETIME_SECONDS = 3600;
3
+ export declare const adminAuthOptions: (vars: Record<string, string | undefined>, host: string | undefined) => AuthMiddlewareOptions;
4
+ //# sourceMappingURL=adminAuthOptions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adminAuthOptions.d.ts","sourceRoot":"","sources":["../../src/api/adminAuthOptions.ts"],"names":[],"mappings":"AAcA,OAAO,EACN,KAAK,qBAAqB,EAE1B,MAAM,6BAA6B,CAAC;AAGrC,eAAO,MAAM,sCAAsC,OAAO,CAAC;AAmB3D,eAAO,MAAM,gBAAgB,SACtB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,QAClC,MAAM,GAAG,SAAS,KACtB,qBAyBF,CAAC"}
@@ -0,0 +1,34 @@
1
+ import { createMemoryJwtReplayGuard } from "@prosopo/api-express-router";
2
+ //#region src/api/adminAuthOptions.ts
3
+ /** Longest admin token lifetime (exp - iat) accepted by default, in seconds. */
4
+ var DEFAULT_ADMIN_JWT_MAX_LIFETIME_SECONDS = 3600;
5
+ var parseList = (value) => (value ?? "").split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
6
+ /**
7
+ * Admin JWT checks for this provider.
8
+ *
9
+ * - Audience: a token carrying `aud` must name this provider. The accepted
10
+ * values come from PROSOPO_ADMIN_JWT_AUDIENCE (comma separated), else from
11
+ * the provider host as both `host` and `https://host`. Tokens without `aud`
12
+ * are still accepted until PROSOPO_ADMIN_JWT_REQUIRE_AUDIENCE=true.
13
+ * - Replay: a token carrying `jti` is accepted once per process.
14
+ * - Lifetime: exp - iat may not exceed PROSOPO_ADMIN_JWT_MAX_LIFETIME_SECONDS
15
+ * (default one hour).
16
+ */
17
+ var adminAuthOptions = (vars, host) => {
18
+ const configured = parseList(vars.PROSOPO_ADMIN_JWT_AUDIENCE);
19
+ const requireAudience = vars.PROSOPO_ADMIN_JWT_REQUIRE_AUDIENCE === "true";
20
+ const derived = host ? [host, `https://${host}`] : [];
21
+ const candidates = configured.length > 0 ? configured : derived;
22
+ const audience = candidates.length > 0 || requireAudience ? candidates : void 0;
23
+ const maxLifetime = Number.parseInt(vars.PROSOPO_ADMIN_JWT_MAX_LIFETIME_SECONDS ?? "", 10);
24
+ return {
25
+ verify: {
26
+ audience,
27
+ requireAudience,
28
+ maxLifetimeSeconds: Number.isFinite(maxLifetime) && maxLifetime > 0 ? maxLifetime : DEFAULT_ADMIN_JWT_MAX_LIFETIME_SECONDS
29
+ },
30
+ replayGuard: createMemoryJwtReplayGuard()
31
+ };
32
+ };
33
+ //#endregion
34
+ export { DEFAULT_ADMIN_JWT_MAX_LIFETIME_SECONDS, adminAuthOptions };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adminAuthOptions.js","sourceRoot":"","sources":["../../src/api/adminAuthOptions.ts"],"names":[],"mappings":"AAcA,OAAO,EAEN,0BAA0B,GAC1B,MAAM,6BAA6B,CAAC;AAGrC,MAAM,CAAC,MAAM,sCAAsC,GAAG,IAAI,CAAC;AAE3D,MAAM,SAAS,GAAG,CAAC,KAAyB,EAAY,EAAE,CACzD,CAAC,KAAK,IAAI,EAAE,CAAC;KACX,KAAK,CAAC,GAAG,CAAC;KACV,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;KAC5B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAavC,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC/B,IAAwC,EACxC,IAAwB,EACA,EAAE;IAC1B,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IAC9D,MAAM,eAAe,GAAG,IAAI,CAAC,kCAAkC,KAAK,MAAM,CAAC;IAC3E,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,WAAW,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACtD,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC;IAEhE,MAAM,QAAQ,GACb,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IAEnE,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,CAClC,IAAI,CAAC,sCAAsC,IAAI,EAAE,EACjD,EAAE,CACF,CAAC;IAEF,OAAO;QACN,MAAM,EAAE;YACP,QAAQ;YACR,eAAe;YACf,kBAAkB,EACjB,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,WAAW,GAAG,CAAC;gBAC9C,CAAC,CAAC,WAAW;gBACb,CAAC,CAAC,sCAAsC;SAC1C;QACD,WAAW,EAAE,0BAA0B,EAAE;KACzC,CAAC;AACH,CAAC,CAAC"}
@@ -1,9 +1,10 @@
1
- import { type IFrictionlessThreshold } from "@prosopo/types";
1
+ import { type IFrictionlessThreshold, type IFrictionlessTypes, type IUserSettings } from "@prosopo/types";
2
2
  export declare const DEFAULT_FRICTIONLESS_THRESHOLD = 0.5;
3
3
  export declare const resolveScoreLadder: (configured: IFrictionlessThreshold | number | undefined) => {
4
4
  botThreshold: number;
5
5
  botImageThreshold: number;
6
6
  };
7
+ export declare const resolveSiteAllowedCaptchaTypes: (settings: Pick<Partial<IUserSettings>, "captchaType" | "frictionlessTypes" | "captchaTypeFeatureFlags">) => IFrictionlessTypes;
7
8
  export declare const DECRYPTION_FAILED_IMAGE_ROUNDS = 3;
8
9
  export declare const MISSING_TOKEN_IMAGE_ROUNDS = 3;
9
10
  export declare const MISSING_HEAD_HASH_IMAGE_ROUNDS = 2;
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/api/captcha/getFrictionlessCaptchaChallenge/constants.ts"],"names":[],"mappings":"AAcA,OAAO,EACN,KAAK,sBAAsB,EAG3B,MAAM,gBAAgB,CAAC;AAExB,eAAO,MAAM,8BAA8B,MACR,CAAC;AAOpC,eAAO,MAAM,kBAAkB,eAClB,sBAAsB,GAAG,MAAM,GAAG,SAAS,KACrD;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAMnD,CAAC;AAWF,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAOhD,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAM5C,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAQhD,eAAO,MAAM,+BAA+B,IAAI,CAAC;AAUjD,eAAO,MAAM,+BAA+B,SACrC,MAAM,sBACQ,MAAM,EAAE,GAAG,SAAS,KACtC,MAEwE,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/api/captcha/getFrictionlessCaptchaChallenge/constants.ts"],"names":[],"mappings":"AAcA,OAAO,EAEN,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAIlB,MAAM,gBAAgB,CAAC;AAExB,eAAO,MAAM,8BAA8B,MACR,CAAC;AAOpC,eAAO,MAAM,kBAAkB,eAClB,sBAAsB,GAAG,MAAM,GAAG,SAAS,KACrD;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAMnD,CAAC;AAOF,eAAO,MAAM,8BAA8B,aAChC,IAAI,CACb,OAAO,CAAC,aAAa,CAAC,EACtB,aAAa,GAAG,mBAAmB,GAAG,yBAAyB,CAC/D,KACC,kBAOA,CAAC;AAWJ,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAOhD,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAM5C,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAQhD,eAAO,MAAM,+BAA+B,IAAI,CAAC;AAUjD,eAAO,MAAM,+BAA+B,SACrC,MAAM,sBACQ,MAAM,EAAE,GAAG,SAAS,KACtC,MAEwE,CAAC"}
@@ -1,4 +1,4 @@
1
- import { resolveFrictionlessThreshold } from "@prosopo/types";
1
+ import { CaptchaType, resolveAllowedCaptchaTypes, resolveFrictionlessThreshold } from "@prosopo/types";
2
2
  /**
3
3
  * A client record's configured ladder, in the terms the decision machine
4
4
  * compares against. Thin rename over `resolveFrictionlessThreshold`, which
@@ -12,6 +12,15 @@ var resolveScoreLadder = (configured) => {
12
12
  };
13
13
  };
14
14
  /**
15
+ * The types any session minted by /frictionless may be. The owner's
16
+ * `frictionlessTypes` only governs the frictionless flow, so a site pinned to
17
+ * a concrete `captchaType` is held to the feature flags alone.
18
+ */
19
+ var resolveSiteAllowedCaptchaTypes = (settings) => resolveAllowedCaptchaTypes({
20
+ captchaTypeFeatureFlags: settings.captchaTypeFeatureFlags,
21
+ ...(!settings.captchaType || settings.captchaType === CaptchaType.frictionless) && { frictionlessTypes: settings.frictionlessTypes }
22
+ });
23
+ /**
15
24
  * Size an image challenge by how much was actually found. `base` is the
16
25
  * sitekey's ordinary image round count; each triggered signal adds a round,
17
26
  * capped by `MAX_DETECTOR_EXTRA_IMAGE_ROUNDS`. A session with nothing
@@ -21,4 +30,4 @@ var resolveScoreLadder = (configured) => {
21
30
  */
22
31
  var getRoundsFromTriggeredDetectors = (base, triggeredDetectors) => base + Math.min(triggeredDetectors?.length ?? 0, 6);
23
32
  //#endregion
24
- export { getRoundsFromTriggeredDetectors, resolveScoreLadder };
33
+ export { getRoundsFromTriggeredDetectors, resolveScoreLadder, resolveSiteAllowedCaptchaTypes };
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../../src/api/captcha/getFrictionlessCaptchaChallenge/constants.ts"],"names":[],"mappings":"AAcA,OAAO,EAEN,kCAAkC,EAClC,4BAA4B,GAC5B,MAAM,gBAAgB,CAAC;AAExB,MAAM,CAAC,MAAM,8BAA8B,GAC1C,kCAAkC,CAAC;AAOpC,MAAM,CAAC,MAAM,kBAAkB,GAAG,CACjC,UAAuD,EACD,EAAE;IACxD,MAAM,MAAM,GAAG,4BAA4B,CAAC,UAAU,CAAC,CAAC;IACxD,OAAO;QACN,YAAY,EAAE,MAAM,CAAC,2BAA2B;QAChD,iBAAiB,EAAE,MAAM,CAAC,0BAA0B;KACpD,CAAC;AACH,CAAC,CAAC;AAWF,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,CAAC;AAOhD,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC;AAM5C,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,CAAC;AAQhD,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAAC,CAAC;AAUjD,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAC9C,IAAY,EACZ,kBAAwC,EAC/B,EAAE,CACX,IAAI;IACJ,IAAI,CAAC,GAAG,CAAC,kBAAkB,EAAE,MAAM,IAAI,CAAC,EAAE,+BAA+B,CAAC,CAAC"}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../../src/api/captcha/getFrictionlessCaptchaChallenge/constants.ts"],"names":[],"mappings":"AAcA,OAAO,EACN,WAAW,EAIX,kCAAkC,EAClC,0BAA0B,EAC1B,4BAA4B,GAC5B,MAAM,gBAAgB,CAAC;AAExB,MAAM,CAAC,MAAM,8BAA8B,GAC1C,kCAAkC,CAAC;AAOpC,MAAM,CAAC,MAAM,kBAAkB,GAAG,CACjC,UAAuD,EACD,EAAE;IACxD,MAAM,MAAM,GAAG,4BAA4B,CAAC,UAAU,CAAC,CAAC;IACxD,OAAO;QACN,YAAY,EAAE,MAAM,CAAC,2BAA2B;QAChD,iBAAiB,EAAE,MAAM,CAAC,0BAA0B;KACpD,CAAC;AACH,CAAC,CAAC;AAOF,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAC7C,QAGC,EACoB,EAAE,CACvB,0BAA0B,CAAC;IAC1B,uBAAuB,EAAE,QAAQ,CAAC,uBAAuB;IACzD,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW;QACzB,QAAQ,CAAC,WAAW,KAAK,WAAW,CAAC,YAAY,CAAC,IAAI;QACtD,iBAAiB,EAAE,QAAQ,CAAC,iBAAiB;KAC7C,CAAC;CACF,CAAC,CAAC;AAWJ,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,CAAC;AAOhD,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC;AAM5C,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,CAAC;AAQhD,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAAC,CAAC;AAUjD,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAC9C,IAAY,EACZ,kBAAwC,EAC/B,EAAE,CACX,IAAI;IACJ,IAAI,CAAC,GAAG,CAAC,kBAAkB,EAAE,MAAM,IAAI,CAAC,EAAE,+BAA+B,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../../src/api/captcha/getFrictionlessCaptchaChallenge/handler.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAEN,KAAK,kBAAkB,EACvB,MAAM,6BAA6B,CAAC;AAGrC,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAG/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;8BAuCtD,mBAAmB,0BACA,kBAAkB,WAGpC,OAAO,GAAG,gBAAgB,OAC1B,QAAQ,QACP,YAAY;AAPpB,wBAm4BG"}
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../../src/api/captcha/getFrictionlessCaptchaChallenge/handler.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAEN,KAAK,kBAAkB,EACvB,MAAM,6BAA6B,CAAC;AAGrC,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAG/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;8BA4CtD,mBAAmB,0BACA,kBAAkB,WAGpC,OAAO,GAAG,gBAAgB,OAC1B,QAAQ,QACP,YAAY;AAPpB,wBAu5BG"}