@prosopo/provider 5.10.1 → 5.10.2

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 (55) hide show
  1. package/.turbo/turbo-build$colon$cjs.log +10 -9
  2. package/.turbo/turbo-build$colon$tsc.log +27 -27
  3. package/.turbo/turbo-build.log +12 -11
  4. package/CHANGELOG.md +95 -0
  5. package/dist/api/captcha/assignDetectorBundle.d.ts.map +1 -1
  6. package/dist/api/captcha/assignDetectorBundle.js +27 -9
  7. package/dist/api/captcha/assignDetectorBundle.js.map +1 -1
  8. package/dist/api/captcha/assignDetectorBundle.unit.test.d.ts +2 -0
  9. package/dist/api/captcha/assignDetectorBundle.unit.test.d.ts.map +1 -0
  10. package/dist/api/captcha/assignDetectorBundle.unit.test.js +153 -0
  11. package/dist/api/captcha/assignDetectorBundle.unit.test.js.map +1 -0
  12. package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.d.ts.map +1 -1
  13. package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js +4 -2
  14. package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js.map +1 -1
  15. package/dist/cjs/api/captcha/assignDetectorBundle.cjs +26 -8
  16. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/handler.cjs +4 -2
  17. package/dist/cjs/tasks/captchaManager.cjs +34 -3
  18. package/dist/cjs/tasks/detection/assignSecret.cjs +45 -0
  19. package/dist/cjs/tasks/detection/bundlePool.cjs +11 -7
  20. package/dist/cjs/tasks/detection/decodePayload.cjs +1103 -1087
  21. package/dist/cjs/tasks/detection/decodePayload.js +1 -1
  22. package/dist/cjs/tasks/detection/getBotScore.cjs +2 -0
  23. package/dist/cjs/tasks/frictionless/frictionlessTasks.cjs +4 -3
  24. package/dist/tasks/captchaManager.d.ts +1 -0
  25. package/dist/tasks/captchaManager.d.ts.map +1 -1
  26. package/dist/tasks/captchaManager.js +34 -3
  27. package/dist/tasks/captchaManager.js.map +1 -1
  28. package/dist/tasks/detection/assignSecret.d.ts +8 -0
  29. package/dist/tasks/detection/assignSecret.d.ts.map +1 -0
  30. package/dist/tasks/detection/assignSecret.js +45 -0
  31. package/dist/tasks/detection/assignSecret.js.map +1 -0
  32. package/dist/tasks/detection/bundlePool.d.ts +2 -1
  33. package/dist/tasks/detection/bundlePool.d.ts.map +1 -1
  34. package/dist/tasks/detection/bundlePool.js +11 -8
  35. package/dist/tasks/detection/bundlePool.js.map +1 -1
  36. package/dist/tasks/detection/bundlePool.test.js +4 -3
  37. package/dist/tasks/detection/bundlePool.test.js.map +1 -1
  38. package/dist/tasks/detection/decodePayload.d.ts +2 -2
  39. package/dist/tasks/detection/decodePayload.d.ts.map +1 -1
  40. package/dist/tasks/detection/decodePayload.js +1 -1
  41. package/dist/tasks/detection/decodePayload.js.map +1 -1
  42. package/dist/tasks/detection/getBotScore.d.ts +2 -0
  43. package/dist/tasks/detection/getBotScore.d.ts.map +1 -1
  44. package/dist/tasks/detection/getBotScore.js +4 -2
  45. package/dist/tasks/detection/getBotScore.js.map +1 -1
  46. package/dist/tasks/frictionless/frictionlessTasks.d.ts.map +1 -1
  47. package/dist/tasks/frictionless/frictionlessTasks.js +4 -3
  48. package/dist/tasks/frictionless/frictionlessTasks.js.map +1 -1
  49. package/dist/tests/unit/tasks/captchaManager.unit.test.js +46 -0
  50. package/dist/tests/unit/tasks/captchaManager.unit.test.js.map +1 -1
  51. package/package.json +15 -15
  52. package/dist/api/captcha/assignDetectorBundle.test.d.ts +0 -2
  53. package/dist/api/captcha/assignDetectorBundle.test.d.ts.map +0 -1
  54. package/dist/api/captcha/assignDetectorBundle.test.js +0 -71
  55. package/dist/api/captcha/assignDetectorBundle.test.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
 
2
- > @prosopo/provider@5.10.1 build:cjs
2
+ > @prosopo/provider@5.10.2 build:cjs
3
3
  > NODE_ENV=${NODE_ENV:-development}; vite build --config vite.cjs.config.ts --mode $NODE_ENV
4
4
 
5
5
  ViteCommonJSConfig: .
@@ -59,7 +59,7 @@ ViteCommonJSConfig: .
59
59
  vite v8.1.5 building ssr environment for production...
60
60
  Bundle build started
61
61
  
62
- ✓ 110 modules transformed.
62
+ ✓ 111 modules transformed.
63
63
  rendering chunks...
64
64
  computing gzip size...
65
65
  dist/cjs/tasks/index.cjs 0.08 kB │ gzip: 0.07 kB
@@ -99,6 +99,7 @@ dist/cjs/tasks/puzzle/backgroundBuffer.cjs 1
99
99
  dist/cjs/pairs.cjs 1.39 kB │ gzip: 0.60 kB
100
100
  dist/cjs/tasks/dataset/datasetTasksUtils.cjs 1.42 kB │ gzip: 0.49 kB
101
101
  dist/cjs/utils/clientMetaData.cjs 1.48 kB │ gzip: 0.72 kB
102
+ dist/cjs/tasks/detection/assignSecret.cjs 1.48 kB │ gzip: 0.75 kB
102
103
  dist/cjs/compositeIpAddress.cjs 1.55 kB │ gzip: 0.57 kB
103
104
  dist/cjs/api/admin/apiGetAllDecisionMachinesEndpoint.cjs 1.59 kB │ gzip: 0.60 kB
104
105
  dist/cjs/schedulers/updateSpamEmailDomains.cjs 1.64 kB │ gzip: 0.62 kB
@@ -114,7 +115,6 @@ dist/cjs/utils/honeypot/phraseBank.cjs 1
114
115
  dist/cjs/api/handshakeTimingMiddleware.cjs 1.88 kB │ gzip: 0.71 kB
115
116
  dist/cjs/api/captcha/signedAssetsResolver.cjs 1.96 kB │ gzip: 0.96 kB
116
117
  dist/cjs/api/admin/apiClearAllCountersEndpoint.cjs 2.01 kB │ gzip: 0.78 kB
117
- dist/cjs/api/captcha/assignDetectorBundle.cjs 2.03 kB │ gzip: 0.86 kB
118
118
  dist/cjs/tasks/frictionless/ipMatch.cjs 2.03 kB │ gzip: 1.03 kB
119
119
  dist/cjs/tasks/spam/updateSpamEmailDomains.cjs 2.12 kB │ gzip: 0.85 kB
120
120
  dist/cjs/tasks/frictionless/routingMachine.cjs 2.17 kB │ gzip: 0.91 kB
@@ -123,13 +123,14 @@ dist/cjs/tasks/puzzle/stratifiedSampler.cjs 2
123
123
  dist/cjs/tasks/frictionless/frictionlessTasksUtils.cjs 2.21 kB │ gzip: 1.10 kB
124
124
  dist/cjs/tasks/captchaTypeSelection.cjs 2.44 kB │ gzip: 1.04 kB
125
125
  dist/cjs/api/admin/apiDnsEventEndpoint.cjs 2.59 kB │ gzip: 1.00 kB
126
- dist/cjs/tasks/detection/getBotScore.cjs 2.60 kB │ gzip: 0.95 kB
126
+ dist/cjs/tasks/detection/getBotScore.cjs 2.63 kB │ gzip: 0.96 kB
127
127
  dist/cjs/utils/dns.cjs 2.66 kB │ gzip: 1.02 kB
128
128
  dist/cjs/api/forwardVerify.cjs 2.66 kB │ gzip: 1.12 kB
129
129
  dist/cjs/tasks/puzzle/puzzleDifficulty.cjs 2.66 kB │ gzip: 1.10 kB
130
130
  dist/cjs/api/public.cjs 2.67 kB │ gzip: 1.05 kB
131
131
  dist/cjs/api/captcha/checkSpamEmail.cjs 2.73 kB │ gzip: 0.95 kB
132
132
  dist/cjs/api/captcha/submitPuzzleCaptchaSolution.cjs 2.77 kB │ gzip: 0.99 kB
133
+ dist/cjs/api/captcha/assignDetectorBundle.cjs 2.90 kB │ gzip: 1.15 kB
133
134
  dist/cjs/api/captcha/maintenanceModeResponses.cjs 2.91 kB │ gzip: 0.77 kB
134
135
  dist/cjs/api/captcha/submitImageCaptchaSolution.cjs 2.93 kB │ gzip: 1.03 kB
135
136
  dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.cjs 3.66 kB │ gzip: 1.35 kB
@@ -154,7 +155,7 @@ dist/cjs/api/captcha/getImageCaptchaChallenge.cjs 7
154
155
  dist/cjs/tasks/spam/checkTrafficFilter.cjs 7.47 kB │ gzip: 2.48 kB
155
156
  dist/cjs/api/captcha/getPuzzleCaptchaChallenge.cjs 7.56 kB │ gzip: 2.03 kB
156
157
  dist/cjs/api/metrics.cjs 7.96 kB │ gzip: 2.23 kB
157
- dist/cjs/tasks/detection/bundlePool.cjs 8.72 kB │ gzip: 3.26 kB
158
+ dist/cjs/tasks/detection/bundlePool.cjs 8.89 kB │ gzip: 3.29 kB
158
159
  dist/cjs/api/captcha/submitPoWCaptchaSolution.cjs 9.54 kB │ gzip: 2.81 kB
159
160
  dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.cjs 10.33 kB │ gzip: 1.82 kB
160
161
  dist/cjs/api/startProviderApi.cjs 11.62 kB │ gzip: 3.70 kB
@@ -163,17 +164,17 @@ dist/cjs/tasks/decisionMachine/decisionMachineRunner.cjs 12
163
164
  dist/cjs/api/blacklistRequestInspector.cjs 13.10 kB │ gzip: 4.41 kB
164
165
  dist/cjs/util.cjs 13.15 kB │ gzip: 3.26 kB
165
166
  dist/cjs/api/verify.cjs 16.79 kB │ gzip: 2.93 kB
166
- dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/handler.cjs 20.58 kB │ gzip: 4.72 kB
167
+ dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/handler.cjs 20.65 kB │ gzip: 4.74 kB
167
168
  dist/cjs/tasks/frictionless/frictionlessTasks.cjs 20.69 kB │ gzip: 5.49 kB
168
- dist/cjs/tasks/captchaManager.cjs 22.43 kB │ gzip: 6.38 kB
169
+ dist/cjs/tasks/captchaManager.cjs 23.52 kB │ gzip: 6.78 kB
169
170
  dist/cjs/tasks/puzzleCaptcha/puzzleTasks.cjs 24.03 kB │ gzip: 5.15 kB
170
171
  dist/cjs/tasks/powCaptcha/powTasks.cjs 24.84 kB │ gzip: 5.97 kB
171
172
  dist/cjs/tasks/imgCaptcha/imgCaptchaTasks.cjs 27.96 kB │ gzip: 6.37 kB
172
173
  dist/cjs/tasks/detection/decodeBehavior.cjs 32.24 kB │ gzip: 10.32 kB
173
174
  dist/cjs/tasks/detection/decodeSimd.cjs 36.03 kB │ gzip: 11.96 kB
174
- dist/cjs/tasks/detection/decodePayload.cjs 54.84 kB │ gzip: 17.78 kB
175
+ dist/cjs/tasks/detection/decodePayload.cjs 53.96 kB │ gzip: 18.08 kB
175
176
 
176
- ✓ built in 203ms
177
+ ✓ built in 173ms
177
178
  [copy-plugin] copying /home/runner/work/captcha/captcha/packages/provider/src/tasks/detection/decodeBehavior.js to /home/runner/work/captcha/captcha/packages/provider/dist/cjs/tasks/detection/decodeBehavior.js
178
179
  [copy-plugin] copying /home/runner/work/captcha/captcha/packages/provider/src/tasks/detection/decodePayload.js to /home/runner/work/captcha/captcha/packages/provider/dist/cjs/tasks/detection/decodePayload.js
179
180
  [copy-plugin] copying /home/runner/work/captcha/captcha/packages/provider/src/tasks/detection/decodeSimd.js to /home/runner/work/captcha/captcha/packages/provider/dist/cjs/tasks/detection/decodeSimd.js
@@ -1,8 +1,8 @@
1
1
 
2
- > @prosopo/provider@5.10.1 build:tsc
2
+ > @prosopo/provider@5.10.2 build:tsc
3
3
  > tsc --build --verbose
4
4
 
5
- 1:49:47 PM - Projects in this build:
5
+ 5:39:46 AM - Projects in this build:
6
6
  * ../../dev/config/tsconfig.json
7
7
  * ../util/tsconfig.json
8
8
  * ../logger/tsconfig.json
@@ -28,53 +28,53 @@
28
28
  * ../web-bot-auth/tsconfig.json
29
29
  * tsconfig.json
30
30
 
31
- 1:49:47 PM - Project '../../dev/config/tsconfig.json' is up to date because newest input '../../dev/config/src/webpack/webpack.config.ts' is older than output '../../dev/config/tsconfig.tsbuildinfo'
31
+ 5:39:46 AM - Project '../../dev/config/tsconfig.json' is up to date because newest input '../../dev/config/src/webpack/webpack.config.ts' is older than output '../../dev/config/tsconfig.tsbuildinfo'
32
32
 
33
- 1:49:47 PM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/url.ts' is older than output '../util/tsconfig.tsbuildinfo'
33
+ 5:39:46 AM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/url.ts' is older than output '../util/tsconfig.tsbuildinfo'
34
34
 
35
- 1:49:47 PM - Project '../logger/tsconfig.json' is up to date because newest input '../logger/src/logger.ts' is older than output '../logger/tsconfig.tsbuildinfo'
35
+ 5:39:46 AM - Project '../logger/tsconfig.json' is up to date because newest input '../logger/src/index.ts' is older than output '../logger/tsconfig.tsbuildinfo'
36
36
 
37
- 1:49:47 PM - Project '../api-route/tsconfig.json' is up to date because newest input '../api-route/src/apiRoutes.ts' is older than output '../api-route/tsconfig.tsbuildinfo'
37
+ 5:39:46 AM - Project '../api-route/tsconfig.json' is up to date because newest input '../api-route/src/apiRoutes.ts' is older than output '../api-route/tsconfig.tsbuildinfo'
38
38
 
39
- 1:49:47 PM - Project '../locale/tsconfig.json' is up to date because newest input '../locale/src/translationKey.ts' is older than output '../locale/tsconfig.tsbuildinfo'
39
+ 5:39:46 AM - Project '../locale/tsconfig.json' is up to date because newest input '../locale/src/translationKey.ts' is older than output '../locale/tsconfig.tsbuildinfo'
40
40
 
41
- 1:49:47 PM - Project '../util-crypto/tsconfig.json' is up to date because newest input '../util-crypto/src/types.ts' is older than output '../util-crypto/tsconfig.tsbuildinfo'
41
+ 5:39:46 AM - Project '../util-crypto/tsconfig.json' is up to date because newest input '../util-crypto/src/types.ts' is older than output '../util-crypto/tsconfig.tsbuildinfo'
42
42
 
43
- 1:49:47 PM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/types.test-d.ts' is older than output '../types/tsconfig.tsbuildinfo'
43
+ 5:39:46 AM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/types.test-d.ts' is older than output '../types/tsconfig.tsbuildinfo'
44
44
 
45
- 1:49:47 PM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/tests/utils/batches.unit.test.ts' is older than output '../common/tsconfig.tsbuildinfo'
45
+ 5:39:46 AM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/index.ts' is older than output '../common/tsconfig.tsbuildinfo'
46
46
 
47
- 1:49:47 PM - Project '../ipinfo/tsconfig.json' is up to date because newest input '../ipinfo/src/types.ts' is older than output '../ipinfo/tsconfig.tsbuildinfo'
47
+ 5:39:46 AM - Project '../ipinfo/tsconfig.json' is up to date because newest input '../ipinfo/src/types.ts' is older than output '../ipinfo/tsconfig.tsbuildinfo'
48
48
 
49
- 1:49:47 PM - Project '../redis-client/tsconfig.json' is up to date because newest input '../redis-client/src/index.ts' is older than output '../redis-client/tsconfig.tsbuildinfo'
49
+ 5:39:46 AM - Project '../redis-client/tsconfig.json' is up to date because newest input '../redis-client/src/index.ts' is older than output '../redis-client/tsconfig.tsbuildinfo'
50
50
 
51
- 1:49:47 PM - Project '../api/tsconfig.json' is up to date because newest input '../api/src/index.ts' is older than output '../api/tsconfig.tsbuildinfo'
51
+ 5:39:46 AM - Project '../api/tsconfig.json' is up to date because newest input '../api/src/index.ts' is older than output '../api/tsconfig.tsbuildinfo'
52
52
 
53
- 1:49:47 PM - Project '../user-access-policy/tsconfig.json' is up to date because newest input '../user-access-policy/src/transformRule.ts' is older than output '../user-access-policy/tsconfig.tsbuildinfo'
53
+ 5:39:47 AM - Project '../user-access-policy/tsconfig.json' is up to date because newest input '../user-access-policy/src/ruleRecord.ts' is older than output '../user-access-policy/tsconfig.tsbuildinfo'
54
54
 
55
- 1:49:47 PM - Project '../types-database/tsconfig.json' is up to date because newest input '../types-database/src/index.ts' is older than output '../types-database/tsconfig.tsbuildinfo'
55
+ 5:39:47 AM - Project '../types-database/tsconfig.json' is up to date because newest input '../types-database/src/index.ts' is older than output '../types-database/tsconfig.tsbuildinfo'
56
56
 
57
- 1:49:47 PM - Project '../database/tsconfig.json' is up to date because newest input '../database/src/tests/unit/captchaLabel.unit.test.ts' is older than output '../database/tsconfig.tsbuildinfo'
57
+ 5:39:47 AM - Project '../database/tsconfig.json' is up to date because newest input '../database/src/tests/mongoGlobalSetup.ts' is older than output '../database/tsconfig.tsbuildinfo'
58
58
 
59
- 1:49:47 PM - Project '../keyring/tsconfig.json' is up to date because newest input '../keyring/src/index.ts' is older than output '../keyring/tsconfig.tsbuildinfo'
59
+ 5:39:47 AM - Project '../keyring/tsconfig.json' is up to date because newest input '../keyring/src/keyring/keyring.test.ts' is older than output '../keyring/tsconfig.tsbuildinfo'
60
60
 
61
- 1:49:47 PM - Project '../types-env/tsconfig.json' is up to date because newest input '../types-env/src/index.ts' is older than output '../types-env/tsconfig.tsbuildinfo'
61
+ 5:39:47 AM - Project '../types-env/tsconfig.json' is up to date because newest input '../types-env/src/env.ts' is older than output '../types-env/tsconfig.tsbuildinfo'
62
62
 
63
- 1:49:47 PM - Project '../env/tsconfig.json' is up to date because newest input '../env/src/index.ts' is older than output '../env/tsconfig.tsbuildinfo'
63
+ 5:39:47 AM - Project '../env/tsconfig.json' is up to date because newest input '../env/src/tests/env.maintenance.unit.test.ts' is older than output '../env/tsconfig.tsbuildinfo'
64
64
 
65
- 1:49:47 PM - Project '../api-express-router/tsconfig.json' is up to date because newest input '../api-express-router/src/middlewares/requestLoggerMiddleware.ts' is older than output '../api-express-router/tsconfig.tsbuildinfo'
65
+ 5:39:47 AM - Project '../api-express-router/tsconfig.json' is up to date because newest input '../api-express-router/src/errorHandler.ts' is older than output '../api-express-router/tsconfig.tsbuildinfo'
66
66
 
67
- 1:49:47 PM - Project '../captcha-severity/tsconfig.json' is up to date because newest input '../captcha-severity/src/index.test.ts' is older than output '../captcha-severity/tsconfig.tsbuildinfo'
67
+ 5:39:47 AM - Project '../captcha-severity/tsconfig.json' is up to date because newest input '../captcha-severity/src/index.test.ts' is older than output '../captcha-severity/tsconfig.tsbuildinfo'
68
68
 
69
- 1:49:47 PM - Project '../datasets/tsconfig.json' is up to date because newest input '../datasets/src/tests/dataset.unit.test.ts' is older than output '../datasets/tsconfig.tsbuildinfo'
69
+ 5:39:47 AM - Project '../datasets/tsconfig.json' is up to date because newest input '../datasets/src/index.ts' is older than output '../datasets/tsconfig.tsbuildinfo'
70
70
 
71
- 1:49:47 PM - Project '../load-balancer/tsconfig.json' is up to date because newest input '../load-balancer/src/balancer.ts' is older than output '../load-balancer/tsconfig.tsbuildinfo'
71
+ 5:39:47 AM - Project '../load-balancer/tsconfig.json' is up to date because newest input '../load-balancer/src/tests/productionProviderList.unit.test.ts' is older than output '../load-balancer/tsconfig.tsbuildinfo'
72
72
 
73
- 1:49:47 PM - Project '../puzzle-assets/tsconfig.json' is up to date because newest input '../puzzle-assets/src/background.ts' is older than output '../puzzle-assets/tsconfig.tsbuildinfo'
73
+ 5:39:47 AM - Project '../puzzle-assets/tsconfig.json' is up to date because newest input '../puzzle-assets/src/background.ts' is older than output '../puzzle-assets/tsconfig.tsbuildinfo'
74
74
 
75
- 1:49:47 PM - Project '../web-bot-auth/tsconfig.json' is up to date because newest input '../web-bot-auth/src/base64.ts' is older than output '../web-bot-auth/tsconfig.tsbuildinfo'
75
+ 5:39:47 AM - Project '../web-bot-auth/tsconfig.json' is up to date because newest input '../web-bot-auth/src/base64.ts' is older than output '../web-bot-auth/tsconfig.tsbuildinfo'
76
76
 
77
- 1:49:47 PM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
77
+ 5:39:47 AM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
78
78
 
79
- 1:49:47 PM - Building project '/home/runner/work/captcha/captcha/packages/provider/tsconfig.json'...
79
+ 5:39:47 AM - Building project '/home/runner/work/captcha/captcha/packages/provider/tsconfig.json'...
80
80
 
@@ -1,9 +1,9 @@
1
1
 
2
- > @prosopo/provider@5.10.1 build
2
+ > @prosopo/provider@5.10.2 build
3
3
  > npm run build:cross-env -- --mode ${NODE_ENV:-development}
4
4
 
5
5
 
6
- > @prosopo/provider@5.10.1 build:cross-env
6
+ > @prosopo/provider@5.10.2 build:cross-env
7
7
  > vite build --config vite.esm.config.ts --mode production
8
8
 
9
9
  ViteEsmConfig: .
@@ -63,7 +63,7 @@ ViteEsmConfig: .
63
63
  vite v8.1.5 building ssr environment for production...
64
64
  Bundle build started
65
65
  
66
- ✓ 110 modules transformed.
66
+ ✓ 111 modules transformed.
67
67
  rendering chunks...
68
68
  computing gzip size...
69
69
  dist/_virtual/_rolldown/runtime.js 0.04 kB │ gzip: 0.06 kB
@@ -103,6 +103,7 @@ dist/api/admin/apiRemoveAllDecisionMachinesEndpoint.js 1.27 k
103
103
  dist/api/admin/apiRemoveDecisionMachineEndpoint.js 1.29 kB │ gzip: 0.51 kB
104
104
  dist/tasks/dataset/datasetTasksUtils.js 1.30 kB │ gzip: 0.48 kB
105
105
  dist/compositeIpAddress.js 1.36 kB │ gzip: 0.55 kB
106
+ dist/tasks/detection/assignSecret.js 1.42 kB │ gzip: 0.74 kB
106
107
  dist/utils/clientMetaData.js 1.42 kB │ gzip: 0.72 kB
107
108
  dist/api/admin/apiGetAllDecisionMachinesEndpoint.js 1.48 kB │ gzip: 0.57 kB
108
109
  dist/schedulers/updateSpamEmailDomains.js 1.52 kB │ gzip: 0.60 kB
@@ -119,7 +120,6 @@ dist/api/handshakeTimingMiddleware.js 1.75 k
119
120
  dist/tasks/spam/updateSpamEmailDomains.js 1.87 kB │ gzip: 0.77 kB
120
121
  dist/api/captcha/signedAssetsResolver.js 1.89 kB │ gzip: 0.94 kB
121
122
  dist/api/admin/apiClearAllCountersEndpoint.js 1.89 kB │ gzip: 0.76 kB
122
- dist/api/captcha/assignDetectorBundle.js 1.90 kB │ gzip: 0.84 kB
123
123
  dist/tasks/frictionless/ipMatch.js 1.98 kB │ gzip: 1.02 kB
124
124
  dist/migrations/frictionlessLadder.js 2.12 kB │ gzip: 0.93 kB
125
125
  dist/tasks/puzzle/stratifiedSampler.js 2.14 kB │ gzip: 1.07 kB
@@ -132,10 +132,11 @@ dist/utils/dns.js 2.36 k
132
132
  dist/api/admin/apiDnsEventEndpoint.js 2.43 kB │ gzip: 0.96 kB
133
133
  dist/api/captcha/checkSpamEmail.js 2.47 kB │ gzip: 0.91 kB
134
134
  dist/tasks/puzzle/puzzleDifficulty.js 2.50 kB │ gzip: 1.09 kB
135
- dist/tasks/detection/getBotScore.js 2.52 kB │ gzip: 0.94 kB
135
+ dist/tasks/detection/getBotScore.js 2.55 kB │ gzip: 0.95 kB
136
136
  dist/api/captcha/submitPuzzleCaptchaSolution.js 2.55 kB │ gzip: 0.96 kB
137
137
  dist/api/forwardVerify.js 2.59 kB │ gzip: 1.10 kB
138
138
  dist/api/captcha/submitImageCaptchaSolution.js 2.59 kB │ gzip: 1.00 kB
139
+ dist/api/captcha/assignDetectorBundle.js 2.69 kB │ gzip: 1.11 kB
139
140
  dist/api/admin/apiAdminRoutesProvider.js 2.78 kB │ gzip: 0.55 kB
140
141
  dist/index.js 2.92 kB │ gzip: 0.80 kB
141
142
  dist/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.js 3.39 kB │ gzip: 1.32 kB
@@ -158,7 +159,7 @@ dist/api/captcha/getPuzzleCaptchaChallenge.js 6.84 k
158
159
  dist/api/healthzGeo.js 6.86 kB │ gzip: 2.68 kB
159
160
  dist/tasks/spam/checkTrafficFilter.js 7.16 kB │ gzip: 2.45 kB
160
161
  dist/api/metrics.js 7.36 kB │ gzip: 2.19 kB
161
- dist/tasks/detection/bundlePool.js 8.30 kB │ gzip: 3.21 kB
162
+ dist/tasks/detection/bundlePool.js 8.46 kB │ gzip: 3.25 kB
162
163
  dist/api/captcha/submitPoWCaptchaSolution.js 9.02 kB │ gzip: 2.76 kB
163
164
  dist/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.js 9.36 kB │ gzip: 1.80 kB
164
165
  dist/api/startProviderApi.js 9.93 kB │ gzip: 3.42 kB
@@ -167,17 +168,17 @@ dist/tasks/decisionMachine/decisionMachineRunner.js 11.93 k
167
168
  dist/api/blacklistRequestInspector.js 12.46 kB │ gzip: 4.37 kB
168
169
  dist/util.js 12.47 kB │ gzip: 3.20 kB
169
170
  dist/api/verify.js 15.12 kB │ gzip: 2.82 kB
170
- dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js 19.06 kB │ gzip: 4.61 kB
171
- dist/tasks/frictionless/frictionlessTasks.js 20.10 kB │ gzip: 5.46 kB
172
- dist/tasks/captchaManager.js 21.92 kB │ gzip: 6.32 kB
171
+ dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js 19.13 kB │ gzip: 4.63 kB
172
+ dist/tasks/frictionless/frictionlessTasks.js 20.12 kB │ gzip: 5.46 kB
173
173
  dist/tasks/puzzleCaptcha/puzzleTasks.js 22.62 kB │ gzip: 5.10 kB
174
+ dist/tasks/captchaManager.js 23.01 kB │ gzip: 6.72 kB
174
175
  dist/tasks/powCaptcha/powTasks.js 23.47 kB │ gzip: 5.93 kB
175
176
  dist/tasks/imgCaptcha/imgCaptchaTasks.js 26.44 kB │ gzip: 6.31 kB
176
177
  dist/tasks/detection/decodeBehavior.js 32.26 kB │ gzip: 10.34 kB
177
178
  dist/tasks/detection/decodeSimd.js 36.06 kB │ gzip: 12.00 kB
178
- dist/tasks/detection/decodePayload.js 54.75 kB │ gzip: 17.77 kB
179
+ dist/tasks/detection/decodePayload.js 53.87 kB │ gzip: 18.06 kB
179
180
 
180
- ✓ built in 196ms
181
+ ✓ built in 170ms
181
182
  [copy-plugin] copying /home/runner/work/captcha/captcha/packages/provider/src/tasks/detection/decodeBehavior.js to /home/runner/work/captcha/captcha/packages/provider/dist/tasks/detection/decodeBehavior.js
182
183
  [copy-plugin] copying /home/runner/work/captcha/captcha/packages/provider/src/tasks/detection/decodePayload.js to /home/runner/work/captcha/captcha/packages/provider/dist/tasks/detection/decodePayload.js
183
184
  [copy-plugin] copying /home/runner/work/captcha/captcha/packages/provider/src/tasks/detection/decodeSimd.js to /home/runner/work/captcha/captcha/packages/provider/dist/tasks/detection/decodeSimd.js
package/CHANGELOG.md CHANGED
@@ -1,5 +1,100 @@
1
1
  # @prosopo/provider
2
2
 
3
+ ## 5.10.2
4
+ ### Patch Changes
5
+
6
+ - 98ab052: Serve a repeat caller the same detector bundle.
7
+
8
+ `/detector/assign` picked a bundle at random on every request. The provider now
9
+ records which bundle it served a caller and returns that one for a bounded
10
+ period, so a returning visitor gets a stable bundle instead of a fresh draw each
11
+ time. Any bundle performs identically, so this is not visible to users. If Redis
12
+ cannot answer, assign falls back to the previous behaviour rather than failing.
13
+
14
+ Also fixes the `assignDetectorBundle` tests, which were failing on main and had
15
+ gone unnoticed because the file was named `*.test.ts` while CI only runs
16
+ `*.unit.test.ts`. Renamed so it runs, and replaced the mock that
17
+ `clearAllMocks` was silently emptying.
18
+ - 028a158: Add optional field `dz` to detector payload
19
+ - 1f0598c: Derive the detector bundle for a caller instead of storing it.
20
+
21
+ `assignDetectorBundle` now picks the bundle with a keyed hash of the caller's
22
+ address, using a per-provider secret kept on the pool volume. Same caller, same
23
+ bundle, with nothing recorded and nothing to expire — which also means the
24
+ result no longer changes when the cache is unavailable.
25
+
26
+ Replaces the Redis client → bundle entry added in the previous patch, so
27
+ `bindDetectorBundleToClient` and its TTL constant are gone.
28
+ - 3958046: Keep a detector bundle binding for as long as its payload is accepted
29
+
30
+ The `detectorSessionId → bundleId` binding held the only key able to read a
31
+ detector payload, and expired after 60 seconds. The frictionless flow accepts a
32
+ payload for ten minutes (`DEFAULT_MAX_TIMESTAMP_AGE`). For nine of those ten
33
+ minutes the provider would therefore accept a payload it had already discarded
34
+ the means to decrypt: `resolveDecryptAttempts` returns an empty key list, the
35
+ decrypt loop never runs, the score is forced to 1 and the caller is challenged
36
+ despite nothing having been measured about them.
37
+
38
+ Sixty seconds is ample for the assign → submit gap in the normal case — it is
39
+ around 1.5s — but it only has to stall once to be lost, and a backgrounded
40
+ mobile tab is enough.
41
+
42
+ The two values are now one value. `DEFAULT_MAX_TIMESTAMP_AGE` moves from a
43
+ private constant in `frictionlessTasks` to `@prosopo/types`, the only package
44
+ both the provider and the database can see, and `DETECTOR_BUNDLE_TTL_SECONDS` is
45
+ derived from it instead of being written down a second time. A unit test pins
46
+ the relationship so they cannot drift apart again.
47
+
48
+ The TTL cannot now outlive the payload-age check, so this does not widen the
49
+ window in which any payload is usable. Bundle selection is unaffected: which
50
+ bundle a caller receives is derived from their IP and a server secret, not from
51
+ this binding's lifetime.
52
+ - 0cdf286: Log why a detector bundle could not be resolved
53
+
54
+ `resolveBundleByDetectorSession` returns undefined on three unrelated
55
+ conditions: the caller sent no detector session id, the
56
+ `detectorSessionId → bundleId` Redis binding was absent or had expired, or the
57
+ binding named a bundle this provider no longer holds in its pool.
58
+
59
+ All three surface downstream as the same thing — `resolveDecryptAttempts`
60
+ hands back an empty key list, the decrypt loop never runs, and the score is
61
+ forced to 1 with `DECRYPTION_FAILED`, so the caller is challenged despite
62
+ nothing having been measured about it. They need different fixes, but nothing
63
+ recorded which one had happened: the only line carrying that detail logged at
64
+ `debug`, which is below the level aggregated log search runs at, and it could
65
+ not have separated them anyway because `resolveDecryptAttempts` omits the
66
+ `bundleId` whenever the attempt list is empty.
67
+
68
+ Adds a single `info` line, `"Detector bundle not resolved"`, carrying a `cause`
69
+ of `noDetectorSession`, `noBinding` or `bundleNotInPool`, plus the `bundleId`
70
+ in the last case. It is emitted only on the failure path, so there is no cost
71
+ on the hot path, and the pool lookup now happens after the binding check rather
72
+ than before it.
73
+
74
+ No behavioural change — the same conditions return undefined as before.
75
+ - ea4e6aa: Persist the optional `cg` and `sm` session fields, which were not being written.
76
+ - 028a158: Add optional session field `dz`.
77
+ - Updated dependencies [98ab052]
78
+ - Updated dependencies [028a158]
79
+ - Updated dependencies [1f0598c]
80
+ - Updated dependencies [3958046]
81
+ - Updated dependencies [864ddde]
82
+ - Updated dependencies [028a158]
83
+ - @prosopo/database@4.0.33
84
+ - @prosopo/types-database@5.5.3
85
+ - @prosopo/types@5.8.3
86
+ - @prosopo/locale@3.4.2
87
+ - @prosopo/env@3.6.56
88
+ - @prosopo/types-env@2.11.2
89
+ - @prosopo/api@4.2.3
90
+ - @prosopo/api-express-router@3.1.87
91
+ - @prosopo/common@3.1.55
92
+ - @prosopo/datasets@3.1.82
93
+ - @prosopo/ipinfo@0.4.2
94
+ - @prosopo/keyring@2.9.89
95
+ - @prosopo/load-balancer@2.10.44
96
+ - @prosopo/user-access-policy@3.14.3
97
+
3
98
  ## 5.10.1
4
99
  ### Patch Changes
5
100
 
@@ -1 +1 @@
1
- {"version":3,"file":"assignDetectorBundle.d.ts","sourceRoot":"","sources":["../../../src/api/captcha/assignDetectorBundle.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;8BAgBpC,mBAAmB,WAEjC,OAAO,GAAG,gBAAgB,OAC1B,QAAQ,SACN,YAAY,KACjB,OAAO,CAAC,QAAQ,CAAC;AALrB,wBAwDG"}
1
+ {"version":3,"file":"assignDetectorBundle.d.ts","sourceRoot":"","sources":["../../../src/api/captcha/assignDetectorBundle.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;8BA4BpC,mBAAmB,WAEjC,OAAO,GAAG,gBAAgB,OAC1B,QAAQ,SACN,YAAY,KACjB,OAAO,CAAC,QAAQ,CAAC;AALrB,wBA+DG"}
@@ -1,19 +1,27 @@
1
- import { getDetectorBundlePool } from "../../tasks/detection/bundlePool.js";
1
+ import { getDetectorBundlePool, getDetectorBundlePoolDir } from "../../tasks/detection/bundlePool.js";
2
2
  import { Tasks } from "../../tasks/tasks.js";
3
+ import { getAssignSecret } from "../../tasks/detection/assignSecret.js";
4
+ import { normalizeRequestIp } from "../../utils/normalizeRequestIp.js";
3
5
  import { ApiParams } from "@prosopo/types";
6
+ import { createHmac } from "node:crypto";
4
7
  import { v4 } from "uuid";
5
8
  //#region src/api/captcha/assignDetectorBundle.ts
9
+ /** Pool position for this caller: a keyed hash of their address. */
10
+ var clientIndex = (ip, secret) => createHmac("sha256", secret).update(ip).digest().readUIntBE(0, 6);
6
11
  /**
7
12
  * Assigns a precomputed detector bundle for this detection session.
8
13
  *
9
- * When the provider has a non-empty pool it picks a random bundle, records the
10
- * (short-TTL) `detectorSessionId → bundleId` binding in Redis, and returns the
11
- * obfuscated detector script inline.
14
+ * The bundle is derived from the caller's address and this provider's secret,
15
+ * so a repeat caller keeps getting the same one with nothing stored and nothing
16
+ * to expire. Any bundle detects equally well, so this is not visible to users.
12
17
  *
13
- * When it cannot (no pool loaded, or Redis unavailable so the binding could not
14
- * be persisted) it returns `useProviderBundle: false`. There is NO bundled
15
- * detector and no legacy key pool to fall back to — the client sends an empty
16
- * token on the frictionless hop and the provider decides what to serve.
18
+ * The `detectorSessionId → bundleId` binding is still written to Redis; the
19
+ * decrypt side resolves the session's bundle from it on the next hop.
20
+ *
21
+ * Returns `useProviderBundle: false` when there is no pool, no secret, or Redis
22
+ * could not store that binding. There is NO bundled detector to fall back to —
23
+ * the client sends an empty token on the frictionless hop and the provider
24
+ * decides what to serve.
17
25
  */
18
26
  var assignDetectorBundle_default = (env) => async (req, res, _next) => {
19
27
  try {
@@ -23,7 +31,17 @@ var assignDetectorBundle_default = (env) => async (req, res, _next) => {
23
31
  };
24
32
  const pool = getDetectorBundlePool();
25
33
  if (!pool || pool.size() === 0) return res.json(noBundle);
26
- const { bundleId, bundle } = pool.pickRandom();
34
+ const secret = getAssignSecret(getDetectorBundlePoolDir(), {
35
+ info: (msg, data) => req.logger.info(() => ({
36
+ msg,
37
+ data
38
+ })),
39
+ warn: (msg, data) => req.logger.warn(() => ({
40
+ msg,
41
+ data
42
+ }))
43
+ });
44
+ const { bundleId, bundle } = pool.at(clientIndex(normalizeRequestIp(req.ip, req.logger), secret));
27
45
  const detectorSessionId = `det-${v4()}`;
28
46
  const writeQueue = new Tasks(env, req.logger).frictionlessManager.writeQueue;
29
47
  if (!writeQueue) return res.json(noBundle);
@@ -1 +1 @@
1
- {"version":3,"file":"assignDetectorBundle.js","sourceRoot":"","sources":["../../../src/api/captcha/assignDetectorBundle.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,SAAS,EAAqC,MAAM,gBAAgB,CAAC;AAG9E,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,MAAM,CAAC;AAEpC,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAC5E,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAc7C,eAAe,CAAC,GAAwB,EAAE,EAAE,CAC3C,KAAK,EACJ,GAA+B,EAC/B,GAAa,EACb,KAAmB,EACC,EAAE;IACtB,IAAI,CAAC;QACJ,MAAM,QAAQ,GAAiC;YAC9C,CAAC,SAAS,CAAC,iBAAiB,CAAC,EAAE,KAAK;YACpC,MAAM,EAAE,IAAI;SACZ,CAAC;QAEF,MAAM,IAAI,GAAG,qBAAqB,EAAE,CAAC;QACrC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;YAChC,OAAO,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3B,CAAC;QAED,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QAC/C,MAAM,iBAAiB,GAAG,OAAO,MAAM,EAAE,EAAE,CAAC;QAM5C,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;QACzC,MAAM,UAAU,GAAG,KAAK,CAAC,mBAAmB,CAAC,UAAU,CAAC;QACxD,IAAI,CAAC,UAAU,EAAE,CAAC;YACjB,OAAO,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3B,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,mBAAmB,CAClD,iBAAiB,EACjB,QAAQ,CACR,CAAC;QACF,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,OAAO,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3B,CAAC;QAED,MAAM,QAAQ,GAAiC;YAC9C,CAAC,SAAS,CAAC,iBAAiB,CAAC,EAAE,IAAI;YACnC,CAAC,SAAS,CAAC,iBAAiB,CAAC,EAAE,iBAAiB;YAChD,CAAC,SAAS,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC,EAAE;YACrC,MAAM,EAAE,IAAI;SACZ,CAAC;QACF,OAAO,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QAGd,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;YACtB,GAAG,EAAE,+DAA+D;YACpE,GAAG;SACH,CAAC,CAAC,CAAC;QACJ,OAAO,GAAG,CAAC,IAAI,CAAC;YACf,CAAC,SAAS,CAAC,iBAAiB,CAAC,EAAE,KAAK;YACpC,MAAM,EAAE,IAAI;SAC2B,CAAC,CAAC;IAC3C,CAAC;AACF,CAAC,CAAC"}
1
+ {"version":3,"file":"assignDetectorBundle.js","sourceRoot":"","sources":["../../../src/api/captcha/assignDetectorBundle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=assignDetectorBundle.unit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assignDetectorBundle.unit.test.d.ts","sourceRoot":"","sources":["../../../src/api/captcha/assignDetectorBundle.unit.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,153 @@
1
+ import { mkdtempSync, readFileSync, rmSync, statSync, writeFileSync, } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
5
+ import { resetAssignSecretCache } from "../../tasks/detection/assignSecret.js";
6
+ import { initDetectorBundlePool } from "../../tasks/detection/bundlePool.js";
7
+ import assignDetectorBundle from "./assignDetectorBundle.js";
8
+ const cacheDetectorBundle = vi.fn(async (_detectorSessionId, _bundleId) => true);
9
+ vi.mock("../../tasks/index.js", () => ({
10
+ Tasks: class {
11
+ constructor() {
12
+ this.frictionlessManager = {
13
+ writeQueue: { cacheDetectorBundle },
14
+ };
15
+ }
16
+ },
17
+ }));
18
+ const makeReq = (ip = "203.0.113.1") => ({
19
+ ip,
20
+ body: { dapp: "site-key" },
21
+ logger: { warn: vi.fn(), info: vi.fn(), debug: vi.fn() },
22
+ });
23
+ const writePool = (dir, count) => {
24
+ const ids = [];
25
+ for (let i = 0; i < count; i++) {
26
+ const id = `bundle-${i}`;
27
+ writeFileSync(join(dir, `${id}.js`), `export default ${i};`);
28
+ writeFileSync(join(dir, `${id}.json`), JSON.stringify({ privateKey: `PK${i}`, innerConfig: `C${i}` }));
29
+ ids.push(id);
30
+ }
31
+ return ids;
32
+ };
33
+ const assignedBundleId = () => cacheDetectorBundle.mock.calls.at(-1)?.[1];
34
+ const assignOnceWith = async (ip) => {
35
+ const r = makeRes();
36
+ await assignDetectorBundle(env)(makeReq(ip), r.res, next);
37
+ return r;
38
+ };
39
+ const assignOnce = async (ip) => {
40
+ await assignOnceWith(ip);
41
+ };
42
+ const makeRes = () => {
43
+ const json = vi.fn((body) => body);
44
+ return { res: { json }, json };
45
+ };
46
+ const env = {};
47
+ const next = vi.fn();
48
+ describe("assignDetectorBundle", () => {
49
+ let dir;
50
+ beforeEach(() => {
51
+ dir = mkdtempSync(join(tmpdir(), "assign-"));
52
+ cacheDetectorBundle.mockClear();
53
+ resetAssignSecretCache();
54
+ });
55
+ afterEach(() => {
56
+ rmSync(dir, { recursive: true, force: true });
57
+ vi.clearAllMocks();
58
+ });
59
+ it("returns useProviderBundle:false when the pool is empty", async () => {
60
+ initDetectorBundlePool(dir);
61
+ const { res, json } = makeRes();
62
+ await assignDetectorBundle(env)(makeReq(), res, next);
63
+ expect(json).toHaveBeenCalledWith({
64
+ useProviderBundle: false,
65
+ status: "ok",
66
+ });
67
+ expect(cacheDetectorBundle).not.toHaveBeenCalled();
68
+ });
69
+ it("assigns a bundle, stores the binding, and returns the script", async () => {
70
+ writeFileSync(join(dir, "bundle-0.js"), "export default 1;");
71
+ writeFileSync(join(dir, "bundle-0.json"), JSON.stringify({ privateKey: "PK", innerConfig: "C" }));
72
+ initDetectorBundlePool(dir);
73
+ const { res, json } = makeRes();
74
+ await assignDetectorBundle(env)(makeReq(), res, next);
75
+ expect(cacheDetectorBundle).toHaveBeenCalledTimes(1);
76
+ const [detSessionId, bundleId] = cacheDetectorBundle.mock.calls[0];
77
+ expect(bundleId).toBe("bundle-0");
78
+ expect(detSessionId).toMatch(/^det-/);
79
+ const body = json.mock.calls[0]?.[0];
80
+ expect(body.useProviderBundle).toBe(true);
81
+ expect(body.detectorScript).toBe("export default 1;");
82
+ expect(body.detectorSessionId).toBe(detSessionId);
83
+ });
84
+ it("falls back to bundled when the Redis binding cannot be stored", async () => {
85
+ writeFileSync(join(dir, "bundle-0.js"), "JS");
86
+ writeFileSync(join(dir, "bundle-0.json"), JSON.stringify({ privateKey: "PK", innerConfig: "C" }));
87
+ initDetectorBundlePool(dir);
88
+ cacheDetectorBundle.mockResolvedValueOnce(false);
89
+ const { res, json } = makeRes();
90
+ await assignDetectorBundle(env)(makeReq(), res, next);
91
+ expect(json).toHaveBeenCalledWith({
92
+ useProviderBundle: false,
93
+ status: "ok",
94
+ });
95
+ });
96
+ it("serves one caller the same bundle across many assigns", async () => {
97
+ writePool(dir, 50);
98
+ initDetectorBundlePool(dir);
99
+ const served = new Set();
100
+ for (let i = 0; i < 200; i++) {
101
+ await assignOnce();
102
+ served.add(assignedBundleId());
103
+ }
104
+ expect(served.size).toBe(1);
105
+ });
106
+ it("keeps serving that bundle after the secret is re-read from disk", async () => {
107
+ writePool(dir, 50);
108
+ initDetectorBundlePool(dir);
109
+ await assignOnce();
110
+ const first = assignedBundleId();
111
+ resetAssignSecretCache();
112
+ await assignOnce();
113
+ expect(assignedBundleId()).toBe(first);
114
+ });
115
+ it("returns the bundle body matching the id it recorded", async () => {
116
+ writePool(dir, 50);
117
+ initDetectorBundlePool(dir);
118
+ const { json } = await assignOnceWith();
119
+ const body = json.mock.calls[0]?.[0];
120
+ expect(body.detectorScript).toBe(`export default ${assignedBundleId().replace("bundle-", "")};`);
121
+ });
122
+ it("spreads distinct callers across the pool", async () => {
123
+ writePool(dir, 50);
124
+ initDetectorBundlePool(dir);
125
+ const served = new Set();
126
+ for (let i = 0; i < 60; i++) {
127
+ await assignOnce(`203.0.113.${i}`);
128
+ served.add(assignedBundleId());
129
+ }
130
+ expect(served.size).toBeGreaterThan(1);
131
+ });
132
+ it("still assigns when Redis holds no client state", async () => {
133
+ writePool(dir, 50);
134
+ initDetectorBundlePool(dir);
135
+ const { json } = await assignOnceWith();
136
+ const body = json.mock.calls[0]?.[0];
137
+ expect(body.useProviderBundle).toBe(true);
138
+ expect(assignedBundleId()).toMatch(/^bundle-\d+$/);
139
+ });
140
+ it("writes the secret 0600 and leaves it alone on reload", async () => {
141
+ writePool(dir, 50);
142
+ initDetectorBundlePool(dir);
143
+ await assignOnce();
144
+ const secretPath = join(dir, ".assign-secret");
145
+ const first = readFileSync(secretPath);
146
+ expect(first.length).toBe(32);
147
+ expect(statSync(secretPath).mode & 0o777).toBe(0o600);
148
+ resetAssignSecretCache();
149
+ await assignOnce();
150
+ expect(readFileSync(secretPath)).toEqual(first);
151
+ });
152
+ });
153
+ //# sourceMappingURL=assignDetectorBundle.unit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assignDetectorBundle.unit.test.js","sourceRoot":"","sources":["../../../src/api/captcha/assignDetectorBundle.unit.test.ts"],"names":[],"mappings":"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"}