@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.
- package/.turbo/turbo-build$colon$cjs.log +9 -9
- package/.turbo/turbo-build$colon$tsc.log +27 -27
- package/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +102 -0
- package/dist/cjs/compositeIpAddress.cjs +44 -0
- package/dist/cjs/index.cjs +1 -0
- package/dist/cjs/tasks/captchaManager.cjs +16 -1
- package/dist/cjs/tasks/detection/bundlePool.cjs +4 -3
- package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasks.cjs +4 -0
- package/dist/cjs/tasks/powCaptcha/powTasks.cjs +16 -2
- package/dist/cjs/tasks/puzzleCaptcha/puzzleTasks.cjs +4 -0
- package/dist/compositeIpAddress.d.ts +1 -0
- package/dist/compositeIpAddress.d.ts.map +1 -1
- package/dist/compositeIpAddress.js +44 -1
- package/dist/compositeIpAddress.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/tasks/captchaManager.d.ts +1 -1
- package/dist/tasks/captchaManager.d.ts.map +1 -1
- package/dist/tasks/captchaManager.js +16 -1
- package/dist/tasks/captchaManager.js.map +1 -1
- package/dist/tasks/detection/bundlePool.d.ts.map +1 -1
- package/dist/tasks/detection/bundlePool.js +4 -3
- package/dist/tasks/detection/bundlePool.js.map +1 -1
- package/dist/tasks/detection/bundlePool.test.js +30 -0
- package/dist/tasks/detection/bundlePool.test.js.map +1 -1
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.d.ts.map +1 -1
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.js +4 -0
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.js.map +1 -1
- package/dist/tasks/powCaptcha/powTasks.d.ts.map +1 -1
- package/dist/tasks/powCaptcha/powTasks.js +17 -3
- package/dist/tasks/powCaptcha/powTasks.js.map +1 -1
- package/dist/tasks/puzzleCaptcha/puzzleTasks.d.ts.map +1 -1
- package/dist/tasks/puzzleCaptcha/puzzleTasks.js +4 -0
- package/dist/tasks/puzzleCaptcha/puzzleTasks.js.map +1 -1
- package/dist/tests/unit/compositeIpAddress.unit.test.js +37 -1
- package/dist/tests/unit/compositeIpAddress.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/captchaManager.unit.test.js +41 -0
- package/dist/tests/unit/tasks/captchaManager.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.unit.test.js +4 -0
- package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.js +104 -1
- package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/puzzleCaptcha/puzzleTasks.unit.test.js +6 -0
- package/dist/tests/unit/tasks/puzzleCaptcha/puzzleTasks.unit.test.js.map +1 -1
- package/package.json +14 -14
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @prosopo/provider@5.
|
|
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
|
|
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.
|
|
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/
|
|
178
|
-
dist/cjs/tasks/
|
|
179
|
-
dist/cjs/tasks/powCaptcha/powTasks.cjs
|
|
180
|
-
dist/cjs/tasks/imgCaptcha/imgCaptchaTasks.cjs
|
|
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
|
-
[32m✓ built in
|
|
182
|
+
[32m✓ built in 195ms[39m
|
|
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.
|
|
2
|
+
> @prosopo/provider@5.13.1 build:tsc
|
|
3
3
|
> tsc --build --verbose
|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
77
|
+
3:37:47 PM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
3:37:47 PM - Building project '/home/runner/work/captcha/captcha/packages/provider/tsconfig.json'...
|
|
80
80
|
|
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
|
|
2
|
-
> @prosopo/provider@5.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
182
|
-
dist/tasks/captchaManager.js
|
|
183
|
-
dist/tasks/powCaptcha/powTasks.js
|
|
184
|
-
dist/tasks/imgCaptcha/imgCaptchaTasks.js 26.
|
|
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
|
-
[32m✓ built in
|
|
186
|
+
[32m✓ built in 196ms[39m
|
|
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;
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -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
|
|
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)
|
|
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 };
|