@prosopo/provider 5.12.3 → 5.13.1

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 (45) hide show
  1. package/.turbo/turbo-build$colon$cjs.log +9 -9
  2. package/.turbo/turbo-build$colon$tsc.log +27 -27
  3. package/.turbo/turbo-build.log +10 -10
  4. package/CHANGELOG.md +102 -0
  5. package/dist/cjs/compositeIpAddress.cjs +44 -0
  6. package/dist/cjs/index.cjs +1 -0
  7. package/dist/cjs/tasks/captchaManager.cjs +16 -1
  8. package/dist/cjs/tasks/detection/bundlePool.cjs +4 -3
  9. package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasks.cjs +4 -0
  10. package/dist/cjs/tasks/powCaptcha/powTasks.cjs +16 -2
  11. package/dist/cjs/tasks/puzzleCaptcha/puzzleTasks.cjs +4 -0
  12. package/dist/compositeIpAddress.d.ts +1 -0
  13. package/dist/compositeIpAddress.d.ts.map +1 -1
  14. package/dist/compositeIpAddress.js +44 -1
  15. package/dist/compositeIpAddress.js.map +1 -1
  16. package/dist/index.js +2 -2
  17. package/dist/tasks/captchaManager.d.ts +1 -1
  18. package/dist/tasks/captchaManager.d.ts.map +1 -1
  19. package/dist/tasks/captchaManager.js +16 -1
  20. package/dist/tasks/captchaManager.js.map +1 -1
  21. package/dist/tasks/detection/bundlePool.d.ts.map +1 -1
  22. package/dist/tasks/detection/bundlePool.js +4 -3
  23. package/dist/tasks/detection/bundlePool.js.map +1 -1
  24. package/dist/tasks/detection/bundlePool.test.js +30 -0
  25. package/dist/tasks/detection/bundlePool.test.js.map +1 -1
  26. package/dist/tasks/imgCaptcha/imgCaptchaTasks.d.ts.map +1 -1
  27. package/dist/tasks/imgCaptcha/imgCaptchaTasks.js +4 -0
  28. package/dist/tasks/imgCaptcha/imgCaptchaTasks.js.map +1 -1
  29. package/dist/tasks/powCaptcha/powTasks.d.ts.map +1 -1
  30. package/dist/tasks/powCaptcha/powTasks.js +17 -3
  31. package/dist/tasks/powCaptcha/powTasks.js.map +1 -1
  32. package/dist/tasks/puzzleCaptcha/puzzleTasks.d.ts.map +1 -1
  33. package/dist/tasks/puzzleCaptcha/puzzleTasks.js +4 -0
  34. package/dist/tasks/puzzleCaptcha/puzzleTasks.js.map +1 -1
  35. package/dist/tests/unit/compositeIpAddress.unit.test.js +37 -1
  36. package/dist/tests/unit/compositeIpAddress.unit.test.js.map +1 -1
  37. package/dist/tests/unit/tasks/captchaManager.unit.test.js +41 -0
  38. package/dist/tests/unit/tasks/captchaManager.unit.test.js.map +1 -1
  39. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.unit.test.js +4 -0
  40. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.unit.test.js.map +1 -1
  41. package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.js +104 -1
  42. package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.js.map +1 -1
  43. package/dist/tests/unit/tasks/puzzleCaptcha/puzzleTasks.unit.test.js +6 -0
  44. package/dist/tests/unit/tasks/puzzleCaptcha/puzzleTasks.unit.test.js.map +1 -1
  45. package/package.json +14 -14
@@ -1,5 +1,5 @@
1
1
 
2
- > @prosopo/provider@5.12.3 build:cjs
2
+ > @prosopo/provider@5.13.1 build:cjs
3
3
  > NODE_ENV=${NODE_ENV:-development}; vite build --config vite.cjs.config.ts --mode $NODE_ENV
4
4
 
5
5
  ViteCommonJSConfig: .
@@ -104,7 +104,6 @@ dist/cjs/tasks/puzzle/backgroundBuffer.cjs 1
104
104
  dist/cjs/utils/clientMetaData.cjs 1.48 kB │ gzip: 0.72 kB
105
105
  dist/cjs/tasks/detection/assignSecret.cjs 1.48 kB │ gzip: 0.75 kB
106
106
  dist/cjs/api/assetDocument.cjs 1.50 kB │ gzip: 0.81 kB
107
- dist/cjs/compositeIpAddress.cjs 1.55 kB │ gzip: 0.57 kB
108
107
  dist/cjs/api/admin/apiGetAllDecisionMachinesEndpoint.cjs 1.59 kB │ gzip: 0.60 kB
109
108
  dist/cjs/schedulers/updateSpamEmailDomains.cjs 1.64 kB │ gzip: 0.62 kB
110
109
  dist/cjs/utils/honeypot/encoders.cjs 1.66 kB │ gzip: 0.67 kB
@@ -137,6 +136,7 @@ dist/cjs/api/public.cjs 2
137
136
  dist/cjs/api/captcha/maintenanceModeResponses.cjs 2.91 kB │ gzip: 0.77 kB
138
137
  dist/cjs/api/captcha/submitImageCaptchaSolution.cjs 2.93 kB │ gzip: 1.03 kB
139
138
  dist/cjs/tasks/detection/getBotScore.cjs 3.37 kB │ gzip: 1.33 kB
139
+ dist/cjs/compositeIpAddress.cjs 3.39 kB │ gzip: 1.36 kB
140
140
  dist/cjs/api/captcha/assetSealFormat.cjs 3.59 kB │ gzip: 1.37 kB
141
141
  dist/cjs/api/captcha/assignDetectorBundle.cjs 3.63 kB │ gzip: 1.37 kB
142
142
  dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.cjs 3.66 kB │ gzip: 1.35 kB
@@ -153,7 +153,7 @@ dist/cjs/tasks/puzzle/puzzleRenderer.cjs 4
153
153
  dist/cjs/api/captcha.cjs 4.51 kB │ gzip: 1.40 kB
154
154
  dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.cjs 4.56 kB │ gzip: 1.07 kB
155
155
  dist/cjs/tasks/dnsEvent/enrichDnsEvent.cjs 4.84 kB │ gzip: 1.74 kB
156
- dist/cjs/index.cjs 4.95 kB │ gzip: 1.03 kB
156
+ dist/cjs/index.cjs 5.02 kB │ gzip: 1.05 kB
157
157
  dist/cjs/tasks/tasks.cjs 5.07 kB │ gzip: 1.57 kB
158
158
  dist/cjs/tasks/detection/decoderPool.cjs 5.47 kB │ gzip: 2.06 kB
159
159
  dist/cjs/util/usageCounters.cjs 5.72 kB │ gzip: 2.03 kB
@@ -163,7 +163,7 @@ dist/cjs/api/captcha/getImageCaptchaChallenge.cjs 7
163
163
  dist/cjs/tasks/spam/checkTrafficFilter.cjs 7.47 kB │ gzip: 2.48 kB
164
164
  dist/cjs/api/captcha/getPuzzleCaptchaChallenge.cjs 7.56 kB │ gzip: 2.03 kB
165
165
  dist/cjs/api/captcha/submitPoWCaptchaSolution.cjs 9.12 kB │ gzip: 2.71 kB
166
- dist/cjs/tasks/detection/bundlePool.cjs 9.24 kB │ gzip: 3.37 kB
166
+ dist/cjs/tasks/detection/bundlePool.cjs 9.29 kB │ gzip: 3.38 kB
167
167
  dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.cjs 10.33 kB │ gzip: 1.82 kB
168
168
  dist/cjs/api/startProviderApi.cjs 11.73 kB │ gzip: 3.72 kB
169
169
  dist/cjs/tasks/client/clientTasks.cjs 11.73 kB │ gzip: 3.04 kB
@@ -174,12 +174,12 @@ dist/cjs/api/blacklistRequestInspector.cjs 13
174
174
  dist/cjs/api/verify.cjs 16.79 kB │ gzip: 2.93 kB
175
175
  dist/cjs/tasks/frictionless/frictionlessTasks.cjs 18.37 kB │ gzip: 4.89 kB
176
176
  dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/handler.cjs 20.05 kB │ gzip: 4.59 kB
177
- dist/cjs/tasks/captchaManager.cjs 23.94 kB │ gzip: 7.10 kB
178
- dist/cjs/tasks/puzzleCaptcha/puzzleTasks.cjs 24.12 kB │ gzip: 5.17 kB
179
- dist/cjs/tasks/powCaptcha/powTasks.cjs 24.99 kB │ gzip: 6.02 kB
180
- dist/cjs/tasks/imgCaptcha/imgCaptchaTasks.cjs 27.94 kB │ gzip: 6.36 kB
177
+ dist/cjs/tasks/puzzleCaptcha/puzzleTasks.cjs 24.30 kB │ gzip: 5.20 kB
178
+ dist/cjs/tasks/captchaManager.cjs 24.67 kB │ gzip: 7.42 kB
179
+ dist/cjs/tasks/powCaptcha/powTasks.cjs 25.74 kB │ gzip: 6.20 kB
180
+ dist/cjs/tasks/imgCaptcha/imgCaptchaTasks.cjs 28.13 kB │ gzip: 6.40 kB
181
181
 
182
- ✓ built in 147ms
182
+ ✓ built in 195ms
183
183
  [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
184
184
  [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
185
185
  [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.12.3 build:tsc
2
+ > @prosopo/provider@5.13.1 build:tsc
3
3
  > tsc --build --verbose
4
4
 
5
- 10:41:07 PM - Projects in this build:
5
+ 3:37:46 PM - 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
- 10:41:07 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
+ 3:37:46 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'
32
32
 
33
- 10:41:07 PM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/url.ts' is older than output '../util/tsconfig.tsbuildinfo'
33
+ 3:37:46 PM - 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
- 10:41:07 PM - Project '../logger/tsconfig.json' is up to date because newest input '../logger/src/tests/loggerBrowser.unit.test.ts' is older than output '../logger/tsconfig.tsbuildinfo'
35
+ 3:37:46 PM - 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
- 10:41:07 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
+ 3:37:46 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'
38
38
 
39
- 10:41:07 PM - Project '../locale/tsconfig.json' is up to date because newest input '../locale/src/process.ts' is older than output '../locale/tsconfig.tsbuildinfo'
39
+ 3:37:46 PM - 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
- 10:41:07 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
+ 3:37:46 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'
42
42
 
43
- 10:41:07 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
+ 3:37:46 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'
44
44
 
45
- 10:41:07 PM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/tests/utils/objectUtils.unit.test.ts' is older than output '../common/tsconfig.tsbuildinfo'
45
+ 3:37:46 PM - 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
- 10:41:07 PM - Project '../ipinfo/tsconfig.json' is up to date because newest input '../ipinfo/src/index.ts' is older than output '../ipinfo/tsconfig.tsbuildinfo'
47
+ 3:37:46 PM - Project '../ipinfo/tsconfig.json' is up to date because newest input '../ipinfo/src/IpInfoService.ts' is older than output '../ipinfo/tsconfig.tsbuildinfo'
48
48
 
49
- 10:41:07 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
+ 3:37:46 PM - Project '../redis-client/tsconfig.json' is up to date because newest input '../redis-client/src/tests/testRedisConnection.ts' is older than output '../redis-client/tsconfig.tsbuildinfo'
50
50
 
51
- 10:41:07 PM - Project '../api/tsconfig.json' is up to date because newest input '../api/src/index.ts' is older than output '../api/tsconfig.tsbuildinfo'
51
+ 3:37:46 PM - 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
- 10:41:07 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
+ 3:37:46 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'
54
54
 
55
- 10:41:07 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
+ 3:37:47 PM - Project '../types-database/tsconfig.json' is up to date because newest input '../types-database/src/types.test-d.ts' is older than output '../types-database/tsconfig.tsbuildinfo'
56
56
 
57
- 10:41:07 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
+ 3:37: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'
58
58
 
59
- 10:41:07 PM - Project '../keyring/tsconfig.json' is up to date because newest input '../keyring/src/index.ts' is older than output '../keyring/tsconfig.tsbuildinfo'
59
+ 3:37:47 PM - 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
- 10:41:07 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
+ 3:37: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'
62
62
 
63
- 10:41:07 PM - Project '../env/tsconfig.json' is up to date because newest input '../env/src/index.ts' is older than output '../env/tsconfig.tsbuildinfo'
63
+ 3:37:47 PM - 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
- 10:41:07 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
+ 3:37:47 PM - Project '../api-express-router/tsconfig.json' is up to date because newest input '../api-express-router/src/tests/unit/testDoubles.ts' is older than output '../api-express-router/tsconfig.tsbuildinfo'
66
66
 
67
- 10:41:07 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
+ 3:37: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'
68
68
 
69
- 10:41:07 PM - Project '../datasets/tsconfig.json' is up to date because newest input '../datasets/src/tests/captcha.unit.test.ts' is older than output '../datasets/tsconfig.tsbuildinfo'
69
+ 3:37:47 PM - Project '../datasets/tsconfig.json' is up to date because newest input '../datasets/src/tests/captcha.unit.test.ts' is older than output '../datasets/tsconfig.tsbuildinfo'
70
70
 
71
- 10:41:07 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
+ 3:37:47 PM - Project '../load-balancer/tsconfig.json' is up to date because newest input '../load-balancer/src/developmentProviderUrl.ts' is older than output '../load-balancer/tsconfig.tsbuildinfo'
72
72
 
73
- 10:41:07 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
+ 3:37: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'
74
74
 
75
- 10:41:07 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
+ 3:37: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'
76
76
 
77
- 10:41:07 PM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
77
+ 3:37:47 PM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
78
78
 
79
- 10:41:07 PM - Building project '/home/runner/work/captcha/captcha/packages/provider/tsconfig.json'...
79
+ 3:37:47 PM - Building project '/home/runner/work/captcha/captcha/packages/provider/tsconfig.json'...
80
80
 
@@ -1,9 +1,9 @@
1
1
 
2
- > @prosopo/provider@5.12.3 build
2
+ > @prosopo/provider@5.13.1 build
3
3
  > npm run build:cross-env -- --mode ${NODE_ENV:-development}
4
4
 
5
5
 
6
- > @prosopo/provider@5.12.3 build:cross-env
6
+ > @prosopo/provider@5.13.1 build:cross-env
7
7
  > vite build --config vite.esm.config.ts --mode production
8
8
 
9
9
  ViteEsmConfig: .
@@ -106,7 +106,6 @@ dist/api/admin/apiRemoveAllDecisionMachinesEndpoint.js 1.27 k
106
106
  dist/api/admin/apiRemoveDecisionMachineEndpoint.js 1.29 kB │ gzip: 0.51 kB
107
107
  dist/tasks/dataset/datasetTasksUtils.js 1.30 kB │ gzip: 0.48 kB
108
108
  dist/api/assetDocument.js 1.36 kB │ gzip: 0.75 kB
109
- dist/compositeIpAddress.js 1.36 kB │ gzip: 0.55 kB
110
109
  dist/tasks/detection/assignSecret.js 1.42 kB │ gzip: 0.74 kB
111
110
  dist/utils/clientMetaData.js 1.42 kB │ gzip: 0.72 kB
112
111
  dist/api/admin/apiGetAllDecisionMachinesEndpoint.js 1.48 kB │ gzip: 0.57 kB
@@ -141,7 +140,8 @@ dist/api/captcha/submitPuzzleCaptchaSolution.js 2.55 k
141
140
  dist/api/forwardVerify.js 2.59 kB │ gzip: 1.10 kB
142
141
  dist/api/captcha/submitImageCaptchaSolution.js 2.59 kB │ gzip: 1.00 kB
143
142
  dist/api/admin/apiAdminRoutesProvider.js 2.78 kB │ gzip: 0.55 kB
144
- dist/index.js 2.92 kB │ gzip: 0.80 kB
143
+ dist/index.js 2.95 kB │ gzip: 0.81 kB
144
+ dist/compositeIpAddress.js 3.15 kB │ gzip: 1.34 kB
145
145
  dist/tasks/detection/getBotScore.js 3.29 kB │ gzip: 1.32 kB
146
146
  dist/api/captcha/assetSealFormat.js 3.38 kB │ gzip: 1.34 kB
147
147
  dist/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.js 3.39 kB │ gzip: 1.32 kB
@@ -167,7 +167,7 @@ dist/api/captcha/getPuzzleCaptchaChallenge.js 6.84 k
167
167
  dist/api/healthzGeo.js 6.86 kB │ gzip: 2.68 kB
168
168
  dist/tasks/spam/checkTrafficFilter.js 7.16 kB │ gzip: 2.45 kB
169
169
  dist/api/captcha/submitPoWCaptchaSolution.js 8.60 kB │ gzip: 2.66 kB
170
- dist/tasks/detection/bundlePool.js 8.80 kB │ gzip: 3.32 kB
170
+ dist/tasks/detection/bundlePool.js 8.86 kB │ gzip: 3.34 kB
171
171
  dist/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.js 9.36 kB │ gzip: 1.80 kB
172
172
  dist/api/startProviderApi.js 10.02 kB │ gzip: 3.45 kB
173
173
  dist/api/metrics.js 11.27 kB │ gzip: 3.53 kB
@@ -178,12 +178,12 @@ dist/api/blacklistRequestInspector.js 12.75 k
178
178
  dist/api/verify.js 15.12 kB │ gzip: 2.82 kB
179
179
  dist/tasks/frictionless/frictionlessTasks.js 17.80 kB │ gzip: 4.86 kB
180
180
  dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js 18.54 kB │ gzip: 4.48 kB
181
- dist/tasks/puzzleCaptcha/puzzleTasks.js 22.71 kB │ gzip: 5.12 kB
182
- dist/tasks/captchaManager.js 23.44 kB │ gzip: 7.03 kB
183
- dist/tasks/powCaptcha/powTasks.js 23.61 kB │ gzip: 5.97 kB
184
- dist/tasks/imgCaptcha/imgCaptchaTasks.js 26.42 kB │ gzip: 6.30 kB
181
+ dist/tasks/puzzleCaptcha/puzzleTasks.js 22.90 kB │ gzip: 5.16 kB
182
+ dist/tasks/captchaManager.js 24.17 kB │ gzip: 7.35 kB
183
+ dist/tasks/powCaptcha/powTasks.js 24.32 kB │ gzip: 6.15 kB
184
+ dist/tasks/imgCaptcha/imgCaptchaTasks.js 26.61 kB │ gzip: 6.34 kB
185
185
 
186
- ✓ built in 150ms
186
+ ✓ built in 196ms
187
187
  [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
188
188
  [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
189
189
  [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,107 @@
1
1
  # @prosopo/provider
2
2
 
3
+ ## 5.13.1
4
+ ### Patch Changes
5
+
6
+ - fda0eba: Decision machines can now see which page the captcha was rendered on. `currentUrl` (the top-frame page) and `iframeUrl` (the widget's own frame, when embedded) were already stored on the session and already read back from the database, but the verify-time path never passed them to the decision machine, so rules always saw them as undefined.
7
+
8
+ They are now forwarded on all three verify paths (image, PoW and puzzle). No behaviour changes on its own — it just makes the fields available to rules that need to treat an embedded widget differently from a first-party one.
9
+
10
+ Both values are reported by the client and are not checked against the request's Origin or Referer header, so a rule must not hand out an exemption on the strength of these fields alone.
11
+ - 1728cd0: Carry the detector bundle's `keyMap` through the pool push.
12
+
13
+ `keyMap` is an opaque per-bundle decode parameter, written alongside each
14
+ bundle by the pool build and meaningless without it — the same contract as
15
+ `payloadLayout`. The admin pool-replace body schema never declared it, so zod
16
+ stripped it from every push, and the endpoint's persist step then wrote the
17
+ bundle back to disk without it.
18
+
19
+ The result was a pool the provider served but could not decode: the push
20
+ returned success with `persisted: true`, the bundles loaded and sessions were
21
+ assigned them, but what they produced could not be read. Pools copied onto the
22
+ volume were unaffected, because that path never goes through the schema.
23
+
24
+ Adds `keyMap` to `ReplaceDetectorPoolBody` and writes it in
25
+ `persistDetectorBundlePool`.
26
+ - 20542d8: Send page scroll events with the captcha's behavioural data.
27
+
28
+ The widget now passes a fourth collector, the page's scroll position and the
29
+ time of each scroll, alongside mouse, touch and click data, and the provider
30
+ stores it as `c4` on the captcha record. People scroll in uneven bursts while
31
+ bots tend to scroll at a steady rate, so this gives detection something to
32
+ work with. Detector bundles that predate the scroll tracker simply send no
33
+ `c4`.
34
+ - Updated dependencies [fda0eba]
35
+ - Updated dependencies [1728cd0]
36
+ - Updated dependencies [20542d8]
37
+ - Updated dependencies [eebe6ee]
38
+ - @prosopo/types@5.10.2
39
+ - @prosopo/types-database@5.6.5
40
+ - @prosopo/api@4.3.5
41
+ - @prosopo/api-express-router@3.1.95
42
+ - @prosopo/database@4.0.41
43
+ - @prosopo/datasets@3.1.91
44
+ - @prosopo/env@3.6.64
45
+ - @prosopo/ipinfo@0.4.10
46
+ - @prosopo/keyring@2.9.97
47
+ - @prosopo/load-balancer@2.11.1
48
+ - @prosopo/types-env@2.11.10
49
+ - @prosopo/user-access-policy@3.14.11
50
+
51
+ ## 5.13.0
52
+ ### Minor Changes
53
+
54
+ - 85a1bb9: Seal captcha image paths into opaque URLs, opened by a CDN edge script.
55
+
56
+ An image URL used to say which image it was: `https://cdn.example.net/dataset/images/abc.webp`. Signed URLs already stop a harvested URL being refetched forever, but the path stays in plain sight, and the path is a stable identifier for the image behind it — which makes it a usable key for caching answers between sessions.
57
+
58
+ The path is now AES-256-GCM sealed, with an expiry, into an opaque blob drawn fresh every time:
59
+
60
+ ```
61
+ https://cdn.example.net/s/AgHk1n...Qx9.webp
62
+ ```
63
+
64
+ The edge script opens it, puts the canonical path back on the request, and the CDN serves the one object that path names. The same image therefore has a different URL in every session, with nothing in the URL to group those together, and there is still only one cached copy and one stored object.
65
+
66
+ Sealing happens strictly below `item.data`. The item hash, the captchaId and the datasetId are untouched, so nothing about the dataset or how a solution is verified changes.
67
+
68
+ The provider seals with `node:crypto`, because `AssetsResolver.resolveAsset` is synchronous and every WebCrypto call returns a promise; the edge opens with WebCrypto, because that is what its runtime has. The wire format, the validation and the base64url alphabet live in one module both ends import, and a test seals with one end and opens with the other so the two cannot drift.
69
+
70
+ Inert unless its key is configured, since a sealed URL is unservable until the matching edge script is live. Configured, it takes precedence over the signed resolver; leaving the signing key in place keeps that as the fallback for any zone not yet moved over. More than one key opens at a time, so keys can be rotated without a flag day. `npm run -w @prosopo/provider build:edge` emits the edge script as a single file. Rollout order, environment variables and rotation are documented for operators outside this repository.
71
+
72
+ Test coverage: 51 unit tests over the wire format, the sealer, the resolver and the edge handler — round trip, tamper, truncation, expiry at the boundary, swapped key id, rotation across two live keys, path traversal and prefix confinement, URL shape and per-call uniqueness, passthrough of requests that are not sealed, idempotency when the handler runs at both hooks, and the provider-seals/edge-opens interop.
73
+
74
+ ### Patch Changes
75
+
76
+ - 4c9b84b: Escalate a PoW solve to an image captcha when it arrives from a different address than the challenge.
77
+
78
+ A PoW challenge is bound to the user account and the site key and to nothing about where the request came from, so a solved challenge can be carried to any host that wants a free pass. The issuing address is already on the record, so binding to it costs nothing: `verifyPowCaptchaSolution` now compares it against the address submitting the solve and escalates when they differ, under a new `IP_CHANGED` reason.
79
+
80
+ Escalation, not denial. A phone handing off between towers mid-solve is a real user and should pay a picture rather than be turned away. For the same reason IPv6 is judged on the /64 alone — the interface identifier in the low 64 bits rotates by design under RFC 8981, several times a day on one unchanged connection — and an address we could not read counts as unchanged.
81
+
82
+ Only applies where a session is linked, since escalation carries the originating session's risk profile forward. Missing coordinates still takes precedence as the reported reason when both fire.
83
+ - 2cca36e: Report a bot score of zero to Pro and Enterprise instead of dropping the field.
84
+
85
+ `canClientSeeScore` tested `score && tier && tier !== Tier.Free`. A session with nothing wrong with it scores 0, which is falsy, so `score` was omitted from the siteverify response for exactly those users: a paying customer got a score for every suspicious visitor and no score at all for their cleanest ones, which reads as "no score available" rather than "no risk".
86
+
87
+ Invisible in production today because `Math.random() * 0.3` is added to every score, making an exact 0 all but impossible. Removing that noise — captcha-private#4433, where this was found — is what would have exposed it.
88
+
89
+ The function also returned `number | boolean | undefined` from its `&&` chain; it now returns `boolean`.
90
+ - Updated dependencies [4c9b84b]
91
+ - Updated dependencies [f13bea8]
92
+ - @prosopo/types@5.10.1
93
+ - @prosopo/load-balancer@2.11.0
94
+ - @prosopo/api@4.3.4
95
+ - @prosopo/api-express-router@3.1.94
96
+ - @prosopo/database@4.0.40
97
+ - @prosopo/datasets@3.1.90
98
+ - @prosopo/env@3.6.63
99
+ - @prosopo/ipinfo@0.4.9
100
+ - @prosopo/keyring@2.9.96
101
+ - @prosopo/types-database@5.6.4
102
+ - @prosopo/types-env@2.11.9
103
+ - @prosopo/user-access-policy@3.14.10
104
+
3
105
  ## 5.12.3
4
106
  ### Patch Changes
5
107
 
@@ -38,9 +38,53 @@ var getIpAddressFromComposite = (compositeIpAddress) => {
38
38
  }
39
39
  };
40
40
  var getBigInt = (number) => BigInt(number || 0n);
41
+ /**
42
+ * Mongo hands these back as Decimal128 rather than the `number | bigint` the
43
+ * type promises, and `BigInt(Decimal128)` throws. Route through the string
44
+ * form, and report a value we cannot read as absent rather than throwing.
45
+ */
46
+ var readPart = (value) => {
47
+ if (value === void 0 || value === null) return void 0;
48
+ try {
49
+ return BigInt(value.toString());
50
+ } catch {
51
+ return;
52
+ }
53
+ };
54
+ /**
55
+ * The value `getCompositeIpAddress` returns when it could not read an
56
+ * address at all. 0.0.0.0 is not a client address, so nothing legitimate is
57
+ * being swallowed by treating it as "we don't know".
58
+ */
59
+ var isUnknownIp = (ip) => ip.type === _prosopo_types.IpAddressType.v4 && readPart(ip.lower) === 0n;
60
+ /**
61
+ * Do two observations of a client come from the same place on the network?
62
+ *
63
+ * v4 is compared exactly. v6 is compared on the /64 prefix alone — the
64
+ * interface identifier in the low 64 bits is designed to rotate (RFC 8981
65
+ * privacy extensions), often several times a day on one unchanged connection,
66
+ * so comparing it would call an ordinary phone a different host.
67
+ *
68
+ * A value we do not actually have counts as the same — one that will not
69
+ * parse, or the all-zero v4 `getCompositeIpAddress` returns when handed an
70
+ * address it could not read. This answer gates extra friction for a real
71
+ * user, and a gap in what we recorded is not evidence against them.
72
+ */
73
+ var isSameIpOrigin = (a, b) => {
74
+ if (isUnknownIp(a) || isUnknownIp(b)) return true;
75
+ if (a.type !== b.type) return false;
76
+ const compare = (left, right) => {
77
+ const leftPart = readPart(left);
78
+ const rightPart = readPart(right);
79
+ if (leftPart === void 0 || rightPart === void 0) return true;
80
+ return leftPart === rightPart;
81
+ };
82
+ return a.type === _prosopo_types.IpAddressType.v6 ? compare(a.upper, b.upper) : compare(a.lower, b.lower);
83
+ };
41
84
  var never = () => {
42
85
  throw new Error("Unhandled type");
43
86
  };
44
87
  //#endregion
45
88
  exports.getCompositeIpAddress = getCompositeIpAddress;
46
89
  exports.getIpAddressFromComposite = getIpAddressFromComposite;
90
+ exports.isSameIpOrigin = isSameIpOrigin;
@@ -50,6 +50,7 @@ exports.getVerdictCache = require_blacklistRequestInspector.getVerdictCache;
50
50
  exports.handshakeTimingMiddleware = require_handshakeTimingMiddleware.handshakeTimingMiddleware;
51
51
  exports.headerCheckMiddleware = require_headerCheckMiddleware.headerCheckMiddleware;
52
52
  exports.ignoreMiddleware = require_ignoreMiddleware.ignoreMiddleware;
53
+ exports.isSameIpOrigin = require_compositeIpAddress.isSameIpOrigin;
53
54
  exports.isTlsAvailable = require_startProviderApi.isTlsAvailable;
54
55
  exports.ja4Middleware = require_ja4Middleware.ja4Middleware;
55
56
  exports.migrateFrictionlessLadder = require_frictionlessLadder.migrateFrictionlessLadder;
@@ -491,6 +491,7 @@ var CaptchaManager = class CaptchaManager {
491
491
  c1Length: result.collector1?.length || 0,
492
492
  c2Length: result.collector2?.length || 0,
493
493
  c3Length: result.collector3?.length || 0,
494
+ c4Length: result.collector4?.length || 0,
494
495
  deviceCapability: result.deviceCapability
495
496
  }
496
497
  }));
@@ -564,8 +565,22 @@ var CaptchaManager = class CaptchaManager {
564
565
  const ipInfo = currentIp ? await env.ipInfoService.lookup(currentIp) : recordIpInfo;
565
566
  return require_checkTrafficFilter.checkTrafficFilter(ipInfo, effective, require_enrichDnsEvent.extraIpInfosFromEnrichedDnsEvent(enrichedDnsEvent), enrichedDnsEvent?.pathValid);
566
567
  }
568
+ /**
569
+ * Does this client's tier entitle it to the bot score, and is there one?
570
+ *
571
+ * `score !== undefined`, not `score`: a session with nothing wrong with it
572
+ * scores 0, and a truthiness test dropped the field for exactly those
573
+ * users — a paying customer reading `score` got a present field for
574
+ * every suspicious visitor and a missing one for their cleanest. It has
575
+ * been invisible because `Math.random() * 0.3` makes an exact 0 all but
576
+ * impossible; taking that noise out (see captcha-private#4433) is what
577
+ * would expose it.
578
+ *
579
+ * Returning `boolean` rather than the old `number | boolean | undefined`,
580
+ * which came from `&&`-chaining the score and the tier.
581
+ */
567
582
  static canClientSeeScore(tier, score) {
568
- return score && tier && tier !== _prosopo_types.Tier.Free;
583
+ return score !== void 0 && tier !== _prosopo_types.Tier.Free;
569
584
  }
570
585
  };
571
586
  //#endregion
@@ -7,8 +7,8 @@ let node_path = require("node:path");
7
7
  * Loads a pool of precomputed, obfuscated detector bundles from disk and caches
8
8
  * them in memory — the Node analogue of the Rust "bumblebee" `bundle_manager`.
9
9
  * Each bundle is a `{id}.js` (served to the browser) paired with a `{id}.json`
10
- * (`{ privateKey, innerConfig, payloadLayout }`, kept server-side) produced by
11
- * the catcher `bundle:pool` build script.
10
+ * (`{ privateKey, innerConfig, payloadLayout, keyMap }`, kept server-side)
11
+ * produced by the catcher `bundle:pool` build script.
12
12
  *
13
13
  * A bundle is assigned per detector session by {@link DetectorBundlePool.at},
14
14
  * served by id, and resolved again at decryption time so the provider uses the
@@ -172,7 +172,8 @@ function persistDetectorBundlePool(bundles, dir = globalPoolDir) {
172
172
  privateKey: bundle.privateKey,
173
173
  innerConfig: bundle.innerConfig,
174
174
  ...bundle.release && { release: bundle.release },
175
- ...bundle.payloadLayout && { payloadLayout: bundle.payloadLayout }
175
+ ...bundle.payloadLayout && { payloadLayout: bundle.payloadLayout },
176
+ ...bundle.keyMap && { keyMap: bundle.keyMap }
176
177
  }), {
177
178
  encoding: "utf8",
178
179
  mode: 384
@@ -142,6 +142,7 @@ var ImgCaptchaManager = class extends require_captchaManager.CaptchaManager {
142
142
  mouseEventsCount: decryptedData.collector1?.length || 0,
143
143
  touchEventsCount: decryptedData.collector2?.length || 0,
144
144
  clickEventsCount: decryptedData.collector3?.length || 0,
145
+ scrollEventsCount: decryptedData.collector4?.length || 0,
145
146
  deviceCapability: decryptedData.deviceCapability
146
147
  }
147
148
  }));
@@ -149,6 +150,7 @@ var ImgCaptchaManager = class extends require_captchaManager.CaptchaManager {
149
150
  c1: decryptedData.collector1 || [],
150
151
  c2: decryptedData.collector2 || [],
151
152
  c3: decryptedData.collector3 || [],
153
+ c4: decryptedData.collector4 || [],
152
154
  d: decryptedData.deviceCapability
153
155
  };
154
156
  deviceCapability = decryptedData.deviceCapability;
@@ -550,6 +552,8 @@ var ImgCaptchaManager = class extends require_captchaManager.CaptchaManager {
550
552
  ruleType: sessionRecord?.ruleType,
551
553
  webView: sessionRecord?.webView,
552
554
  iFrame: sessionRecord?.iFrame,
555
+ currentUrl: sessionRecord?.currentUrl,
556
+ iframeUrl: sessionRecord?.iframeUrl,
553
557
  coords: solution.coords,
554
558
  synNs: sessionRecord?.synNs,
555
559
  synackNs: sessionRecord?.synackNs,
@@ -150,6 +150,7 @@ var PowCaptchaManager = class extends require_captchaManager.CaptchaManager {
150
150
  mouseEventsCount: decryptedData.collector1?.length || 0,
151
151
  touchEventsCount: decryptedData.collector2?.length || 0,
152
152
  clickEventsCount: decryptedData.collector3?.length || 0,
153
+ scrollEventsCount: decryptedData.collector4?.length || 0,
153
154
  deviceCapability: decryptedData.deviceCapability,
154
155
  captchaResult: correct ? "passed" : "failed"
155
156
  }
@@ -158,6 +159,7 @@ var PowCaptchaManager = class extends require_captchaManager.CaptchaManager {
158
159
  c1: decryptedData.collector1 || [],
159
160
  c2: decryptedData.collector2 || [],
160
161
  c3: decryptedData.collector3 || [],
162
+ c4: decryptedData.collector4 || [],
161
163
  d: decryptedData.deviceCapability
162
164
  };
163
165
  behavioralUpdates = {
@@ -190,14 +192,24 @@ var PowCaptchaManager = class extends require_captchaManager.CaptchaManager {
190
192
  await Promise.all(writePromises);
191
193
  if (!correct) return { verified: correct };
192
194
  const escalateForMissingCoords = !coords && Boolean(challengeRecord.sessionId);
195
+ const solveIp = require_compositeIpAddress.getCompositeIpAddress(ipAddress);
196
+ const escalateForIpChange = Boolean(challengeRecord.sessionId) && !require_compositeIpAddress.isSameIpOrigin(challengeRecord.ipAddress, solveIp);
197
+ if (escalateForIpChange) this.logger.info(() => ({
198
+ msg: "PoW solve arrived from a different address than the challenge",
199
+ data: {
200
+ challenge,
201
+ issuedType: challengeRecord.ipAddress.type,
202
+ solvedType: solveIp.type
203
+ }
204
+ }));
193
205
  let routingOutput = this.postPowContext ? await this.runPostPowRouting({
194
206
  challengeRecord,
195
207
  challengeSplit,
196
208
  behavioralDataPacked: decryptedBehavioralDataPacked
197
209
  }) : void 0;
198
- if (escalateForMissingCoords && routingOutput?.captchaType !== _prosopo_types.CaptchaType.image && routingOutput?.captchaType !== _prosopo_types.CaptchaType.puzzle) routingOutput = {
210
+ if ((escalateForMissingCoords || escalateForIpChange) && routingOutput?.captchaType !== _prosopo_types.CaptchaType.image && routingOutput?.captchaType !== _prosopo_types.CaptchaType.puzzle) routingOutput = {
199
211
  captchaType: _prosopo_types.CaptchaType.image,
200
- reason: _prosopo_types.FrictionlessReason.MISSING_COORDINATES
212
+ reason: escalateForMissingCoords ? _prosopo_types.FrictionlessReason.MISSING_COORDINATES : _prosopo_types.FrictionlessReason.IP_CHANGED
201
213
  };
202
214
  return {
203
215
  verified: correct,
@@ -499,6 +511,8 @@ var PowCaptchaManager = class extends require_captchaManager.CaptchaManager {
499
511
  ruleType: sessionRecord?.ruleType,
500
512
  webView: sessionRecord?.webView,
501
513
  iFrame: sessionRecord?.iFrame,
514
+ currentUrl: sessionRecord?.currentUrl,
515
+ iframeUrl: sessionRecord?.iframeUrl,
502
516
  coords: challengeRecord.coords,
503
517
  synNs: sessionRecord?.synNs,
504
518
  synackNs: sessionRecord?.synackNs,
@@ -147,6 +147,7 @@ var PuzzleCaptchaManager = class extends require_captchaManager.CaptchaManager {
147
147
  mouseEventsCount: decryptedData.collector1?.length || 0,
148
148
  touchEventsCount: decryptedData.collector2?.length || 0,
149
149
  clickEventsCount: decryptedData.collector3?.length || 0,
150
+ scrollEventsCount: decryptedData.collector4?.length || 0,
150
151
  deviceCapability: decryptedData.deviceCapability,
151
152
  captchaResult: correct ? "passed" : "failed"
152
153
  }
@@ -155,6 +156,7 @@ var PuzzleCaptchaManager = class extends require_captchaManager.CaptchaManager {
155
156
  c1: decryptedData.collector1 || [],
156
157
  c2: decryptedData.collector2 || [],
157
158
  c3: decryptedData.collector3 || [],
159
+ c4: decryptedData.collector4 || [],
158
160
  d: decryptedData.deviceCapability
159
161
  };
160
162
  await this.db.updatePuzzleCaptchaRecord(challenge, {
@@ -467,6 +469,8 @@ var PuzzleCaptchaManager = class extends require_captchaManager.CaptchaManager {
467
469
  ruleType: sessionRecord?.ruleType,
468
470
  webView: sessionRecord?.webView,
469
471
  iFrame: sessionRecord?.iFrame,
472
+ currentUrl: sessionRecord?.currentUrl,
473
+ iframeUrl: sessionRecord?.iframeUrl,
470
474
  coords: challengeRecord.coords,
471
475
  puzzleEvents: challengeRecord.puzzleEvents,
472
476
  synNs: sessionRecord?.synNs,
@@ -2,4 +2,5 @@ import type { IPAddress } from "@prosopo/types";
2
2
  import { type CompositeIpAddress } from "@prosopo/types";
3
3
  export declare const getCompositeIpAddress: (ip: string | IPAddress) => CompositeIpAddress;
4
4
  export declare const getIpAddressFromComposite: (compositeIpAddress: CompositeIpAddress) => IPAddress;
5
+ export declare const isSameIpOrigin: (a: CompositeIpAddress, b: CompositeIpAddress) => boolean;
5
6
  //# sourceMappingURL=compositeIpAddress.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"compositeIpAddress.d.ts","sourceRoot":"","sources":["../src/compositeIpAddress.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,KAAK,kBAAkB,EAAiB,MAAM,gBAAgB,CAAC;AAOxE,eAAO,MAAM,qBAAqB,OAC7B,MAAM,GAAG,SAAS,KACpB,kBAaF,CAAC;AAuBF,eAAO,MAAM,yBAAyB,uBACjB,kBAAkB,KACpC,SAaF,CAAC"}
1
+ {"version":3,"file":"compositeIpAddress.d.ts","sourceRoot":"","sources":["../src/compositeIpAddress.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,KAAK,kBAAkB,EAAiB,MAAM,gBAAgB,CAAC;AAOxE,eAAO,MAAM,qBAAqB,OAC7B,MAAM,GAAG,SAAS,KACpB,kBAaF,CAAC;AAuBF,eAAO,MAAM,yBAAyB,uBACjB,kBAAkB,KACpC,SAaF,CAAC;AAuCF,eAAO,MAAM,cAAc,MACvB,kBAAkB,KAClB,kBAAkB,KACnB,OAiBF,CAAC"}
@@ -38,8 +38,51 @@ var getIpAddressFromComposite = (compositeIpAddress) => {
38
38
  }
39
39
  };
40
40
  var getBigInt = (number) => BigInt(number || 0n);
41
+ /**
42
+ * Mongo hands these back as Decimal128 rather than the `number | bigint` the
43
+ * type promises, and `BigInt(Decimal128)` throws. Route through the string
44
+ * form, and report a value we cannot read as absent rather than throwing.
45
+ */
46
+ var readPart = (value) => {
47
+ if (value === void 0 || value === null) return void 0;
48
+ try {
49
+ return BigInt(value.toString());
50
+ } catch {
51
+ return;
52
+ }
53
+ };
54
+ /**
55
+ * The value `getCompositeIpAddress` returns when it could not read an
56
+ * address at all. 0.0.0.0 is not a client address, so nothing legitimate is
57
+ * being swallowed by treating it as "we don't know".
58
+ */
59
+ var isUnknownIp = (ip) => ip.type === IpAddressType.v4 && readPart(ip.lower) === 0n;
60
+ /**
61
+ * Do two observations of a client come from the same place on the network?
62
+ *
63
+ * v4 is compared exactly. v6 is compared on the /64 prefix alone — the
64
+ * interface identifier in the low 64 bits is designed to rotate (RFC 8981
65
+ * privacy extensions), often several times a day on one unchanged connection,
66
+ * so comparing it would call an ordinary phone a different host.
67
+ *
68
+ * A value we do not actually have counts as the same — one that will not
69
+ * parse, or the all-zero v4 `getCompositeIpAddress` returns when handed an
70
+ * address it could not read. This answer gates extra friction for a real
71
+ * user, and a gap in what we recorded is not evidence against them.
72
+ */
73
+ var isSameIpOrigin = (a, b) => {
74
+ if (isUnknownIp(a) || isUnknownIp(b)) return true;
75
+ if (a.type !== b.type) return false;
76
+ const compare = (left, right) => {
77
+ const leftPart = readPart(left);
78
+ const rightPart = readPart(right);
79
+ if (leftPart === void 0 || rightPart === void 0) return true;
80
+ return leftPart === rightPart;
81
+ };
82
+ return a.type === IpAddressType.v6 ? compare(a.upper, b.upper) : compare(a.lower, b.lower);
83
+ };
41
84
  var never = () => {
42
85
  throw new Error("Unhandled type");
43
86
  };
44
87
  //#endregion
45
- export { getCompositeIpAddress, getIpAddressFromComposite };
88
+ export { getCompositeIpAddress, getIpAddressFromComposite, isSameIpOrigin };