@prosopo/provider 5.10.0 → 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.
- package/.turbo/turbo-build$colon$cjs.log +14 -13
- package/.turbo/turbo-build$colon$tsc.log +27 -27
- package/.turbo/turbo-build.log +15 -14
- package/CHANGELOG.md +126 -0
- package/dist/api/captcha/assignDetectorBundle.d.ts.map +1 -1
- package/dist/api/captcha/assignDetectorBundle.js +27 -9
- package/dist/api/captcha/assignDetectorBundle.js.map +1 -1
- package/dist/api/captcha/assignDetectorBundle.unit.test.d.ts +2 -0
- package/dist/api/captcha/assignDetectorBundle.unit.test.d.ts.map +1 -0
- package/dist/api/captcha/assignDetectorBundle.unit.test.js +153 -0
- package/dist/api/captcha/assignDetectorBundle.unit.test.js.map +1 -0
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.d.ts.map +1 -1
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js +6 -2
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js.map +1 -1
- package/dist/api/captcha/submitPoWCaptchaSolution.js +51 -11
- package/dist/api/captcha/submitPoWCaptchaSolution.js.map +1 -1
- package/dist/cjs/api/captcha/assignDetectorBundle.cjs +26 -8
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/handler.cjs +6 -2
- package/dist/cjs/api/captcha/submitPoWCaptchaSolution.cjs +51 -11
- package/dist/cjs/services/ipComparison.cjs +4 -2
- package/dist/cjs/tasks/captchaManager.cjs +34 -3
- package/dist/cjs/tasks/detection/assignSecret.cjs +45 -0
- package/dist/cjs/tasks/detection/bundlePool.cjs +11 -7
- package/dist/cjs/tasks/detection/decodePayload.cjs +1147 -988
- package/dist/cjs/tasks/detection/decodePayload.js +1 -2
- package/dist/cjs/tasks/detection/getBotScore.cjs +30 -1
- package/dist/cjs/tasks/frictionless/frictionlessTasks.cjs +95 -82
- package/dist/cjs/util.cjs +4 -9
- package/dist/services/ipComparison.d.ts.map +1 -1
- package/dist/services/ipComparison.js +4 -2
- package/dist/services/ipComparison.js.map +1 -1
- package/dist/tasks/captchaManager.d.ts +1 -0
- package/dist/tasks/captchaManager.d.ts.map +1 -1
- package/dist/tasks/captchaManager.js +34 -3
- package/dist/tasks/captchaManager.js.map +1 -1
- package/dist/tasks/detection/assignSecret.d.ts +8 -0
- package/dist/tasks/detection/assignSecret.d.ts.map +1 -0
- package/dist/tasks/detection/assignSecret.js +45 -0
- package/dist/tasks/detection/assignSecret.js.map +1 -0
- package/dist/tasks/detection/bundlePool.d.ts +2 -1
- package/dist/tasks/detection/bundlePool.d.ts.map +1 -1
- package/dist/tasks/detection/bundlePool.js +11 -8
- package/dist/tasks/detection/bundlePool.js.map +1 -1
- package/dist/tasks/detection/bundlePool.test.js +4 -3
- package/dist/tasks/detection/bundlePool.test.js.map +1 -1
- package/dist/tasks/detection/decodePayload.d.ts +2 -2
- package/dist/tasks/detection/decodePayload.d.ts.map +1 -1
- package/dist/tasks/detection/decodePayload.js +1 -2
- package/dist/tasks/detection/decodePayload.js.map +1 -1
- package/dist/tasks/detection/getBotScore.d.ts +11 -0
- package/dist/tasks/detection/getBotScore.d.ts.map +1 -1
- package/dist/tasks/detection/getBotScore.js +32 -4
- package/dist/tasks/detection/getBotScore.js.map +1 -1
- package/dist/tasks/frictionless/frictionlessTasks.d.ts +7 -3
- package/dist/tasks/frictionless/frictionlessTasks.d.ts.map +1 -1
- package/dist/tasks/frictionless/frictionlessTasks.js +95 -82
- package/dist/tasks/frictionless/frictionlessTasks.js.map +1 -1
- package/dist/tests/integration/escalationPeekBeforeConsume.integration.test.js +26 -2
- package/dist/tests/integration/escalationPeekBeforeConsume.integration.test.js.map +1 -1
- package/dist/tests/unit/api/captcha/submitPoWCaptchaSolution.escalation.unit.test.js +49 -38
- package/dist/tests/unit/api/captcha/submitPoWCaptchaSolution.escalation.unit.test.js.map +1 -1
- package/dist/tests/unit/services/ipComparison.unit.test.js +27 -0
- package/dist/tests/unit/services/ipComparison.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/captchaManager.unit.test.js +46 -0
- package/dist/tests/unit/tasks/captchaManager.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/detection/getBotScore.unit.test.js +44 -1
- package/dist/tests/unit/tasks/detection/getBotScore.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/frictionless/decryptPayload.unit.test.js +4 -0
- package/dist/tests/unit/tasks/frictionless/decryptPayload.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/frictionless/frictionlessTasks.unit.test.js +78 -2
- package/dist/tests/unit/tasks/frictionless/frictionlessTasks.unit.test.js.map +1 -1
- package/dist/tests/unit/util.deepValidateIpAddress.rules.unit.test.d.ts +2 -0
- package/dist/tests/unit/util.deepValidateIpAddress.rules.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/util.deepValidateIpAddress.rules.unit.test.js +115 -0
- package/dist/tests/unit/util.deepValidateIpAddress.rules.unit.test.js.map +1 -0
- package/dist/tests/unit/util.evaluateIpValidationRules.unit.test.js +70 -0
- package/dist/tests/unit/util.evaluateIpValidationRules.unit.test.js.map +1 -1
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +4 -9
- package/dist/util.js.map +1 -1
- package/package.json +16 -16
- package/dist/api/captcha/assignDetectorBundle.test.d.ts +0 -2
- package/dist/api/captcha/assignDetectorBundle.test.d.ts.map +0 -1
- package/dist/api/captcha/assignDetectorBundle.test.js +0 -71
- package/dist/api/captcha/assignDetectorBundle.test.js.map +0 -1
|
@@ -355,13 +355,44 @@ var CaptchaManager = class CaptchaManager {
|
|
|
355
355
|
* the bundleId too so the caller can promote it onto the session.
|
|
356
356
|
*/
|
|
357
357
|
async resolveBundleByDetectorSession(detectorSessionId) {
|
|
358
|
-
if (!detectorSessionId || !this.writeQueue)
|
|
358
|
+
if (!detectorSessionId || !this.writeQueue) {
|
|
359
|
+
this.logUnresolvedDetectorBundle("noDetectorSession");
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
359
362
|
const bundleId = await this.writeQueue.getDetectorBundle(detectorSessionId);
|
|
363
|
+
if (!bundleId) {
|
|
364
|
+
this.logUnresolvedDetectorBundle("noBinding");
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
360
367
|
const decrypt = this.resolveBundleById(bundleId);
|
|
361
|
-
|
|
368
|
+
if (!decrypt) {
|
|
369
|
+
this.logUnresolvedDetectorBundle("bundleNotInPool", bundleId);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
return {
|
|
362
373
|
...decrypt,
|
|
363
374
|
bundleId
|
|
364
|
-
}
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Returning undefined here leaves the frictionless decrypt with no keys to
|
|
379
|
+
* try, which fails closed: the score is forced to 1 and the caller is
|
|
380
|
+
* challenged despite nothing having been measured about it. The three causes
|
|
381
|
+
* need different fixes — a caller that sent no detector session, a binding
|
|
382
|
+
* that was absent or had expired (see `DETECTOR_BUNDLE_TTL_SECONDS`), and a
|
|
383
|
+
* bundle this provider no longer holds — but they are indistinguishable
|
|
384
|
+
* downstream, where all three surface as the same decrypt failure. Logged at
|
|
385
|
+
* info because that is the level aggregated log search runs at, and only on
|
|
386
|
+
* the failure path, so this costs nothing on the hot path.
|
|
387
|
+
*/
|
|
388
|
+
logUnresolvedDetectorBundle(cause, bundleId) {
|
|
389
|
+
this.logger?.info(() => ({
|
|
390
|
+
msg: "Detector bundle not resolved",
|
|
391
|
+
data: {
|
|
392
|
+
cause,
|
|
393
|
+
...bundleId !== void 0 && { bundleId }
|
|
394
|
+
}
|
|
395
|
+
}));
|
|
365
396
|
}
|
|
366
397
|
/**
|
|
367
398
|
* Resolve the detector pool bundle for a session via the `bundleId` promoted
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
let node_crypto = require("node:crypto");
|
|
2
|
+
let node_fs = require("node:fs");
|
|
3
|
+
let node_path = require("node:path");
|
|
4
|
+
//#region src/tasks/detection/assignSecret.ts
|
|
5
|
+
/**
|
|
6
|
+
* Per-provider key for the client → bundle mapping in `assignDetectorBundle`.
|
|
7
|
+
* Generated once and kept on the pool volume so a restart does not change the
|
|
8
|
+
* mapping. Treat it as a credential.
|
|
9
|
+
*/
|
|
10
|
+
var SECRET_BYTES = 32;
|
|
11
|
+
/**
|
|
12
|
+
* Dot-prefixed so a pool replace leaves it alone: `loadFromDir` and
|
|
13
|
+
* `persistDetectorBundlePool` only touch `.js`/`.json`.
|
|
14
|
+
*/
|
|
15
|
+
var SECRET_FILENAME = ".assign-secret";
|
|
16
|
+
var cached = null;
|
|
17
|
+
function getAssignSecret(poolDir, logger = {}) {
|
|
18
|
+
const path = (0, node_path.join)(poolDir, SECRET_FILENAME);
|
|
19
|
+
if (cached && cached.path === path) return cached.secret;
|
|
20
|
+
try {
|
|
21
|
+
const existing = (0, node_fs.readFileSync)(path);
|
|
22
|
+
if (existing.length === SECRET_BYTES) {
|
|
23
|
+
cached = {
|
|
24
|
+
path,
|
|
25
|
+
secret: existing
|
|
26
|
+
};
|
|
27
|
+
return existing;
|
|
28
|
+
}
|
|
29
|
+
logger.warn?.("assign secret has unexpected length, regenerating", { bytes: existing.length });
|
|
30
|
+
} catch {}
|
|
31
|
+
const secret = (0, node_crypto.randomBytes)(SECRET_BYTES);
|
|
32
|
+
(0, node_fs.mkdirSync)((0, node_path.dirname)(path), { recursive: true });
|
|
33
|
+
const tmp = `${path}.tmp`;
|
|
34
|
+
(0, node_fs.writeFileSync)(tmp, secret, { mode: 384 });
|
|
35
|
+
(0, node_fs.chmodSync)(tmp, 384);
|
|
36
|
+
(0, node_fs.renameSync)(tmp, path);
|
|
37
|
+
logger.info?.("generated a new assign secret", { path });
|
|
38
|
+
cached = {
|
|
39
|
+
path,
|
|
40
|
+
secret
|
|
41
|
+
};
|
|
42
|
+
return secret;
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
exports.getAssignSecret = getAssignSecret;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
let node_crypto = require("node:crypto");
|
|
2
1
|
let node_fs = require("node:fs");
|
|
3
2
|
let node_path = require("node:path");
|
|
4
3
|
//#region src/tasks/detection/bundlePool.ts
|
|
@@ -11,7 +10,7 @@ let node_path = require("node:path");
|
|
|
11
10
|
* (`{ privateKey, innerConfig }`, kept server-side) produced by the catcher
|
|
12
11
|
* `bundle:pool` build script.
|
|
13
12
|
*
|
|
14
|
-
* A bundle is assigned per detector session by {@link DetectorBundlePool.
|
|
13
|
+
* A bundle is assigned per detector session by {@link DetectorBundlePool.at},
|
|
15
14
|
* served by id, and resolved again at decryption time so the provider uses the
|
|
16
15
|
* single correct keypair + inner config rather than brute-forcing a key pool.
|
|
17
16
|
* The session→bundle binding lives in Redis (short TTL); this module only holds
|
|
@@ -105,13 +104,13 @@ var DetectorBundlePool = class {
|
|
|
105
104
|
return this.ids.length;
|
|
106
105
|
}
|
|
107
106
|
/**
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
107
|
+
* Select by position. `ids` is sorted by {@link replace}, so an index means
|
|
108
|
+
* the same bundle until the pool contents change. Reduced modulo the size, so
|
|
109
|
+
* callers need not know how many bundles are loaded.
|
|
111
110
|
*/
|
|
112
|
-
|
|
111
|
+
at(index) {
|
|
113
112
|
if (this.ids.length === 0) throw new Error("detector bundle pool is empty");
|
|
114
|
-
const bundleId = this.ids[
|
|
113
|
+
const bundleId = this.ids[index % this.ids.length];
|
|
115
114
|
const bundle = bundleId !== void 0 ? this.bundles.get(bundleId) : void 0;
|
|
116
115
|
if (bundleId === void 0 || !bundle) throw new Error("detector bundle pool selection failed");
|
|
117
116
|
return {
|
|
@@ -183,6 +182,10 @@ function persistDetectorBundlePool(bundles, dir = globalPoolDir) {
|
|
|
183
182
|
function getDetectorBundlePool() {
|
|
184
183
|
return globalPool;
|
|
185
184
|
}
|
|
185
|
+
/** Where the global pool was last loaded from — the host-mounted volume. */
|
|
186
|
+
function getDetectorBundlePoolDir() {
|
|
187
|
+
return globalPoolDir;
|
|
188
|
+
}
|
|
186
189
|
/**
|
|
187
190
|
* Hot-swap the global pool's contents (admin "emergency push" channel) and
|
|
188
191
|
* write it to the pool directory so it survives a restart. Creates the global
|
|
@@ -215,6 +218,7 @@ function replaceDetectorBundlePool(bundles, logger = {}) {
|
|
|
215
218
|
exports.DEFAULT_DETECTOR_POOL_DIR = DEFAULT_DETECTOR_POOL_DIR;
|
|
216
219
|
exports.DetectorBundlePool = DetectorBundlePool;
|
|
217
220
|
exports.getDetectorBundlePool = getDetectorBundlePool;
|
|
221
|
+
exports.getDetectorBundlePoolDir = getDetectorBundlePoolDir;
|
|
218
222
|
exports.initDetectorBundlePool = initDetectorBundlePool;
|
|
219
223
|
exports.persistDetectorBundlePool = persistDetectorBundlePool;
|
|
220
224
|
exports.replaceDetectorBundlePool = replaceDetectorBundlePool;
|