@prosopo/provider 5.8.0 → 5.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/.turbo/turbo-build$colon$cjs.log +15 -11
  2. package/.turbo/turbo-build$colon$tsc.log +29 -26
  3. package/.turbo/turbo-build.log +18 -14
  4. package/CHANGELOG.md +162 -0
  5. package/dist/api/blacklistRequestInspector.d.ts +1 -1
  6. package/dist/api/blacklistRequestInspector.d.ts.map +1 -1
  7. package/dist/api/blacklistRequestInspector.js +4 -2
  8. package/dist/api/blacklistRequestInspector.js.map +1 -1
  9. package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.d.ts.map +1 -1
  10. package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js +36 -5
  11. package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js.map +1 -1
  12. package/dist/api/captcha/getImageCaptchaChallenge.d.ts.map +1 -1
  13. package/dist/api/captcha/getImageCaptchaChallenge.js +3 -1
  14. package/dist/api/captcha/getImageCaptchaChallenge.js.map +1 -1
  15. package/dist/api/captcha/signedAssetsResolver.d.ts +20 -0
  16. package/dist/api/captcha/signedAssetsResolver.d.ts.map +1 -0
  17. package/dist/api/captcha/signedAssetsResolver.js +53 -0
  18. package/dist/api/captcha/signedAssetsResolver.js.map +1 -0
  19. package/dist/api/verify.d.ts.map +1 -1
  20. package/dist/api/verify.js +83 -0
  21. package/dist/api/verify.js.map +1 -1
  22. package/dist/cjs/api/blacklistRequestInspector.cjs +4 -2
  23. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/handler.cjs +36 -5
  24. package/dist/cjs/api/captcha/getImageCaptchaChallenge.cjs +3 -1
  25. package/dist/cjs/api/captcha/signedAssetsResolver.cjs +54 -0
  26. package/dist/cjs/api/verify.cjs +83 -0
  27. package/dist/cjs/tasks/detection/getBotScore.cjs +3 -1
  28. package/dist/cjs/tasks/frictionless/frictionlessTasks.cjs +90 -2
  29. package/dist/cjs/tasks/frictionless/ipMatch.cjs +45 -0
  30. package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasks.cjs +1 -1
  31. package/dist/cjs/tasks/powCaptcha/powTasks.cjs +1 -1
  32. package/dist/cjs/tasks/puzzleCaptcha/puzzleTasks.cjs +1 -1
  33. package/dist/tasks/detection/getBotScore.d.ts +2 -0
  34. package/dist/tasks/detection/getBotScore.d.ts.map +1 -1
  35. package/dist/tasks/detection/getBotScore.js +3 -1
  36. package/dist/tasks/detection/getBotScore.js.map +1 -1
  37. package/dist/tasks/frictionless/frictionlessTasks.d.ts +5 -0
  38. package/dist/tasks/frictionless/frictionlessTasks.d.ts.map +1 -1
  39. package/dist/tasks/frictionless/frictionlessTasks.js +91 -3
  40. package/dist/tasks/frictionless/frictionlessTasks.js.map +1 -1
  41. package/dist/tasks/frictionless/ipMatch.d.ts +3 -0
  42. package/dist/tasks/frictionless/ipMatch.d.ts.map +1 -0
  43. package/dist/tasks/frictionless/ipMatch.js +45 -0
  44. package/dist/tasks/frictionless/ipMatch.js.map +1 -0
  45. package/dist/tasks/frictionless/routingMachine.d.ts +1 -0
  46. package/dist/tasks/frictionless/routingMachine.d.ts.map +1 -1
  47. package/dist/tasks/frictionless/routingMachine.js.map +1 -1
  48. package/dist/tasks/imgCaptcha/imgCaptchaTasks.js +1 -1
  49. package/dist/tasks/powCaptcha/powTasks.js +1 -1
  50. package/dist/tasks/puzzleCaptcha/puzzleTasks.js +1 -1
  51. package/dist/tests/integration/api/admin/apiRegisterSiteKeyEndpoint.integration.test.js +3 -1
  52. package/dist/tests/integration/api/admin/apiRegisterSiteKeyEndpoint.integration.test.js.map +1 -1
  53. package/dist/tests/integration/clientSettingsPersistence.integration.test.js +2 -1
  54. package/dist/tests/integration/clientSettingsPersistence.integration.test.js.map +1 -1
  55. package/dist/tests/integration/webBotAuthAuthenticated.integration.test.d.ts +2 -0
  56. package/dist/tests/integration/webBotAuthAuthenticated.integration.test.d.ts.map +1 -0
  57. package/dist/tests/integration/webBotAuthAuthenticated.integration.test.js +448 -0
  58. package/dist/tests/integration/webBotAuthAuthenticated.integration.test.js.map +1 -0
  59. package/dist/tests/unit/api/admin/apiRegisterSiteKeyEndpoint.unit.test.js +2 -1
  60. package/dist/tests/unit/api/admin/apiRegisterSiteKeyEndpoint.unit.test.js.map +1 -1
  61. package/dist/tests/unit/api/getFrictionlessCaptchaChallenge.unit.test.js +26 -0
  62. package/dist/tests/unit/api/getFrictionlessCaptchaChallenge.unit.test.js.map +1 -1
  63. package/dist/tests/unit/signedAssetsResolver.unit.test.d.ts +2 -0
  64. package/dist/tests/unit/signedAssetsResolver.unit.test.d.ts.map +1 -0
  65. package/dist/tests/unit/signedAssetsResolver.unit.test.js +77 -0
  66. package/dist/tests/unit/signedAssetsResolver.unit.test.js.map +1 -0
  67. package/dist/tests/unit/tasks/captchaManager.unit.test.js +2 -1
  68. package/dist/tests/unit/tasks/captchaManager.unit.test.js.map +1 -1
  69. package/dist/tests/unit/tasks/frictionless/ipMatch.unit.test.d.ts +2 -0
  70. package/dist/tests/unit/tasks/frictionless/ipMatch.unit.test.d.ts.map +1 -0
  71. package/dist/tests/unit/tasks/frictionless/ipMatch.unit.test.js +107 -0
  72. package/dist/tests/unit/tasks/frictionless/ipMatch.unit.test.js.map +1 -0
  73. package/dist/tests/unit/tasks/frictionless/routerPuzzleOverrides.unit.test.js +88 -1
  74. package/dist/tests/unit/tasks/frictionless/routerPuzzleOverrides.unit.test.js.map +1 -1
  75. package/dist/tests/unit/tasks/puzzleMaxDifficultyMatchesLadder.unit.test.d.ts +2 -0
  76. package/dist/tests/unit/tasks/puzzleMaxDifficultyMatchesLadder.unit.test.d.ts.map +1 -0
  77. package/dist/tests/unit/tasks/puzzleMaxDifficultyMatchesLadder.unit.test.js +16 -0
  78. package/dist/tests/unit/tasks/puzzleMaxDifficultyMatchesLadder.unit.test.js.map +1 -0
  79. package/dist/util.d.ts.map +1 -1
  80. package/dist/util.js.map +1 -1
  81. package/package.json +16 -14
@@ -0,0 +1,54 @@
1
+ let node_crypto = require("node:crypto");
2
+ var SignedAssetsResolver = class {
3
+ constructor(options) {
4
+ this.securityKey = options.securityKey;
5
+ this.ttlSeconds = options.ttlSeconds ?? 300;
6
+ this.clientIp = options.clientIp;
7
+ this.now = options.now ?? (() => Date.now());
8
+ }
9
+ sign(path, expires) {
10
+ const base = `${this.securityKey}${path}${expires}${this.clientIp ?? ""}`;
11
+ return (0, node_crypto.createHash)("sha256").update(base).digest("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
12
+ }
13
+ resolveAsset(assetURI) {
14
+ const url = this.toUrl(assetURI);
15
+ if (!url) return {
16
+ URI: assetURI,
17
+ getURL: () => assetURI
18
+ };
19
+ if (url.search !== "") throw new Error(`Cannot sign an asset URL that already has a query string: ${assetURI}`);
20
+ const expires = Math.floor(this.now() / 1e3) + this.ttlSeconds;
21
+ const token = this.sign(url.pathname, expires);
22
+ const signed = `${url.origin}${url.pathname}?token=${token}&expires=${expires}`;
23
+ return {
24
+ URI: assetURI,
25
+ getURL: () => signed
26
+ };
27
+ }
28
+ toUrl(assetURI) {
29
+ try {
30
+ const url = new URL(assetURI);
31
+ return url.protocol === "http:" || url.protocol === "https:" ? url : void 0;
32
+ } catch {
33
+ return;
34
+ }
35
+ }
36
+ };
37
+ /**
38
+ * Build the resolver from the environment, or `undefined` when it is not
39
+ * configured. Undefined means `parseCaptchaAssets` passes `item.data`
40
+ * through unchanged — the behaviour before this existed — so the feature is
41
+ * off until a key is deliberately supplied.
42
+ */
43
+ function getSignedAssetsResolver(clientIp) {
44
+ const securityKey = process.env.PROSOPO_ASSET_TOKEN_KEY;
45
+ if (!securityKey) return;
46
+ return new SignedAssetsResolver({
47
+ securityKey,
48
+ ttlSeconds: Number.parseInt(process.env.PROSOPO_ASSET_TOKEN_TTL_SECONDS ?? "", 10) || 300,
49
+ ...process.env.PROSOPO_ASSET_TOKEN_BIND_IP === "true" && clientIp ? { clientIp } : {}
50
+ });
51
+ }
52
+ //#endregion
53
+ exports.SignedAssetsResolver = SignedAssetsResolver;
54
+ exports.getSignedAssetsResolver = getSignedAssetsResolver;
@@ -331,6 +331,89 @@ function prosopoVerifyRouter(env) {
331
331
  }));
332
332
  }
333
333
  });
334
+ /**
335
+ * Verify a Web Bot Auth authenticated session token. Enforces IP binding:
336
+ * the operator MUST forward `ip` in the request body, and it must equal
337
+ * the client IP captured on the session at frictionless issuance. Only
338
+ * tokens minted with captchaType=authenticated are accepted here; a
339
+ * regular image/pow/puzzle token routed to this endpoint fails with
340
+ * INCORRECT_CAPTCHA_TYPE.
341
+ */
342
+ router.post(_prosopo_types.ClientApiPaths.VerifyAuthenticatedSession, async (req, res, next) => {
343
+ if (require_apiToggleMaintenanceModeEndpoint.getMaintenanceMode()) {
344
+ const verificationResponse = require_maintenanceModeResponses.buildMaintenanceVerificationResponse(req.i18n.t);
345
+ return res.json(verificationResponse);
346
+ }
347
+ let parsed;
348
+ try {
349
+ parsed = _prosopo_types.VerifySolutionBody.parse(req.body);
350
+ } catch (err) {
351
+ return next(new _prosopo_common.ProsopoApiError("CAPTCHA.PARSE_ERROR", {
352
+ context: {
353
+ code: 400,
354
+ error: err,
355
+ body: req.body
356
+ },
357
+ i18n: req.i18n,
358
+ logger: req.logger
359
+ }));
360
+ }
361
+ const { dappSignature, token, ip, clientSessionId } = parsed;
362
+ try {
363
+ const { user, dapp, timestamp, commitmentId: sessionId, providerUrl } = (0, _prosopo_types.decodeProcaptchaOutput)(token);
364
+ const testVerdict = require_testSiteKey.resolveTestSiteKeyVerdict(dapp, req.logger);
365
+ if (testVerdict !== null) return res.json({
366
+ status: "ok",
367
+ verified: testVerdict
368
+ });
369
+ const forwarded = await require_forwardVerify.forwardVerifyIfNotIssuer({
370
+ env,
371
+ logger: req.logger,
372
+ path: _prosopo_types.ClientApiPaths.VerifyAuthenticatedSession,
373
+ providerUrl,
374
+ dapp,
375
+ user,
376
+ body: parsed,
377
+ alreadyForwarded: req.headers[_prosopo_api.VERIFY_FORWARDED_HEADER] !== void 0
378
+ });
379
+ if (forwarded) return res.json(forwarded);
380
+ const tasks = new require_tasks.Tasks(env, req.logger);
381
+ (0, _prosopo_util_crypto.validateAddress)(dapp, false, 42);
382
+ (0, _prosopo_util_crypto.validateAddress)(user, false, 42);
383
+ if (!await tasks.db.getClientRecord(dapp)) return next(new _prosopo_common.ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
384
+ context: {
385
+ code: 400,
386
+ siteKey: dapp,
387
+ user
388
+ },
389
+ i18n: req.i18n,
390
+ logger: req.logger
391
+ }));
392
+ const keyPair = env.keyring.addFromAddress(dapp);
393
+ (0, _prosopo_api_express_router.verifySignature)(dappSignature, timestamp.toString(), keyPair);
394
+ if (!sessionId) return res.json({
395
+ status: "API.USER_NOT_VERIFIED_NO_SOLUTION",
396
+ verified: false
397
+ });
398
+ const outcome = await tasks.frictionlessManager.verifyAuthenticatedSession(sessionId, ip, clientSessionId);
399
+ res.json(outcome);
400
+ } catch (err) {
401
+ req.logger.error(() => ({
402
+ err,
403
+ msg: "Error in verifyAuthenticatedSession",
404
+ data: { body: req.body }
405
+ }));
406
+ return next(new _prosopo_common.ProsopoApiError("API.BAD_REQUEST", {
407
+ context: {
408
+ code: 500,
409
+ siteKey: req.body.dapp,
410
+ user: req.body.user
411
+ },
412
+ i18n: req.i18n,
413
+ logger: req.logger
414
+ }));
415
+ }
416
+ });
334
417
  router.use(_prosopo_api_express_router.handleErrors);
335
418
  return router;
336
419
  }
@@ -21,6 +21,7 @@ var getBotScore = async (payload, headHash, privateKeyString, innerConfigEncoded
21
21
  const md = result.md;
22
22
  const bn = result.bn;
23
23
  const fs = result.fs;
24
+ const b = result.b;
24
25
  if (baseBotScore === void 0 || Number.isNaN(baseBotScore)) return {
25
26
  baseBotScore: 1,
26
27
  timestamp: 0
@@ -44,7 +45,8 @@ var getBotScore = async (payload, headHash, privateKeyString, innerConfigEncoded
44
45
  sw,
45
46
  md,
46
47
  bn,
47
- fs
48
+ fs,
49
+ b
48
50
  };
49
51
  };
50
52
  //#endregion
@@ -2,10 +2,12 @@ const require_usageCounters = require("../../util/usageCounters.cjs");
2
2
  const require_decisionMachineRunner = require("../decisionMachine/decisionMachineRunner.cjs");
3
3
  const require_dnsEventUrl = require("../../api/dnsEventUrl.cjs");
4
4
  const require_lang = require("../../rules/lang.cjs");
5
+ const require_clientMetaData = require("../../utils/clientMetaData.cjs");
5
6
  const require_captchaManager = require("../captchaManager.cjs");
6
7
  const require_captchaTypeSelection = require("../captchaTypeSelection.cjs");
7
8
  const require_getBotScore = require("../detection/getBotScore.cjs");
8
9
  const require_puzzleDifficulty = require("../puzzle/puzzleDifficulty.cjs");
10
+ const require_ipMatch = require("./ipMatch.cjs");
9
11
  const require_routingMachine = require("./routingMachine.cjs");
10
12
  let _prosopo_types = require("@prosopo/types");
11
13
  let _prosopo_captcha_severity = require("@prosopo/captcha-severity");
@@ -172,6 +174,91 @@ var FrictionlessManager = class extends require_captchaManager.CaptchaManager {
172
174
  }
173
175
  return sessionRecord;
174
176
  }
177
+ /**
178
+ * Dedicated issuance path for Web Bot Auth verified requests. The signature
179
+ * verification already carried the trust decision; there is no captcha to
180
+ * solve, no bot score to compute, no routing to run. The session is minted
181
+ * with `captchaType: authenticated`, `agent: true`, `webBotAuthAgent` set to
182
+ * the verified Signature-Agent URL, and `ipAddress` frozen for the verify-
183
+ * time IP-binding check. Consumed only by `/client/authenticated/verify`.
184
+ */
185
+ async createAuthenticatedSession(token, ipAddress, webBotAuthAgent, siteKey, userSitekeyIpHash, headers, ipInfo, clientSessionId) {
186
+ const sessionRecord = {
187
+ sessionId: `${getSessionIDPrefix(this.config.host)}-${(0, uuid.v4)()}`,
188
+ createdAt: /* @__PURE__ */ new Date(),
189
+ token,
190
+ score: 0,
191
+ threshold: 0,
192
+ scoreComponents: { baseScore: 0 },
193
+ ipAddress,
194
+ captchaType: _prosopo_types.CaptchaType.authenticated,
195
+ userSitekeyIpHash,
196
+ webView: false,
197
+ iFrame: false,
198
+ decryptedHeadHash: "",
199
+ siteKey,
200
+ serverChecked: false,
201
+ agent: true,
202
+ webBotAuthAgent,
203
+ ...ipInfo && { ipInfo },
204
+ ...headers && { headers },
205
+ ...clientSessionId && { clientMetaData: { clientSessionId } }
206
+ };
207
+ await this.db.storeSessionRecord(sessionRecord);
208
+ if (this.writeQueue) {
209
+ const cacheData = sessionRecord;
210
+ const cachePromises = [this.writeQueue.cacheSession(sessionRecord.sessionId, cacheData)];
211
+ if (userSitekeyIpHash) cachePromises.push(this.writeQueue.cacheSessionByHash(userSitekeyIpHash, sessionRecord.sessionId));
212
+ await Promise.all(cachePromises).catch(() => {});
213
+ }
214
+ return sessionRecord;
215
+ }
216
+ /**
217
+ * Verify an authenticated (Web Bot Auth) session. Called from
218
+ * `/client/authenticated/verify` after the operator forwards their
219
+ * dApp-signed token. Enforces the four properties that make replay
220
+ * infeasible:
221
+ * 1. session exists and was minted with captchaType=authenticated
222
+ * (so ordinary captcha tokens can't be redeemed here)
223
+ * 2. session hasn't been consumed (serverChecked === false)
224
+ * 3. operator forwarded the client IP (`ip` is required — silently
225
+ * dropping the check would nullify the whole binding)
226
+ * 4. the forwarded IP matches the IP the session was issued to
227
+ *
228
+ * Marks serverChecked=true on success so subsequent verifies fail loudly.
229
+ */
230
+ async verifyAuthenticatedSession(sessionId, ip, clientSessionId) {
231
+ if (!ip) return {
232
+ verified: false,
233
+ status: "API.AUTHENTICATED_IP_REQUIRED"
234
+ };
235
+ const session = await this.db.getSessionRecordBySessionId(sessionId);
236
+ if (!session) return {
237
+ verified: false,
238
+ status: "API.USER_NOT_VERIFIED_NO_SOLUTION"
239
+ };
240
+ if (session.captchaType !== _prosopo_types.CaptchaType.authenticated) return {
241
+ verified: false,
242
+ status: "API.INCORRECT_CAPTCHA_TYPE"
243
+ };
244
+ if (session.serverChecked) return {
245
+ verified: false,
246
+ status: "API.USER_ALREADY_VERIFIED"
247
+ };
248
+ if (!require_ipMatch.ipMatchesSession(ip, session.ipAddress)) return {
249
+ verified: false,
250
+ status: "API.AUTHENTICATED_IP_MISMATCH"
251
+ };
252
+ if (require_clientMetaData.isClientSessionMismatch(clientSessionId, session.clientMetaData?.clientSessionId)) return {
253
+ verified: false,
254
+ status: "API.CLIENT_SESSION_MISMATCH"
255
+ };
256
+ await this.db.updateSessionRecord(sessionId, { serverChecked: true });
257
+ return {
258
+ verified: true,
259
+ status: "API.USER_VERIFIED"
260
+ };
261
+ }
175
262
  async sendImageCaptcha(params) {
176
263
  return this.sendCaptcha(_prosopo_types.CaptchaType.image, params);
177
264
  }
@@ -198,8 +285,10 @@ var FrictionlessManager = class extends require_captchaManager.CaptchaManager {
198
285
  const imageRoundsBounds = this.routingContext ?? {};
199
286
  const finalSolvedImagesCount = finalCaptchaType === _prosopo_types.CaptchaType.image ? requestedSolvedImagesCount !== void 0 ? (0, _prosopo_types.clampImageRounds)(requestedSolvedImagesCount, imageRoundsBounds) : requestedSolvedImagesCount : void 0;
200
287
  const finalPowDifficulty = finalCaptchaType === _prosopo_types.CaptchaType.pow ? routed.powDifficulty ?? effectiveParams.powDifficulty : void 0;
288
+ const finalReason = routed.reason ?? effectiveParams.reason;
201
289
  const finalPuzzleOverrides = finalCaptchaType === _prosopo_types.CaptchaType.puzzle ? (() => {
202
- const level = (0, _prosopo_captcha_severity.severityToPuzzleDifficulty)(requestedSolvedImagesCount, this.routingContext?.baseImageRounds ?? this.config.captchas.solved.count);
290
+ const maxLevel = this.routingContext?.puzzleMaxDifficulty ?? _prosopo_types.puzzleMaxDifficultyDefault;
291
+ const level = finalReason !== void 0 && _prosopo_types.NO_MEASUREMENT_REASONS.has(finalReason) ? 0 : (0, _prosopo_captcha_severity.severityToPuzzleDifficulty)(requestedSolvedImagesCount, this.routingContext?.baseImageRounds ?? this.config.captchas.solved.count, maxLevel);
203
292
  const difficulty = level > 0 ? require_puzzleDifficulty.samplePuzzleDifficulty(level, _prosopo_puzzle_assets.DEFAULT_RENDER_SETTINGS.holeDarken) : void 0;
204
293
  const tolerance = routed.puzzleTolerance ?? difficulty?.tolerance;
205
294
  const puzzle = routed.puzzle || difficulty ? {
@@ -211,7 +300,6 @@ var FrictionlessManager = class extends require_captchaManager.CaptchaManager {
211
300
  ...puzzle !== void 0 && { puzzle }
212
301
  };
213
302
  })() : {};
214
- const finalReason = routed.reason ?? effectiveParams.reason;
215
303
  const blocked = finalCaptchaType === _prosopo_types.CaptchaType.image ? effectiveParams.blocked : void 0;
216
304
  const sessionRecord = await this.createSession(effectiveParams.token, effectiveParams.score, effectiveParams.threshold, effectiveParams.scoreComponents, effectiveParams.ipAddress, finalCaptchaType, effectiveParams.siteKey, finalSolvedImagesCount, finalPowDifficulty, effectiveParams.userSitekeyIpHash, effectiveParams.webView ?? false, effectiveParams.iFrame ?? false, effectiveParams.decryptedHeadHash, finalReason, blocked, void 0, effectiveParams.ipInfo, effectiveParams.headers, effectiveParams.mode, effectiveParams.simdReadings, effectiveParams.entropyMathRandomFingerprint, effectiveParams.entropyCryptoFingerprint, effectiveParams.entropyWallClockOffsetMs, effectiveParams.entropyMathRandomFirst, effectiveParams.bundleId, effectiveParams.currentUrl, effectiveParams.tcpToChelloUs, effectiveParams.chelloToHandshakeUs, void 0, effectiveParams.iframeUrl, effectiveParams.isProtect, void 0, effectiveParams.g, effectiveParams.matchedRule, effectiveParams.i, effectiveParams.sw, effectiveParams.md, effectiveParams.bn, effectiveParams.fs, {
217
305
  synNs: effectiveParams.synNs,
@@ -0,0 +1,45 @@
1
+ const require_compositeIpAddress = require("../../compositeIpAddress.cjs");
2
+ //#region src/tasks/frictionless/ipMatch.ts
3
+ /**
4
+ * `CompositeIpAddress` types its halves as `bigint`, but that is only true of
5
+ * a value just built by `getCompositeIpAddress`. Read back off a session the
6
+ * halves are whatever BSON stored — a `Decimal128`, or a `Long` on records
7
+ * written before the Decimal128 migration — so `===` against a real bigint is
8
+ * false for every session that has been through Mongo. Both BSON types
9
+ * stringify to plain digits at IP magnitudes (≤ 2^64, well inside
10
+ * Decimal128's 34 significant digits), so a string round-trip is exact.
11
+ *
12
+ * `null` for anything unparseable rather than a 0n default: two unparseable
13
+ * halves must not compare equal, or garbage would match garbage.
14
+ */
15
+ var asBigInt = (half) => {
16
+ if (typeof half === "bigint") return half;
17
+ if (half === void 0 || half === null) return 0n;
18
+ try {
19
+ return BigInt(String(half));
20
+ } catch {
21
+ return null;
22
+ }
23
+ };
24
+ /**
25
+ * Compare an operator-supplied plain-string IP against the CompositeIpAddress
26
+ * captured on a session. Returns true iff the parsed IP has the same type
27
+ * (v4/v6) and numeric halves.
28
+ *
29
+ * Load-bearing for Web Bot Auth IP binding — a leaked authenticated token
30
+ * replayed from a different IP fails here. Malformed operator IPs degrade
31
+ * to {lower: 0n, type: v4} inside getCompositeIpAddress and can never match
32
+ * a real session (a real v4 session has a non-zero `lower`; a real v6 has
33
+ * type=v6). That degradation is deliberate: silently treating garbage as a
34
+ * match would nullify the whole binding.
35
+ */
36
+ var ipMatchesSession = (operatorIp, sessionIp) => {
37
+ const parsed = require_compositeIpAddress.getCompositeIpAddress(operatorIp);
38
+ if (parsed.type !== sessionIp.type) return false;
39
+ const lower = asBigInt(sessionIp.lower);
40
+ const upper = asBigInt(sessionIp.upper);
41
+ if (lower === null || upper === null) return false;
42
+ return parsed.lower === lower && (parsed.upper ?? 0n) === upper;
43
+ };
44
+ //#endregion
45
+ exports.ipMatchesSession = ipMatchesSession;
@@ -1,12 +1,12 @@
1
1
  const require_usageCounters = require("../../util/usageCounters.cjs");
2
2
  const require_decisionMachineRunner = require("../decisionMachine/decisionMachineRunner.cjs");
3
3
  const require_lang = require("../../rules/lang.cjs");
4
+ const require_clientMetaData = require("../../utils/clientMetaData.cjs");
4
5
  const require_compositeIpAddress = require("../../compositeIpAddress.cjs");
5
6
  const require_enrichDnsEvent = require("../dnsEvent/enrichDnsEvent.cjs");
6
7
  const require_captchaManager = require("../captchaManager.cjs");
7
8
  const require_pairs = require("../../pairs.cjs");
8
9
  const require_util = require("../../util.cjs");
9
- const require_clientMetaData = require("../../utils/clientMetaData.cjs");
10
10
  const require_devicePlatform = require("../../utils/devicePlatform.cjs");
11
11
  const require_frictionlessTasksUtils = require("../frictionless/frictionlessTasksUtils.cjs");
12
12
  const require_evaluateEmailSpamRules = require("../spam/evaluateEmailSpamRules.cjs");
@@ -1,11 +1,11 @@
1
1
  const require_usageCounters = require("../../util/usageCounters.cjs");
2
2
  const require_decisionMachineRunner = require("../decisionMachine/decisionMachineRunner.cjs");
3
+ const require_clientMetaData = require("../../utils/clientMetaData.cjs");
3
4
  const require_compositeIpAddress = require("../../compositeIpAddress.cjs");
4
5
  const require_enrichDnsEvent = require("../dnsEvent/enrichDnsEvent.cjs");
5
6
  const require_captchaManager = require("../captchaManager.cjs");
6
7
  const require_routingMachine = require("../frictionless/routingMachine.cjs");
7
8
  const require_util = require("../../util.cjs");
8
- const require_clientMetaData = require("../../utils/clientMetaData.cjs");
9
9
  const require_devicePlatform = require("../../utils/devicePlatform.cjs");
10
10
  const require_frictionlessTasksUtils = require("../frictionless/frictionlessTasksUtils.cjs");
11
11
  const require_evaluateEmailSpamRules = require("../spam/evaluateEmailSpamRules.cjs");
@@ -1,10 +1,10 @@
1
1
  const require_usageCounters = require("../../util/usageCounters.cjs");
2
2
  const require_decisionMachineRunner = require("../decisionMachine/decisionMachineRunner.cjs");
3
+ const require_clientMetaData = require("../../utils/clientMetaData.cjs");
3
4
  const require_compositeIpAddress = require("../../compositeIpAddress.cjs");
4
5
  const require_enrichDnsEvent = require("../dnsEvent/enrichDnsEvent.cjs");
5
6
  const require_captchaManager = require("../captchaManager.cjs");
6
7
  const require_util = require("../../util.cjs");
7
- const require_clientMetaData = require("../../utils/clientMetaData.cjs");
8
8
  const require_devicePlatform = require("../../utils/devicePlatform.cjs");
9
9
  const require_frictionlessTasksUtils = require("../frictionless/frictionlessTasksUtils.cjs");
10
10
  const require_evaluateEmailSpamRules = require("../spam/evaluateEmailSpamRules.cjs");
@@ -18,6 +18,7 @@ export declare const getBotScore: (payload: string, headHash: string, privateKey
18
18
  md?: undefined;
19
19
  bn?: undefined;
20
20
  fs?: undefined;
21
+ b?: undefined;
21
22
  } | {
22
23
  baseBotScore: number;
23
24
  timestamp: number;
@@ -38,5 +39,6 @@ export declare const getBotScore: (payload: string, headHash: string, privateKey
38
39
  md: boolean | undefined;
39
40
  bn: boolean | undefined;
40
41
  fs: boolean | undefined;
42
+ b: Record<string, string[]> | undefined;
41
43
  }>;
42
44
  //# sourceMappingURL=getBotScore.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"getBotScore.d.ts","sourceRoot":"","sources":["../../../src/tasks/detection/getBotScore.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,WAAW,YACd,MAAM,YACL,MAAM,qBACG,MAAM,uBACJ,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4D3B,CAAC"}
1
+ {"version":3,"file":"getBotScore.d.ts","sourceRoot":"","sources":["../../../src/tasks/detection/getBotScore.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,WAAW,YACd,MAAM,YACL,MAAM,qBACG,MAAM,uBACJ,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8D3B,CAAC"}
@@ -21,6 +21,7 @@ var getBotScore = async (payload, headHash, privateKeyString, innerConfigEncoded
21
21
  const md = result.md;
22
22
  const bn = result.bn;
23
23
  const fs = result.fs;
24
+ const b = result.b;
24
25
  if (baseBotScore === void 0 || Number.isNaN(baseBotScore)) return {
25
26
  baseBotScore: 1,
26
27
  timestamp: 0
@@ -44,7 +45,8 @@ var getBotScore = async (payload, headHash, privateKeyString, innerConfigEncoded
44
45
  sw,
45
46
  md,
46
47
  bn,
47
- fs
48
+ fs,
49
+ b
48
50
  };
49
51
  };
50
52
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"getBotScore.js","sourceRoot":"","sources":["../../../src/tasks/detection/getBotScore.ts"],"names":[],"mappings":"AAeA,OAAO,sBAAsB,MAAM,oBAAoB,CAAC;AAExD,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,EAC/B,OAAe,EACf,QAAgB,EAChB,gBAAyB,EACzB,kBAA2B,EAC1B,EAAE;IACH,MAAM,MAAM,GAAG,CAAC,MAAM,sBAAsB,CAC3C,OAAO,EACP,QAAQ,EACR,gBAAgB,EAChB,kBAAkB,CAClB,CAAmB,CAAC;IACrB,MAAM,YAAY,GAAW,MAAM,CAAC,KAAK,CAAC;IAC1C,MAAM,SAAS,GAAW,MAAM,CAAC,SAAS,CAAC;IAC3C,MAAM,MAAM,GAAW,MAAM,CAAC,MAAM,CAAC;IACrC,MAAM,SAAS,GAAW,MAAM,CAAC,SAAS,CAAC;IAC3C,MAAM,SAAS,GAAY,MAAM,CAAC,SAAS,IAAI,KAAK,CAAC;IACrD,MAAM,QAAQ,GAAY,MAAM,CAAC,QAAQ,IAAI,KAAK,CAAC;IACnD,MAAM,iBAAiB,GAAW,MAAM,CAAC,iBAAiB,CAAC;IAC3D,MAAM,kBAAkB,GAAyB,MAAM,CAAC,kBAAkB,CAAC;IAC3E,MAAM,gBAAgB,GAAwB,MAAM,CAAC,gBAAgB,CAAC;IACtE,MAAM,4BAA4B,GACjC,MAAM,CAAC,4BAA4B,CAAC;IACrC,MAAM,wBAAwB,GAC7B,MAAM,CAAC,wBAAwB,CAAC;IACjC,MAAM,wBAAwB,GAC7B,MAAM,CAAC,wBAAwB,CAAC;IACjC,MAAM,sBAAsB,GAC3B,MAAM,CAAC,sBAAsB,CAAC;IAC/B,MAAM,CAAC,GAAuB,MAAM,CAAC,CAAC,CAAC;IACvC,MAAM,CAAC,GAAwB,MAAM,CAAC,CAAC,CAAC;IACxC,MAAM,EAAE,GAAwB,MAAM,CAAC,EAAE,CAAC;IAC1C,MAAM,EAAE,GAAwB,MAAM,CAAC,EAAE,CAAC;IAC1C,MAAM,EAAE,GAAwB,MAAM,CAAC,EAAE,CAAC;IAC1C,MAAM,EAAE,GAAwB,MAAM,CAAC,EAAE,CAAC;IAE1C,IAAI,YAAY,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC;QAC9D,OAAO;YACN,YAAY,EAAE,CAAC;YACf,SAAS,EAAE,CAAC;SACZ,CAAC;IACH,CAAC;IAED,OAAO;QACN,YAAY;QACZ,SAAS;QACT,MAAM;QACN,SAAS;QACT,SAAS;QACT,QAAQ;QACR,iBAAiB;QACjB,kBAAkB;QAClB,gBAAgB;QAChB,4BAA4B;QAC5B,wBAAwB;QACxB,wBAAwB;QACxB,sBAAsB;QACtB,CAAC;QACD,CAAC;QACD,EAAE;QACF,EAAE;QACF,EAAE;QACF,EAAE;KACF,CAAC;AACH,CAAC,CAAC"}
1
+ {"version":3,"file":"getBotScore.js","sourceRoot":"","sources":["../../../src/tasks/detection/getBotScore.ts"],"names":[],"mappings":"AAeA,OAAO,sBAAsB,MAAM,oBAAoB,CAAC;AAExD,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,EAC/B,OAAe,EACf,QAAgB,EAChB,gBAAyB,EACzB,kBAA2B,EAC1B,EAAE;IACH,MAAM,MAAM,GAAG,CAAC,MAAM,sBAAsB,CAC3C,OAAO,EACP,QAAQ,EACR,gBAAgB,EAChB,kBAAkB,CAClB,CAAmB,CAAC;IACrB,MAAM,YAAY,GAAW,MAAM,CAAC,KAAK,CAAC;IAC1C,MAAM,SAAS,GAAW,MAAM,CAAC,SAAS,CAAC;IAC3C,MAAM,MAAM,GAAW,MAAM,CAAC,MAAM,CAAC;IACrC,MAAM,SAAS,GAAW,MAAM,CAAC,SAAS,CAAC;IAC3C,MAAM,SAAS,GAAY,MAAM,CAAC,SAAS,IAAI,KAAK,CAAC;IACrD,MAAM,QAAQ,GAAY,MAAM,CAAC,QAAQ,IAAI,KAAK,CAAC;IACnD,MAAM,iBAAiB,GAAW,MAAM,CAAC,iBAAiB,CAAC;IAC3D,MAAM,kBAAkB,GAAyB,MAAM,CAAC,kBAAkB,CAAC;IAC3E,MAAM,gBAAgB,GAAwB,MAAM,CAAC,gBAAgB,CAAC;IACtE,MAAM,4BAA4B,GACjC,MAAM,CAAC,4BAA4B,CAAC;IACrC,MAAM,wBAAwB,GAC7B,MAAM,CAAC,wBAAwB,CAAC;IACjC,MAAM,wBAAwB,GAC7B,MAAM,CAAC,wBAAwB,CAAC;IACjC,MAAM,sBAAsB,GAC3B,MAAM,CAAC,sBAAsB,CAAC;IAC/B,MAAM,CAAC,GAAuB,MAAM,CAAC,CAAC,CAAC;IACvC,MAAM,CAAC,GAAwB,MAAM,CAAC,CAAC,CAAC;IACxC,MAAM,EAAE,GAAwB,MAAM,CAAC,EAAE,CAAC;IAC1C,MAAM,EAAE,GAAwB,MAAM,CAAC,EAAE,CAAC;IAC1C,MAAM,EAAE,GAAwB,MAAM,CAAC,EAAE,CAAC;IAC1C,MAAM,EAAE,GAAwB,MAAM,CAAC,EAAE,CAAC;IAC1C,MAAM,CAAC,GAAyC,MAAM,CAAC,CAAC,CAAC;IAEzD,IAAI,YAAY,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC;QAC9D,OAAO;YACN,YAAY,EAAE,CAAC;YACf,SAAS,EAAE,CAAC;SACZ,CAAC;IACH,CAAC;IAED,OAAO;QACN,YAAY;QACZ,SAAS;QACT,MAAM;QACN,SAAS;QACT,SAAS;QACT,QAAQ;QACR,iBAAiB;QACjB,kBAAkB;QAClB,gBAAgB;QAChB,4BAA4B;QAC5B,wBAAwB;QACxB,wBAAwB;QACxB,sBAAsB;QACtB,CAAC;QACD,CAAC;QACD,EAAE;QACF,EAAE;QACF,EAAE;QACF,EAAE;QACF,CAAC;KACD,CAAC;AACH,CAAC,CAAC"}
@@ -27,6 +27,11 @@ export declare class FrictionlessManager extends CaptchaManager {
27
27
  updateScore(score: number, scoreComponents: ScoreComponents): void;
28
28
  checkLangRules(acceptLanguage: string): number;
29
29
  createSession(token: string, score: number, threshold: number, scoreComponents: ScoreComponents, ipAddress: CompositeIpAddress, captchaType: CaptchaType, siteKey: string, solvedImagesCount?: number, powDifficulty?: number, userSitekeyIpHash?: string, webView?: boolean, iFrame?: boolean, decryptedHeadHash?: string, reason?: FrictionlessReason, blocked?: boolean, deleted?: boolean, ipInfo?: IPInfoResponse, headers?: RequestHeaders, mode?: ModeEnum, simdReadings?: Session["simdReadings"], entropyMathRandomFingerprint?: Session["entropyMathRandomFingerprint"], entropyCryptoFingerprint?: Session["entropyCryptoFingerprint"], entropyWallClockOffsetMs?: Session["entropyWallClockOffsetMs"], entropyMathRandomFirst?: Session["entropyMathRandomFirst"], bundleId?: Session["bundleId"], currentUrl?: Session["currentUrl"], tcpToChelloUs?: Session["tcpToChelloUs"], chelloToHandshakeUs?: Session["chelloToHandshakeUs"], isEscalation?: Session["isEscalation"], iframeUrl?: Session["iframeUrl"], isProtect?: Session["isProtect"], originSessionId?: Session["originSessionId"], g?: Session["g"], matchedRule?: Session["matchedRule"], i?: Session["i"], sw?: Session["sw"], md?: Session["md"], bn?: Session["bn"], fs?: Session["fs"], rawTlsSignals?: Partial<RawTlsSignals>, puzzleOverrides?: Pick<Session, "puzzleTolerance" | "puzzle">): Promise<Session>;
30
+ createAuthenticatedSession(token: string, ipAddress: CompositeIpAddress, webBotAuthAgent: string, siteKey: string, userSitekeyIpHash?: string, headers?: RequestHeaders, ipInfo?: IPInfoResponse, clientSessionId?: string): Promise<Session>;
31
+ verifyAuthenticatedSession(sessionId: string, ip: string | undefined, clientSessionId: string | undefined): Promise<{
32
+ verified: boolean;
33
+ status: string;
34
+ }>;
30
35
  sendImageCaptcha(params?: Partial<ImageCaptchaSessionParams>): Promise<GetFrictionlessCaptchaResponse>;
31
36
  sendPowCaptcha(params?: Partial<PowCaptchaSessionParams>): Promise<GetFrictionlessCaptchaResponse>;
32
37
  sendPuzzleCaptcha(params?: Partial<PuzzleCaptchaSessionParams>): Promise<GetFrictionlessCaptchaResponse>;
@@ -1 +1 @@
1
- {"version":3,"file":"frictionlessTasks.d.ts","sourceRoot":"","sources":["../../../src/tasks/frictionless/frictionlessTasks.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"frictionlessTasks.d.ts","sourceRoot":"","sources":["../../../src/tasks/frictionless/frictionlessTasks.ts"],"names":[],"mappings":"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"}
@@ -2,12 +2,14 @@ import { buildAllWindowIncrements } from "../../util/usageCounters.js";
2
2
  import { DecisionMachineRunner } from "../decisionMachine/decisionMachineRunner.js";
3
3
  import { buildDnsEventUrl } from "../../api/dnsEventUrl.js";
4
4
  import { checkLangRules } from "../../rules/lang.js";
5
+ import { isClientSessionMismatch } from "../../utils/clientMetaData.js";
5
6
  import { CaptchaManager } from "../captchaManager.js";
6
7
  import { coerceToEnabledCaptchaType } from "../captchaTypeSelection.js";
7
8
  import { getBotScore } from "../detection/getBotScore.js";
8
9
  import { samplePuzzleDifficulty } from "../puzzle/puzzleDifficulty.js";
10
+ import { ipMatchesSession } from "./ipMatch.js";
9
11
  import { applyRouter } from "./routingMachine.js";
10
- import { ApiParams, CaptchaType, FrictionlessReason as FrictionlessReason$1, SimdReadingsStage, clampImageRounds } from "@prosopo/types";
12
+ import { ApiParams, CaptchaType, FrictionlessReason as FrictionlessReason$1, NO_MEASUREMENT_REASONS, SimdReadingsStage, clampImageRounds, puzzleMaxDifficultyDefault } from "@prosopo/types";
11
13
  import { severityToPuzzleDifficulty } from "@prosopo/captcha-severity";
12
14
  import { DEFAULT_RENDER_SETTINGS } from "@prosopo/puzzle-assets";
13
15
  import { v4 } from "uuid";
@@ -172,6 +174,91 @@ var FrictionlessManager = class extends CaptchaManager {
172
174
  }
173
175
  return sessionRecord;
174
176
  }
177
+ /**
178
+ * Dedicated issuance path for Web Bot Auth verified requests. The signature
179
+ * verification already carried the trust decision; there is no captcha to
180
+ * solve, no bot score to compute, no routing to run. The session is minted
181
+ * with `captchaType: authenticated`, `agent: true`, `webBotAuthAgent` set to
182
+ * the verified Signature-Agent URL, and `ipAddress` frozen for the verify-
183
+ * time IP-binding check. Consumed only by `/client/authenticated/verify`.
184
+ */
185
+ async createAuthenticatedSession(token, ipAddress, webBotAuthAgent, siteKey, userSitekeyIpHash, headers, ipInfo, clientSessionId) {
186
+ const sessionRecord = {
187
+ sessionId: `${getSessionIDPrefix(this.config.host)}-${v4()}`,
188
+ createdAt: /* @__PURE__ */ new Date(),
189
+ token,
190
+ score: 0,
191
+ threshold: 0,
192
+ scoreComponents: { baseScore: 0 },
193
+ ipAddress,
194
+ captchaType: CaptchaType.authenticated,
195
+ userSitekeyIpHash,
196
+ webView: false,
197
+ iFrame: false,
198
+ decryptedHeadHash: "",
199
+ siteKey,
200
+ serverChecked: false,
201
+ agent: true,
202
+ webBotAuthAgent,
203
+ ...ipInfo && { ipInfo },
204
+ ...headers && { headers },
205
+ ...clientSessionId && { clientMetaData: { clientSessionId } }
206
+ };
207
+ await this.db.storeSessionRecord(sessionRecord);
208
+ if (this.writeQueue) {
209
+ const cacheData = sessionRecord;
210
+ const cachePromises = [this.writeQueue.cacheSession(sessionRecord.sessionId, cacheData)];
211
+ if (userSitekeyIpHash) cachePromises.push(this.writeQueue.cacheSessionByHash(userSitekeyIpHash, sessionRecord.sessionId));
212
+ await Promise.all(cachePromises).catch(() => {});
213
+ }
214
+ return sessionRecord;
215
+ }
216
+ /**
217
+ * Verify an authenticated (Web Bot Auth) session. Called from
218
+ * `/client/authenticated/verify` after the operator forwards their
219
+ * dApp-signed token. Enforces the four properties that make replay
220
+ * infeasible:
221
+ * 1. session exists and was minted with captchaType=authenticated
222
+ * (so ordinary captcha tokens can't be redeemed here)
223
+ * 2. session hasn't been consumed (serverChecked === false)
224
+ * 3. operator forwarded the client IP (`ip` is required — silently
225
+ * dropping the check would nullify the whole binding)
226
+ * 4. the forwarded IP matches the IP the session was issued to
227
+ *
228
+ * Marks serverChecked=true on success so subsequent verifies fail loudly.
229
+ */
230
+ async verifyAuthenticatedSession(sessionId, ip, clientSessionId) {
231
+ if (!ip) return {
232
+ verified: false,
233
+ status: "API.AUTHENTICATED_IP_REQUIRED"
234
+ };
235
+ const session = await this.db.getSessionRecordBySessionId(sessionId);
236
+ if (!session) return {
237
+ verified: false,
238
+ status: "API.USER_NOT_VERIFIED_NO_SOLUTION"
239
+ };
240
+ if (session.captchaType !== CaptchaType.authenticated) return {
241
+ verified: false,
242
+ status: "API.INCORRECT_CAPTCHA_TYPE"
243
+ };
244
+ if (session.serverChecked) return {
245
+ verified: false,
246
+ status: "API.USER_ALREADY_VERIFIED"
247
+ };
248
+ if (!ipMatchesSession(ip, session.ipAddress)) return {
249
+ verified: false,
250
+ status: "API.AUTHENTICATED_IP_MISMATCH"
251
+ };
252
+ if (isClientSessionMismatch(clientSessionId, session.clientMetaData?.clientSessionId)) return {
253
+ verified: false,
254
+ status: "API.CLIENT_SESSION_MISMATCH"
255
+ };
256
+ await this.db.updateSessionRecord(sessionId, { serverChecked: true });
257
+ return {
258
+ verified: true,
259
+ status: "API.USER_VERIFIED"
260
+ };
261
+ }
175
262
  async sendImageCaptcha(params) {
176
263
  return this.sendCaptcha(CaptchaType.image, params);
177
264
  }
@@ -198,8 +285,10 @@ var FrictionlessManager = class extends CaptchaManager {
198
285
  const imageRoundsBounds = this.routingContext ?? {};
199
286
  const finalSolvedImagesCount = finalCaptchaType === CaptchaType.image ? requestedSolvedImagesCount !== void 0 ? clampImageRounds(requestedSolvedImagesCount, imageRoundsBounds) : requestedSolvedImagesCount : void 0;
200
287
  const finalPowDifficulty = finalCaptchaType === CaptchaType.pow ? routed.powDifficulty ?? effectiveParams.powDifficulty : void 0;
288
+ const finalReason = routed.reason ?? effectiveParams.reason;
201
289
  const finalPuzzleOverrides = finalCaptchaType === CaptchaType.puzzle ? (() => {
202
- const level = severityToPuzzleDifficulty(requestedSolvedImagesCount, this.routingContext?.baseImageRounds ?? this.config.captchas.solved.count);
290
+ const maxLevel = this.routingContext?.puzzleMaxDifficulty ?? puzzleMaxDifficultyDefault;
291
+ const level = finalReason !== void 0 && NO_MEASUREMENT_REASONS.has(finalReason) ? 0 : severityToPuzzleDifficulty(requestedSolvedImagesCount, this.routingContext?.baseImageRounds ?? this.config.captchas.solved.count, maxLevel);
203
292
  const difficulty = level > 0 ? samplePuzzleDifficulty(level, DEFAULT_RENDER_SETTINGS.holeDarken) : void 0;
204
293
  const tolerance = routed.puzzleTolerance ?? difficulty?.tolerance;
205
294
  const puzzle = routed.puzzle || difficulty ? {
@@ -211,7 +300,6 @@ var FrictionlessManager = class extends CaptchaManager {
211
300
  ...puzzle !== void 0 && { puzzle }
212
301
  };
213
302
  })() : {};
214
- const finalReason = routed.reason ?? effectiveParams.reason;
215
303
  const blocked = finalCaptchaType === CaptchaType.image ? effectiveParams.blocked : void 0;
216
304
  const sessionRecord = await this.createSession(effectiveParams.token, effectiveParams.score, effectiveParams.threshold, effectiveParams.scoreComponents, effectiveParams.ipAddress, finalCaptchaType, effectiveParams.siteKey, finalSolvedImagesCount, finalPowDifficulty, effectiveParams.userSitekeyIpHash, effectiveParams.webView ?? false, effectiveParams.iFrame ?? false, effectiveParams.decryptedHeadHash, finalReason, blocked, void 0, effectiveParams.ipInfo, effectiveParams.headers, effectiveParams.mode, effectiveParams.simdReadings, effectiveParams.entropyMathRandomFingerprint, effectiveParams.entropyCryptoFingerprint, effectiveParams.entropyWallClockOffsetMs, effectiveParams.entropyMathRandomFirst, effectiveParams.bundleId, effectiveParams.currentUrl, effectiveParams.tcpToChelloUs, effectiveParams.chelloToHandshakeUs, void 0, effectiveParams.iframeUrl, effectiveParams.isProtect, void 0, effectiveParams.g, effectiveParams.matchedRule, effectiveParams.i, effectiveParams.sw, effectiveParams.md, effectiveParams.bn, effectiveParams.fs, {
217
305
  synNs: effectiveParams.synNs,