@prosopo/provider 5.0.4 → 5.1.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.
- package/.turbo/turbo-build$colon$cjs.log +17 -16
- package/.turbo/turbo-build$colon$tsc.log +24 -24
- package/.turbo/turbo-build.log +18 -17
- package/CHANGELOG.md +74 -0
- package/dist/api/blacklistRequestInspector.d.ts.map +1 -1
- package/dist/api/blacklistRequestInspector.js +3 -2
- package/dist/api/blacklistRequestInspector.js.map +1 -1
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.d.ts +2 -2
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.d.ts.map +1 -1
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.js +2 -0
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.js.map +1 -1
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.d.ts.map +1 -1
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js +12 -0
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js.map +1 -1
- package/dist/api/captcha/getImageCaptchaChallenge.d.ts.map +1 -1
- package/dist/api/captcha/getImageCaptchaChallenge.js +13 -1
- package/dist/api/captcha/getImageCaptchaChallenge.js.map +1 -1
- package/dist/api/captcha/getPoWCaptchaChallenge.d.ts.map +1 -1
- package/dist/api/captcha/getPoWCaptchaChallenge.js +12 -1
- package/dist/api/captcha/getPoWCaptchaChallenge.js.map +1 -1
- package/dist/api/captcha/getPuzzleCaptchaChallenge.d.ts.map +1 -1
- package/dist/api/captcha/getPuzzleCaptchaChallenge.js +12 -1
- package/dist/api/captcha/getPuzzleCaptchaChallenge.js.map +1 -1
- package/dist/api/captcha/trafficFilterRequestTime.d.ts +38 -0
- package/dist/api/captcha/trafficFilterRequestTime.d.ts.map +1 -0
- package/dist/api/captcha/trafficFilterRequestTime.js +109 -0
- package/dist/api/captcha/trafficFilterRequestTime.js.map +1 -0
- package/dist/cjs/api/blacklistRequestInspector.cjs +2 -1
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.cjs +2 -0
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/handler.cjs +13 -0
- package/dist/cjs/api/captcha/getImageCaptchaChallenge.cjs +13 -1
- package/dist/cjs/api/captcha/getPoWCaptchaChallenge.cjs +12 -1
- package/dist/cjs/api/captcha/getPuzzleCaptchaChallenge.cjs +12 -1
- package/dist/cjs/api/captcha/trafficFilterRequestTime.cjs +111 -0
- package/dist/cjs/tasks/captchaManager.cjs +13 -6
- package/dist/cjs/tasks/dnsEvent/enrichDnsEvent.cjs +10 -8
- package/dist/cjs/tasks/frictionless/frictionlessTasks.cjs +19 -4
- package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasks.cjs +5 -1
- package/dist/cjs/tasks/powCaptcha/powTasks.cjs +5 -1
- package/dist/cjs/tasks/puzzleCaptcha/puzzleTasks.cjs +5 -4
- package/dist/cjs/tasks/spam/checkTrafficFilter.cjs +101 -75
- package/dist/tasks/captchaManager.d.ts +3 -3
- package/dist/tasks/captchaManager.d.ts.map +1 -1
- package/dist/tasks/captchaManager.js +14 -7
- package/dist/tasks/captchaManager.js.map +1 -1
- package/dist/tasks/dnsEvent/enrichDnsEvent.d.ts.map +1 -1
- package/dist/tasks/dnsEvent/enrichDnsEvent.js +10 -8
- package/dist/tasks/dnsEvent/enrichDnsEvent.js.map +1 -1
- package/dist/tasks/frictionless/frictionlessTasks.d.ts +2 -1
- package/dist/tasks/frictionless/frictionlessTasks.d.ts.map +1 -1
- package/dist/tasks/frictionless/frictionlessTasks.js +19 -4
- package/dist/tasks/frictionless/frictionlessTasks.js.map +1 -1
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.d.ts +1 -1
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.d.ts.map +1 -1
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.js +5 -1
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.js.map +1 -1
- package/dist/tasks/powCaptcha/powTasks.d.ts +1 -1
- package/dist/tasks/powCaptcha/powTasks.d.ts.map +1 -1
- package/dist/tasks/powCaptcha/powTasks.js +5 -1
- package/dist/tasks/powCaptcha/powTasks.js.map +1 -1
- package/dist/tasks/puzzleCaptcha/puzzleTasks.d.ts +1 -1
- package/dist/tasks/puzzleCaptcha/puzzleTasks.d.ts.map +1 -1
- package/dist/tasks/puzzleCaptcha/puzzleTasks.js +5 -4
- package/dist/tasks/puzzleCaptcha/puzzleTasks.js.map +1 -1
- package/dist/tasks/spam/checkTrafficFilter.d.ts +18 -1
- package/dist/tasks/spam/checkTrafficFilter.d.ts.map +1 -1
- package/dist/tasks/spam/checkTrafficFilter.js +101 -77
- package/dist/tasks/spam/checkTrafficFilter.js.map +1 -1
- package/dist/tests/integration/clientSettingsPersistence.integration.test.js +24 -17
- package/dist/tests/integration/clientSettingsPersistence.integration.test.js.map +1 -1
- package/dist/tests/integration/trafficFilterHierarchy.integration.test.d.ts +2 -0
- package/dist/tests/integration/trafficFilterHierarchy.integration.test.d.ts.map +1 -0
- package/dist/tests/integration/trafficFilterHierarchy.integration.test.js +215 -0
- package/dist/tests/integration/trafficFilterHierarchy.integration.test.js.map +1 -0
- package/dist/tests/unit/api/blacklistRequestInspector.unit.test.js +6 -0
- package/dist/tests/unit/api/blacklistRequestInspector.unit.test.js.map +1 -1
- package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.unit.test.js +58 -0
- package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.unit.test.js.map +1 -1
- package/dist/tests/unit/api/captcha/trafficFilterRequestTime.unit.test.d.ts +2 -0
- package/dist/tests/unit/api/captcha/trafficFilterRequestTime.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/api/captcha/trafficFilterRequestTime.unit.test.js +255 -0
- package/dist/tests/unit/api/captcha/trafficFilterRequestTime.unit.test.js.map +1 -0
- package/dist/tests/unit/api/getFrictionlessCaptchaChallenge.unit.test.js +2 -0
- package/dist/tests/unit/api/getFrictionlessCaptchaChallenge.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/dnsEvent/enrichDnsEvent.unit.test.js +6 -9
- package/dist/tests/unit/tasks/dnsEvent/enrichDnsEvent.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.unit.test.js +49 -0
- package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.js +7 -0
- package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/puzzleCaptcha/puzzleTasks.unit.test.js +130 -0
- package/dist/tests/unit/tasks/puzzleCaptcha/puzzleTasks.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/spam/checkTrafficFilter.unit.test.js +272 -98
- package/dist/tests/unit/tasks/spam/checkTrafficFilter.unit.test.js.map +1 -1
- package/package.json +14 -14
|
@@ -438,7 +438,10 @@ var CaptchaManager = class CaptchaManager {
|
|
|
438
438
|
return require_checkSpamEmail.checkSpamEmail(email, this.db, this.config, this.logger);
|
|
439
439
|
}
|
|
440
440
|
/**
|
|
441
|
-
* Resolves the IP info to feed to `checkTrafficFilter` and runs the check
|
|
441
|
+
* Resolves the IP info to feed to `checkTrafficFilter` and runs the check
|
|
442
|
+
* at submit time. Only `action: "block"` matches produce `isBlocked:true`;
|
|
443
|
+
* `action: "challenge"` matches were already applied at request time and
|
|
444
|
+
* are ignored here.
|
|
442
445
|
*
|
|
443
446
|
* - The captcha record already carries the IPInfoResponse from request
|
|
444
447
|
* time (ipInfoMiddleware → storeXxxRecord), so by default we reuse it
|
|
@@ -448,9 +451,10 @@ var CaptchaManager = class CaptchaManager {
|
|
|
448
451
|
* and may differ from the IP that originally requested the captcha.
|
|
449
452
|
* - When the session carries a `dnsEvent`, its `peerIp` and `resolverIp`
|
|
450
453
|
* are enriched and passed alongside the primary IP.
|
|
451
|
-
* -
|
|
452
|
-
* blocked even when the site hasn't configured a
|
|
453
|
-
*
|
|
454
|
+
* - The abuser category defaults to `{action:"block"}` so abusive
|
|
455
|
+
* networks are always blocked even when the site hasn't configured a
|
|
456
|
+
* trafficFilter.
|
|
457
|
+
* - Returns `{ isBlocked: false }` if no category is active, without
|
|
454
458
|
* consulting the payload at all.
|
|
455
459
|
*
|
|
456
460
|
* Callers handle the "blocked" branch themselves (each verify path
|
|
@@ -459,10 +463,13 @@ var CaptchaManager = class CaptchaManager {
|
|
|
459
463
|
*/
|
|
460
464
|
async resolveTrafficFilterCheck(env, recordIpInfo, trafficFilter, currentIp, enrichedDnsEvent) {
|
|
461
465
|
const effective = {
|
|
462
|
-
|
|
466
|
+
abuser: { action: _prosopo_types.TrafficFilterAction.Block },
|
|
463
467
|
...trafficFilter
|
|
464
468
|
};
|
|
465
|
-
if (!
|
|
469
|
+
if (!(effective.vpn !== void 0 || effective.proxy !== void 0 || effective.tor !== void 0 || effective.abuser !== void 0 || effective.datacenter !== void 0 || effective.mobile !== void 0 || effective.satellite !== void 0 || effective.crawler !== void 0)) return {
|
|
470
|
+
isBlocked: false,
|
|
471
|
+
matches: []
|
|
472
|
+
};
|
|
466
473
|
const ipInfo = currentIp ? await env.ipInfoService.lookup(currentIp) : recordIpInfo;
|
|
467
474
|
return require_checkTrafficFilter.checkTrafficFilter(ipInfo, effective, require_enrichDnsEvent.extraIpInfosFromEnrichedDnsEvent(enrichedDnsEvent), enrichedDnsEvent?.pathValid);
|
|
468
475
|
}
|
|
@@ -46,15 +46,17 @@ var extraIpInfosFromEnrichedDnsEvent = (enriched) => {
|
|
|
46
46
|
* - client IP is a consumer ISP but resolver is datacenter (compound)
|
|
47
47
|
*
|
|
48
48
|
* When `trafficFilter` is supplied, the datacenter and abuser contributions
|
|
49
|
-
* are gated on the same rules `checkTrafficFilter` uses for the client IP
|
|
49
|
+
* are gated on the same rules `checkTrafficFilter` uses for the client IP.
|
|
50
|
+
* A category counts here whenever the operator has configured any policy
|
|
51
|
+
* for it (block or challenge) — either intent signals suspicion.
|
|
50
52
|
*
|
|
51
|
-
* -
|
|
53
|
+
* - datacenter category must be configured; IPs whose
|
|
52
54
|
* `providerType === "isp"` short-circuit; named allowlist skips
|
|
53
|
-
* -
|
|
55
|
+
* - abuser category defaults to configured, with the score threshold
|
|
54
56
|
* - cross-category suppression: VPN / proxy / Tor / crawler IPs that also
|
|
55
57
|
* carry `isDatacenter=true` are NOT counted as datacenter when the
|
|
56
|
-
* operator hasn't
|
|
57
|
-
*
|
|
58
|
+
* operator hasn't configured that more specific category — mirrors
|
|
59
|
+
* the `datacenterSuppressedByCategory` rule in evaluateIpInfo.
|
|
58
60
|
*
|
|
59
61
|
* `pathValid` is a protocol signal, not a category — it always contributes
|
|
60
62
|
* regardless of trafficFilter. When `trafficFilter` is omitted, all
|
|
@@ -68,16 +70,16 @@ var computeDnsAsymmetry = (enriched, clientIpInfo, trafficFilter) => {
|
|
|
68
70
|
const countDc = (ip) => {
|
|
69
71
|
if (!ip?.isValid || !ip.isDatacenter) return false;
|
|
70
72
|
if (!trafficFilter) return true;
|
|
71
|
-
if (trafficFilter.
|
|
73
|
+
if (trafficFilter.datacenter === void 0) return false;
|
|
72
74
|
if (require_checkTrafficFilter.isDatacenterDenylisted(ip, trafficFilter.datacenterNameDenylist)) return true;
|
|
73
|
-
if (ip.isVPN && trafficFilter.
|
|
75
|
+
if (ip.isVPN && trafficFilter.vpn === void 0 || ip.isProxy && trafficFilter.proxy === void 0 || ip.isTor && trafficFilter.tor === void 0 || ip.isCrawler && trafficFilter.crawler === void 0) return false;
|
|
74
76
|
if (ip.providerType === "isp") return false;
|
|
75
77
|
return !require_checkTrafficFilter.isDatacenterAllowlisted(ip, trafficFilter.datacenterNameAllowlist);
|
|
76
78
|
};
|
|
77
79
|
const countAbuser = (ip) => {
|
|
78
80
|
if (!ip?.isValid || !ip.isAbuser) return false;
|
|
79
81
|
if (!trafficFilter) return true;
|
|
80
|
-
if (trafficFilter.
|
|
82
|
+
if (trafficFilter.abuser === void 0) return false;
|
|
81
83
|
const threshold = trafficFilter.abuserScoreThreshold ?? _prosopo_types.trafficFilterAbuserScoreThresholdDefault;
|
|
82
84
|
return Math.max(ip.abuserScore ?? 0, ip.companyAbuserScore ?? 0) >= threshold;
|
|
83
85
|
};
|
|
@@ -74,6 +74,20 @@ var FrictionlessManager = class extends require_captchaManager.CaptchaManager {
|
|
|
74
74
|
chelloToHandshakeUs: params.chelloToHandshakeUs
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Record the access rule that matched this request, so every session this
|
|
79
|
+
* request goes on to write carries it.
|
|
80
|
+
*
|
|
81
|
+
* Set separately from `setSessionParams` (which runs before rules are
|
|
82
|
+
* evaluated) and applied at `createSession` time, so it reaches all the
|
|
83
|
+
* outcomes a matched rule can lead to: the 401'd block, the auto-ban a
|
|
84
|
+
* score bump triggered, the captcha type a Restrict rule forced, and the
|
|
85
|
+
* ordinary decision-machine session a score-only Restrict leaves behind.
|
|
86
|
+
* Mirrors `updateScore`'s after-the-fact mutation of the same bag.
|
|
87
|
+
*/
|
|
88
|
+
setMatchedRule(matchedRule) {
|
|
89
|
+
if (this.sessionParams) this.sessionParams.matchedRule = matchedRule;
|
|
90
|
+
}
|
|
77
91
|
updateScore(score, scoreComponents) {
|
|
78
92
|
if (this.sessionParams) {
|
|
79
93
|
this.sessionParams.score = score;
|
|
@@ -83,7 +97,7 @@ var FrictionlessManager = class extends require_captchaManager.CaptchaManager {
|
|
|
83
97
|
checkLangRules(acceptLanguage) {
|
|
84
98
|
return require_lang.checkLangRules(this.config, acceptLanguage);
|
|
85
99
|
}
|
|
86
|
-
async createSession(token, score, threshold, scoreComponents, ipAddress, captchaType, siteKey, solvedImagesCount, powDifficulty, userSitekeyIpHash, webView = false, iFrame = false, decryptedHeadHash = "", reason, blocked, deleted, ipInfo, headers, mode, simdReadings, entropyMathRandomFingerprint, entropyCryptoFingerprint, entropyWallClockOffsetMs, entropyMathRandomFirst, bundleId, currentUrl, tcpToChelloUs, chelloToHandshakeUs, isEscalation, iframeUrl, isProtect, originSessionId, g) {
|
|
100
|
+
async createSession(token, score, threshold, scoreComponents, ipAddress, captchaType, siteKey, solvedImagesCount, powDifficulty, userSitekeyIpHash, webView = false, iFrame = false, decryptedHeadHash = "", reason, blocked, deleted, ipInfo, headers, mode, simdReadings, entropyMathRandomFingerprint, entropyCryptoFingerprint, entropyWallClockOffsetMs, entropyMathRandomFirst, bundleId, currentUrl, tcpToChelloUs, chelloToHandshakeUs, isEscalation, iframeUrl, isProtect, originSessionId, g, matchedRule) {
|
|
87
101
|
const sessionRecord = {
|
|
88
102
|
sessionId: `${getSessionIDPrefix(this.config.host)}-${(0, uuid.v4)()}`,
|
|
89
103
|
createdAt: /* @__PURE__ */ new Date(),
|
|
@@ -120,7 +134,8 @@ var FrictionlessManager = class extends require_captchaManager.CaptchaManager {
|
|
|
120
134
|
entropyMathRandomFirst,
|
|
121
135
|
g,
|
|
122
136
|
tcpToChelloUs,
|
|
123
|
-
chelloToHandshakeUs
|
|
137
|
+
chelloToHandshakeUs,
|
|
138
|
+
...matchedRule && { matchedRule }
|
|
124
139
|
};
|
|
125
140
|
await this.db.storeSessionRecord(sessionRecord);
|
|
126
141
|
if (this.writeQueue) {
|
|
@@ -156,7 +171,7 @@ var FrictionlessManager = class extends require_captchaManager.CaptchaManager {
|
|
|
156
171
|
const finalSolvedImagesCount = finalCaptchaType === _prosopo_types.CaptchaType.image ? routed.solvedImagesCount ?? effectiveParams.solvedImagesCount : void 0;
|
|
157
172
|
const finalPowDifficulty = finalCaptchaType === _prosopo_types.CaptchaType.pow ? routed.powDifficulty ?? effectiveParams.powDifficulty : void 0;
|
|
158
173
|
const blocked = finalCaptchaType === _prosopo_types.CaptchaType.image ? effectiveParams.blocked : void 0;
|
|
159
|
-
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, effectiveParams.reason, 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);
|
|
174
|
+
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, effectiveParams.reason, 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);
|
|
160
175
|
if (this.routingContext && this.usageCounters) this.usageCounters.incrManyAsync(this.routingContext.dappAccount, require_usageCounters.buildAllWindowIncrements("served", finalCaptchaType, this.routingContext.ip, this.routingContext.userAccount));
|
|
161
176
|
return {
|
|
162
177
|
[_prosopo_types.ApiParams.captchaType]: finalCaptchaType,
|
|
@@ -171,7 +186,7 @@ var FrictionlessManager = class extends require_captchaManager.CaptchaManager {
|
|
|
171
186
|
...params
|
|
172
187
|
};
|
|
173
188
|
if (!effectiveParams.token || effectiveParams.score === void 0 || effectiveParams.threshold === void 0 || !effectiveParams.scoreComponents || !effectiveParams.ipAddress || effectiveParams.siteKey === void 0) throw new Error("Session parameters must be set before calling registerBlockedSession");
|
|
174
|
-
await this.createSession(effectiveParams.token, effectiveParams.score, effectiveParams.threshold, effectiveParams.scoreComponents, effectiveParams.ipAddress, _prosopo_types.CaptchaType.image, effectiveParams.siteKey, effectiveParams.solvedImagesCount, void 0, effectiveParams.userSitekeyIpHash, effectiveParams.webView ?? false, effectiveParams.iFrame ?? false, effectiveParams.decryptedHeadHash, effectiveParams.reason, true, true, 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);
|
|
189
|
+
await this.createSession(effectiveParams.token, effectiveParams.score, effectiveParams.threshold, effectiveParams.scoreComponents, effectiveParams.ipAddress, _prosopo_types.CaptchaType.image, effectiveParams.siteKey, effectiveParams.solvedImagesCount, void 0, effectiveParams.userSitekeyIpHash, effectiveParams.webView ?? false, effectiveParams.iFrame ?? false, effectiveParams.decryptedHeadHash, effectiveParams.reason, true, true, 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);
|
|
175
190
|
}
|
|
176
191
|
scoreIncreaseAccessPolicy(accessPolicy, baseBotScore, botScore, scoreComponents) {
|
|
177
192
|
const accessPolicyPenalty = accessPolicy?.frictionlessScore || this.config.penalties.PENALTY_ACCESS_RULE;
|
|
@@ -14,6 +14,7 @@ let _prosopo_types = require("@prosopo/types");
|
|
|
14
14
|
let _prosopo_util = require("@prosopo/util");
|
|
15
15
|
let _prosopo_util_crypto = require("@prosopo/util-crypto");
|
|
16
16
|
let _prosopo_datasets = require("@prosopo/datasets");
|
|
17
|
+
let _prosopo_user_access_policy = require("@prosopo/user-access-policy");
|
|
17
18
|
let _polkadot_util = require("@polkadot/util");
|
|
18
19
|
//#region src/tasks/imgCaptcha/imgCaptchaTasks.ts
|
|
19
20
|
var ImgCaptchaManager = class extends require_captchaManager.CaptchaManager {
|
|
@@ -338,6 +339,7 @@ var ImgCaptchaManager = class extends require_captchaManager.CaptchaManager {
|
|
|
338
339
|
}
|
|
339
340
|
const commitmentUpdates = {};
|
|
340
341
|
let failStatus;
|
|
342
|
+
let matchedRule;
|
|
341
343
|
if (userAccessRulesStorage) try {
|
|
342
344
|
const blockPolicy = await this.checkForHardBlock(userAccessRulesStorage, solution, solution.userAccount, solution.headers, solution.coords, solution.ipInfo?.isValid ? solution.ipInfo.countryCode : void 0, solution.ipInfo?.isValid ? solution.ipInfo.asnNumber : void 0);
|
|
343
345
|
if (blockPolicy) {
|
|
@@ -354,6 +356,7 @@ var ImgCaptchaManager = class extends require_captchaManager.CaptchaManager {
|
|
|
354
356
|
reason: _prosopo_types.ResultReason.ACCESS_POLICY_BLOCK
|
|
355
357
|
};
|
|
356
358
|
failStatus = _prosopo_types.ResultReason.ACCESS_POLICY_BLOCK;
|
|
359
|
+
matchedRule = (0, _prosopo_user_access_policy.describeMatchedRule)(blockPolicy);
|
|
357
360
|
}
|
|
358
361
|
} catch (error) {
|
|
359
362
|
logger.warn(() => ({
|
|
@@ -563,7 +566,8 @@ var ImgCaptchaManager = class extends require_captchaManager.CaptchaManager {
|
|
|
563
566
|
if (solution.sessionId) writePromises.push(this.updateSessionRecordWithCache(solution.sessionId, {
|
|
564
567
|
serverChecked: true,
|
|
565
568
|
result: finalResult,
|
|
566
|
-
...(0, _prosopo_types.isBlockingCaptchaResult)(_prosopo_types.CaptchaType.image, finalResult) && { blocked: true }
|
|
569
|
+
...(0, _prosopo_types.isBlockingCaptchaResult)(_prosopo_types.CaptchaType.image, finalResult) && { blocked: true },
|
|
570
|
+
...matchedRule && { matchedRule }
|
|
567
571
|
}, true));
|
|
568
572
|
if (writePromises.length > 0) await Promise.all(writePromises);
|
|
569
573
|
return {
|
|
@@ -11,6 +11,7 @@ const require_powTasksUtils = require("./powTasksUtils.cjs");
|
|
|
11
11
|
let _prosopo_common = require("@prosopo/common");
|
|
12
12
|
let _prosopo_types = require("@prosopo/types");
|
|
13
13
|
let _prosopo_util = require("@prosopo/util");
|
|
14
|
+
let _prosopo_user_access_policy = require("@prosopo/user-access-policy");
|
|
14
15
|
let _polkadot_util = require("@polkadot/util");
|
|
15
16
|
//#region src/tasks/powCaptcha/powTasks.ts
|
|
16
17
|
var DEFAULT_POW_DIFFICULTY = 4;
|
|
@@ -293,6 +294,7 @@ var PowCaptchaManager = class extends require_captchaManager.CaptchaManager {
|
|
|
293
294
|
const powRecordUpdates = {};
|
|
294
295
|
let failResult;
|
|
295
296
|
let failReason;
|
|
297
|
+
let matchedRule;
|
|
296
298
|
const submittedAt = challengeRecord.submittedAtTimestamp;
|
|
297
299
|
if ((submittedAt instanceof Date ? Date.now() - submittedAt.getTime() : Number.POSITIVE_INFINITY) > timeout) {
|
|
298
300
|
failResult = {
|
|
@@ -316,6 +318,7 @@ var PowCaptchaManager = class extends require_captchaManager.CaptchaManager {
|
|
|
316
318
|
reason: _prosopo_types.ResultReason.ACCESS_POLICY_BLOCK
|
|
317
319
|
};
|
|
318
320
|
failReason = "API.ACCESS_POLICY_BLOCK";
|
|
321
|
+
matchedRule = (0, _prosopo_user_access_policy.describeMatchedRule)(blockPolicy);
|
|
319
322
|
}
|
|
320
323
|
} catch (error) {
|
|
321
324
|
logger.warn(() => ({
|
|
@@ -515,7 +518,8 @@ var PowCaptchaManager = class extends require_captchaManager.CaptchaManager {
|
|
|
515
518
|
if (challengeRecord.sessionId) writePromises.push(this.updateSessionRecordWithCache(challengeRecord.sessionId, {
|
|
516
519
|
serverChecked: true,
|
|
517
520
|
result: finalResult,
|
|
518
|
-
...(0, _prosopo_types.isBlockingCaptchaResult)(_prosopo_types.CaptchaType.pow, finalResult) && { blocked: true }
|
|
521
|
+
...(0, _prosopo_types.isBlockingCaptchaResult)(_prosopo_types.CaptchaType.pow, finalResult) && { blocked: true },
|
|
522
|
+
...matchedRule && { matchedRule }
|
|
519
523
|
}, true));
|
|
520
524
|
if (writePromises.length > 0) await Promise.all(writePromises);
|
|
521
525
|
if (failReason) return notVerified(failReason);
|
|
@@ -11,6 +11,7 @@ const require_puzzleTasksUtils = require("./puzzleTasksUtils.cjs");
|
|
|
11
11
|
let _prosopo_common = require("@prosopo/common");
|
|
12
12
|
let _prosopo_types = require("@prosopo/types");
|
|
13
13
|
let _prosopo_util = require("@prosopo/util");
|
|
14
|
+
let _prosopo_user_access_policy = require("@prosopo/user-access-policy");
|
|
14
15
|
let _polkadot_util = require("@polkadot/util");
|
|
15
16
|
//#region src/tasks/puzzleCaptcha/puzzleTasks.ts
|
|
16
17
|
var PuzzleCaptchaManager = class extends require_captchaManager.CaptchaManager {
|
|
@@ -126,6 +127,7 @@ var PuzzleCaptchaManager = class extends require_captchaManager.CaptchaManager {
|
|
|
126
127
|
const dappAccount = (0, _prosopo_util.at)(challengeSplit, 2);
|
|
127
128
|
this.usageCounters.incrManyAsync(dappAccount, require_usageCounters.buildAllWindowIncrements("solved", _prosopo_types.CaptchaType.puzzle, ipAddress.address, userAccount));
|
|
128
129
|
}
|
|
130
|
+
await this.db.updatePuzzleCaptchaRecord(challenge, { puzzleEvents });
|
|
129
131
|
if (behavioralData) try {
|
|
130
132
|
const bundle = await this.resolveBundleBySessionId(challengeRecord.sessionId);
|
|
131
133
|
const decryptedData = await this.decryptBehavioralData(behavioralData, bundle);
|
|
@@ -152,8 +154,7 @@ var PuzzleCaptchaManager = class extends require_captchaManager.CaptchaManager {
|
|
|
152
154
|
};
|
|
153
155
|
await this.db.updatePuzzleCaptchaRecord(challenge, {
|
|
154
156
|
behavioralDataPacked: packedData,
|
|
155
|
-
deviceCapability: decryptedData.deviceCapability
|
|
156
|
-
puzzleEvents
|
|
157
|
+
deviceCapability: decryptedData.deviceCapability
|
|
157
158
|
});
|
|
158
159
|
}
|
|
159
160
|
} catch (error) {
|
|
@@ -162,7 +163,6 @@ var PuzzleCaptchaManager = class extends require_captchaManager.CaptchaManager {
|
|
|
162
163
|
err: error
|
|
163
164
|
}));
|
|
164
165
|
}
|
|
165
|
-
else await this.db.updatePuzzleCaptchaRecord(challenge, { puzzleEvents });
|
|
166
166
|
if (clientMetaData?.hp) await this.db.updatePuzzleCaptchaRecord(challenge, { clientMetaData: { hp: clientMetaData.hp } });
|
|
167
167
|
await this.db.updatePuzzleCaptchaRecordResult(challenge, result, false, true, userTimestampSignature, coords);
|
|
168
168
|
if (challengeRecord.sessionId) {
|
|
@@ -260,7 +260,8 @@ var PuzzleCaptchaManager = class extends require_captchaManager.CaptchaManager {
|
|
|
260
260
|
if (challengeRecord.sessionId) await this.updateSessionRecordWithCache(challengeRecord.sessionId, {
|
|
261
261
|
serverChecked: true,
|
|
262
262
|
result: blockedResult,
|
|
263
|
-
...isBlocked && { blocked: true }
|
|
263
|
+
...isBlocked && { blocked: true },
|
|
264
|
+
matchedRule: (0, _prosopo_user_access_policy.describeMatchedRule)(blockPolicy)
|
|
264
265
|
});
|
|
265
266
|
return notVerifiedResponse;
|
|
266
267
|
}
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
let _prosopo_types = require("@prosopo/types");
|
|
2
|
-
|
|
2
|
+
var CATEGORY_TO_REASON = {
|
|
3
|
+
vpn: _prosopo_types.ResultReason.VPN_BLOCKED,
|
|
4
|
+
proxy: _prosopo_types.ResultReason.PROXY_BLOCKED,
|
|
5
|
+
tor: _prosopo_types.ResultReason.TOR_BLOCKED,
|
|
6
|
+
abuser: _prosopo_types.ResultReason.ABUSER_BLOCKED,
|
|
7
|
+
datacenter: _prosopo_types.ResultReason.DATACENTER_BLOCKED,
|
|
8
|
+
mobile: _prosopo_types.ResultReason.MOBILE_BLOCKED,
|
|
9
|
+
satellite: _prosopo_types.ResultReason.SATELLITE_BLOCKED,
|
|
10
|
+
crawler: _prosopo_types.ResultReason.CRAWLER_BLOCKED
|
|
11
|
+
};
|
|
12
|
+
var categoryToReason = (category) => CATEGORY_TO_REASON[category];
|
|
13
|
+
var isActive = (policy) => policy !== void 0;
|
|
3
14
|
var matchesDatacenterNameList = (ipInfo, list) => {
|
|
4
15
|
if (!list || list.length === 0) return false;
|
|
5
16
|
const candidates = [];
|
|
@@ -18,65 +29,43 @@ var matchesDatacenterNameList = (ipInfo, list) => {
|
|
|
18
29
|
};
|
|
19
30
|
var isDatacenterAllowlisted = (ipInfo, allowlist) => matchesDatacenterNameList(ipInfo, allowlist);
|
|
20
31
|
var isDatacenterDenylisted = (ipInfo, denylist) => matchesDatacenterNameList(ipInfo, denylist);
|
|
32
|
+
var push = (matches, category, policy) => {
|
|
33
|
+
if (policy) matches.push({
|
|
34
|
+
category,
|
|
35
|
+
policy
|
|
36
|
+
});
|
|
37
|
+
};
|
|
21
38
|
var evaluateIpInfo = (ipInfo, trafficFilter, isDnsExtra = false) => {
|
|
22
|
-
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (trafficFilter.
|
|
28
|
-
isBlocked: true,
|
|
29
|
-
reason: _prosopo_types.ResultReason.PROXY_BLOCKED
|
|
30
|
-
};
|
|
31
|
-
if (trafficFilter.blockTor && ipInfo.isTor) return {
|
|
32
|
-
isBlocked: true,
|
|
33
|
-
reason: _prosopo_types.ResultReason.TOR_BLOCKED
|
|
34
|
-
};
|
|
35
|
-
if ((trafficFilter.blockAbuser ?? true) && ipInfo.isAbuser) {
|
|
39
|
+
const matches = [];
|
|
40
|
+
if (!ipInfo || !ipInfo.isValid) return matches;
|
|
41
|
+
if (isActive(trafficFilter.vpn) && ipInfo.isVPN) push(matches, "vpn", trafficFilter.vpn);
|
|
42
|
+
if (isActive(trafficFilter.proxy) && ipInfo.isProxy) push(matches, "proxy", trafficFilter.proxy);
|
|
43
|
+
if (isActive(trafficFilter.tor) && ipInfo.isTor) push(matches, "tor", trafficFilter.tor);
|
|
44
|
+
if (isActive(trafficFilter.abuser) && ipInfo.isAbuser) {
|
|
36
45
|
const threshold = trafficFilter.abuserScoreThreshold ?? _prosopo_types.trafficFilterAbuserScoreThresholdDefault;
|
|
37
|
-
if (Math.max(ipInfo.abuserScore ?? 0, ipInfo.companyAbuserScore ?? 0) >= threshold)
|
|
38
|
-
isBlocked: true,
|
|
39
|
-
reason: _prosopo_types.ResultReason.ABUSER_BLOCKED
|
|
40
|
-
};
|
|
46
|
+
if (Math.max(ipInfo.abuserScore ?? 0, ipInfo.companyAbuserScore ?? 0) >= threshold) push(matches, "abuser", trafficFilter.abuser);
|
|
41
47
|
}
|
|
42
|
-
const datacenterSuppressedByCategory = ipInfo.isVPN && !trafficFilter.
|
|
43
|
-
if (trafficFilter.
|
|
44
|
-
if (isDatacenterDenylisted(ipInfo, trafficFilter.datacenterNameDenylist))
|
|
45
|
-
|
|
46
|
-
reason: _prosopo_types.ResultReason.DATACENTER_BLOCKED
|
|
47
|
-
};
|
|
48
|
-
if (ipInfo.providerType !== "isp" && !datacenterSuppressedByCategory && !isDatacenterAllowlisted(ipInfo, trafficFilter.datacenterNameAllowlist)) return {
|
|
49
|
-
isBlocked: true,
|
|
50
|
-
reason: _prosopo_types.ResultReason.DATACENTER_BLOCKED
|
|
51
|
-
};
|
|
48
|
+
const datacenterSuppressedByCategory = ipInfo.isVPN && !isActive(trafficFilter.vpn) || ipInfo.isProxy && !isActive(trafficFilter.proxy) || ipInfo.isTor && !isActive(trafficFilter.tor) || ipInfo.isCrawler && !isActive(trafficFilter.crawler);
|
|
49
|
+
if (isActive(trafficFilter.datacenter) && ipInfo.isDatacenter) {
|
|
50
|
+
if (isDatacenterDenylisted(ipInfo, trafficFilter.datacenterNameDenylist)) push(matches, "datacenter", trafficFilter.datacenter);
|
|
51
|
+
else if (ipInfo.providerType !== "isp" && !datacenterSuppressedByCategory && !isDatacenterAllowlisted(ipInfo, trafficFilter.datacenterNameAllowlist)) push(matches, "datacenter", trafficFilter.datacenter);
|
|
52
52
|
}
|
|
53
|
-
if (trafficFilter.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (trafficFilter.blockSatellite && ipInfo.isSatellite) return {
|
|
58
|
-
isBlocked: true,
|
|
59
|
-
reason: _prosopo_types.ResultReason.SATELLITE_BLOCKED
|
|
60
|
-
};
|
|
61
|
-
if (!isDnsExtra && trafficFilter.blockCrawler && ipInfo.isCrawler) return {
|
|
62
|
-
isBlocked: true,
|
|
63
|
-
reason: _prosopo_types.ResultReason.CRAWLER_BLOCKED
|
|
64
|
-
};
|
|
65
|
-
return { isBlocked: false };
|
|
53
|
+
if (isActive(trafficFilter.mobile) && ipInfo.isMobile) push(matches, "mobile", trafficFilter.mobile);
|
|
54
|
+
if (isActive(trafficFilter.satellite) && ipInfo.isSatellite) push(matches, "satellite", trafficFilter.satellite);
|
|
55
|
+
if (!isDnsExtra && isActive(trafficFilter.crawler) && ipInfo.isCrawler) push(matches, "crawler", trafficFilter.crawler);
|
|
56
|
+
return matches;
|
|
66
57
|
};
|
|
67
58
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* `
|
|
71
|
-
*
|
|
72
|
-
* `ipInfo` falls back to "not blocked" so an outage in the upstream
|
|
73
|
-
* service can't block all traffic.
|
|
59
|
+
* Evaluate trafficFilter against the request's IPs and return every category
|
|
60
|
+
* that matched (with the operator's policy for that category). Each match
|
|
61
|
+
* carries the policy `action` — `block` fires at submit time; `challenge`
|
|
62
|
+
* fires at request time to override captcha type + params.
|
|
74
63
|
*
|
|
75
|
-
* Cross-
|
|
76
|
-
* carries a more specific category the operator has
|
|
77
|
-
*
|
|
78
|
-
* infrastructure, so "
|
|
79
|
-
*
|
|
64
|
+
* Cross-category rule: the datacenter category is suppressed when the IP
|
|
65
|
+
* carries a more specific category the operator has not configured — VPN,
|
|
66
|
+
* proxy, Tor, or crawler. All four legitimately sit on datacenter
|
|
67
|
+
* infrastructure, so "datacenter" (a scraping rule) shouldn't catch them
|
|
68
|
+
* out the back door.
|
|
80
69
|
*
|
|
81
70
|
* Extras-only rule: the crawler check is skipped on DNS extras. Public
|
|
82
71
|
* DNS resolvers share IP space with search crawlers.
|
|
@@ -84,40 +73,77 @@ var evaluateIpInfo = (ipInfo, trafficFilter, isDnsExtra = false) => {
|
|
|
84
73
|
* The datacenter rule also honours `datacenterNameAllowlist`: consumer
|
|
85
74
|
* relays route through datacenter ranges and are reported as
|
|
86
75
|
* `is_datacenter=true` by upstream but the exiting users are real humans.
|
|
87
|
-
* Operators can list the datacenter, provider, or ASN organisation names
|
|
88
|
-
* they want to allow through — see `isDatacenterAllowlisted`.
|
|
89
76
|
*
|
|
90
77
|
* The datacenter rule also short-circuits when upstream classifies the
|
|
91
|
-
* provider as an ISP (`providerType === "isp"`). Consumer ISPs
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
* hosting services — but the eyeball ranges behind those ASNs carry
|
|
95
|
-
* ordinary end-users. The ISP categorisation is stronger evidence of
|
|
96
|
-
* consumer traffic than the datacenter boolean.
|
|
78
|
+
* provider as an ISP (`providerType === "isp"`). Consumer ISPs are
|
|
79
|
+
* sometimes flagged `is_datacenter=true` upstream, but the ranges behind
|
|
80
|
+
* those ASNs carry ordinary end-users.
|
|
97
81
|
*
|
|
98
82
|
* The ISP short-circuit and the allowlist are both overridden by
|
|
99
|
-
* `datacenterNameDenylist
|
|
100
|
-
* or `asnOrganization` matches a denylist entry, the block fires. This
|
|
101
|
-
* lets operators opt named providers (for example IP-leasing platforms
|
|
102
|
-
* whose ranges are announced from carrier ASNs but exit as proxy pools)
|
|
103
|
-
* back into the datacenter rule.
|
|
83
|
+
* `datacenterNameDenylist`.
|
|
104
84
|
*
|
|
105
85
|
* When `trafficFilter.skipExtrasOnValidDnsPath` is on and the catcher
|
|
106
86
|
* confirmed the DNS path matched the connection path (`pathValid: true`),
|
|
107
|
-
* the extras evaluation is skipped.
|
|
108
|
-
*
|
|
87
|
+
* the extras evaluation is skipped.
|
|
88
|
+
*
|
|
89
|
+
* The `isBlocked` verdict is set when at least one match has
|
|
90
|
+
* `action === "block"`, with `reason` naming that category. Callers that
|
|
91
|
+
* only care about challenges can inspect `matches` directly and use
|
|
92
|
+
* `resolveChallengePolicy` to combine them.
|
|
109
93
|
*/
|
|
110
94
|
var checkTrafficFilter = (ipInfo, trafficFilter, extraIpInfos, dnsPathValid) => {
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
if (trafficFilter.skipExtrasOnValidDnsPath && dnsPathValid === true)
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
95
|
+
const matches = [];
|
|
96
|
+
matches.push(...evaluateIpInfo(ipInfo, trafficFilter));
|
|
97
|
+
if (!(trafficFilter.skipExtrasOnValidDnsPath && dnsPathValid === true)) for (const extra of extraIpInfos ?? []) matches.push(...evaluateIpInfo(extra, trafficFilter, true));
|
|
98
|
+
const firstBlock = matches.find((m) => m.policy.action === _prosopo_types.TrafficFilterAction.Block);
|
|
99
|
+
if (firstBlock) return {
|
|
100
|
+
isBlocked: true,
|
|
101
|
+
reason: categoryToReason(firstBlock.category),
|
|
102
|
+
matches
|
|
103
|
+
};
|
|
104
|
+
return {
|
|
105
|
+
isBlocked: false,
|
|
106
|
+
matches
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
var CAPTCHA_TYPE_RANK = {
|
|
110
|
+
[_prosopo_types.CaptchaType.image]: 4,
|
|
111
|
+
[_prosopo_types.CaptchaType.puzzle]: 3,
|
|
112
|
+
[_prosopo_types.CaptchaType.pow]: 2,
|
|
113
|
+
[_prosopo_types.CaptchaType.frictionless]: 1
|
|
114
|
+
};
|
|
115
|
+
var rankCaptchaType = (t) => t === void 0 ? 0 : CAPTCHA_TYPE_RANK[t] ?? 0;
|
|
116
|
+
/**
|
|
117
|
+
* Combine the `challenge` matches into a single effective policy: pick the
|
|
118
|
+
* strictest captcha type, then take the hardest params (max difficulty,
|
|
119
|
+
* max image count, min puzzle tolerance). `block` matches are ignored —
|
|
120
|
+
* callers should short-circuit on the top-level `isBlocked` verdict
|
|
121
|
+
* first. Returns undefined when there are no challenge matches.
|
|
122
|
+
*/
|
|
123
|
+
var resolveChallengePolicy = (matches) => {
|
|
124
|
+
const challenges = matches.filter((m) => m.policy.action === _prosopo_types.TrafficFilterAction.Challenge);
|
|
125
|
+
if (challenges.length === 0) return void 0;
|
|
126
|
+
let winningCaptchaType;
|
|
127
|
+
for (const m of challenges) if (rankCaptchaType(m.policy.captchaType) > rankCaptchaType(winningCaptchaType)) winningCaptchaType = m.policy.captchaType;
|
|
128
|
+
let powDifficulty;
|
|
129
|
+
let solvedImagesCount;
|
|
130
|
+
let puzzleTolerance;
|
|
131
|
+
for (const m of challenges) {
|
|
132
|
+
if (m.policy.powDifficulty !== void 0) powDifficulty = powDifficulty === void 0 ? m.policy.powDifficulty : Math.max(powDifficulty, m.policy.powDifficulty);
|
|
133
|
+
if (m.policy.solvedImagesCount !== void 0) solvedImagesCount = solvedImagesCount === void 0 ? m.policy.solvedImagesCount : Math.max(solvedImagesCount, m.policy.solvedImagesCount);
|
|
134
|
+
if (m.policy.puzzleTolerance !== void 0) puzzleTolerance = puzzleTolerance === void 0 ? m.policy.puzzleTolerance : Math.min(puzzleTolerance, m.policy.puzzleTolerance);
|
|
117
135
|
}
|
|
118
|
-
return {
|
|
136
|
+
return {
|
|
137
|
+
captchaType: winningCaptchaType,
|
|
138
|
+
powDifficulty,
|
|
139
|
+
solvedImagesCount,
|
|
140
|
+
puzzleTolerance,
|
|
141
|
+
sourceCategories: challenges.map((m) => m.category)
|
|
142
|
+
};
|
|
119
143
|
};
|
|
120
144
|
//#endregion
|
|
145
|
+
exports.categoryToReason = categoryToReason;
|
|
121
146
|
exports.checkTrafficFilter = checkTrafficFilter;
|
|
122
147
|
exports.isDatacenterAllowlisted = isDatacenterAllowlisted;
|
|
123
148
|
exports.isDatacenterDenylisted = isDatacenterDenylisted;
|
|
149
|
+
exports.resolveChallengePolicy = resolveChallengePolicy;
|
|
@@ -4,7 +4,7 @@ import { type Logger } from "@prosopo/logger";
|
|
|
4
4
|
import { type CaptchaType, type EnrichedDnsEvent, type IPInfoResponse, type ITrafficFilter, type KeyringPair, type ProsopoConfigOutput, type RequestHeaders, type Session, type SimdReadingsStage, Tier, type UserCommitment } from "@prosopo/types";
|
|
5
5
|
import type { ClientRecord, IProviderDatabase, IUserDataSlim, PoWCaptchaRecord, PuzzleCaptchaRecord } from "@prosopo/types-database";
|
|
6
6
|
import type { ProviderEnvironment } from "@prosopo/types-env";
|
|
7
|
-
import { type AccessPolicy, type AccessRulesStorage, type UserScope, type UserScopeRecord } from "@prosopo/user-access-policy";
|
|
7
|
+
import { type AccessPolicy, type AccessRule, type AccessRulesStorage, type UserScope, type UserScopeRecord } from "@prosopo/user-access-policy";
|
|
8
8
|
import type { BehavioralDataResult } from "./detection/decodeBehavior.js";
|
|
9
9
|
import type { SimdReadingsResult } from "./detection/decodeSimd.js";
|
|
10
10
|
import { type TrafficCheckResult } from "./spam/checkTrafficFilter.js";
|
|
@@ -48,7 +48,7 @@ export declare class CaptchaManager {
|
|
|
48
48
|
getPrioritisedAccessPolicies(userAccessRulesStorage: AccessRulesStorage, clientId: string, userScope: UserScope | UserScopeRecord, options?: {
|
|
49
49
|
blockOnly?: boolean;
|
|
50
50
|
requestMemoHost?: object;
|
|
51
|
-
}): Promise<
|
|
51
|
+
}): Promise<AccessRule[]>;
|
|
52
52
|
resolveBundleById(bundleId?: string | null): PoolBundleDecrypt | undefined;
|
|
53
53
|
resolveBundleByDetectorSession(detectorSessionId?: string): Promise<(PoolBundleDecrypt & {
|
|
54
54
|
bundleId: string;
|
|
@@ -56,7 +56,7 @@ export declare class CaptchaManager {
|
|
|
56
56
|
resolveBundleBySessionId(sessionId?: string): Promise<PoolBundleDecrypt | undefined>;
|
|
57
57
|
decryptSimdReadings(encryptedData: string, bundle: PoolBundleDecrypt | undefined): Promise<SimdReadingsResult | null>;
|
|
58
58
|
decryptBehavioralData(encryptedData: string, bundle: PoolBundleDecrypt | undefined): Promise<BehavioralDataResult | null>;
|
|
59
|
-
checkForHardBlock(userAccessRulesStorage: AccessRulesStorage, challengeRecord: PoWCaptchaRecord | PuzzleCaptchaRecord | UserCommitment, userAccount: string, headers: RequestHeaders, coords?: [number, number][][], countryCode?: string, asn?: number): Promise<
|
|
59
|
+
checkForHardBlock(userAccessRulesStorage: AccessRulesStorage, challengeRecord: PoWCaptchaRecord | PuzzleCaptchaRecord | UserCommitment, userAccount: string, headers: RequestHeaders, coords?: [number, number][][], countryCode?: string, asn?: number): Promise<AccessRule | undefined>;
|
|
60
60
|
checkSpamEmail(email: string): Promise<boolean>;
|
|
61
61
|
resolveTrafficFilterCheck(env: ProviderEnvironment, recordIpInfo: IPInfoResponse | undefined, trafficFilter: Partial<ITrafficFilter> | undefined, currentIp?: string, enrichedDnsEvent?: EnrichedDnsEvent): Promise<TrafficCheckResult>;
|
|
62
62
|
static canClientSeeScore(tier: Tier, score?: number): boolean | 0 | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"captchaManager.d.ts","sourceRoot":"","sources":["../../src/tasks/captchaManager.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,KAAK,MAAM,EAAa,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAEN,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EAEnB,KAAK,OAAO,EACZ,KAAK,iBAAiB,EACtB,IAAI,
|
|
1
|
+
{"version":3,"file":"captchaManager.d.ts","sourceRoot":"","sources":["../../src/tasks/captchaManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@ import { checkTrafficFilter } from "./spam/checkTrafficFilter.js";
|
|
|
5
5
|
import { extraIpInfosFromEnrichedDnsEvent } from "./dnsEvent/enrichDnsEvent.js";
|
|
6
6
|
import { checkSpamEmail } from "./spam/checkSpamEmail.js";
|
|
7
7
|
import { getLogger } from "@prosopo/logger";
|
|
8
|
-
import { ApiParams, ResultReason, Tier } from "@prosopo/types";
|
|
8
|
+
import { ApiParams, ResultReason, Tier, TrafficFilterAction } from "@prosopo/types";
|
|
9
9
|
import { AccessPolicyType } from "@prosopo/user-access-policy";
|
|
10
10
|
//#region src/tasks/captchaManager.ts
|
|
11
11
|
/**
|
|
@@ -438,7 +438,10 @@ var CaptchaManager = class CaptchaManager {
|
|
|
438
438
|
return checkSpamEmail(email, this.db, this.config, this.logger);
|
|
439
439
|
}
|
|
440
440
|
/**
|
|
441
|
-
* Resolves the IP info to feed to `checkTrafficFilter` and runs the check
|
|
441
|
+
* Resolves the IP info to feed to `checkTrafficFilter` and runs the check
|
|
442
|
+
* at submit time. Only `action: "block"` matches produce `isBlocked:true`;
|
|
443
|
+
* `action: "challenge"` matches were already applied at request time and
|
|
444
|
+
* are ignored here.
|
|
442
445
|
*
|
|
443
446
|
* - The captcha record already carries the IPInfoResponse from request
|
|
444
447
|
* time (ipInfoMiddleware → storeXxxRecord), so by default we reuse it
|
|
@@ -448,9 +451,10 @@ var CaptchaManager = class CaptchaManager {
|
|
|
448
451
|
* and may differ from the IP that originally requested the captcha.
|
|
449
452
|
* - When the session carries a `dnsEvent`, its `peerIp` and `resolverIp`
|
|
450
453
|
* are enriched and passed alongside the primary IP.
|
|
451
|
-
* -
|
|
452
|
-
* blocked even when the site hasn't configured a
|
|
453
|
-
*
|
|
454
|
+
* - The abuser category defaults to `{action:"block"}` so abusive
|
|
455
|
+
* networks are always blocked even when the site hasn't configured a
|
|
456
|
+
* trafficFilter.
|
|
457
|
+
* - Returns `{ isBlocked: false }` if no category is active, without
|
|
454
458
|
* consulting the payload at all.
|
|
455
459
|
*
|
|
456
460
|
* Callers handle the "blocked" branch themselves (each verify path
|
|
@@ -459,10 +463,13 @@ var CaptchaManager = class CaptchaManager {
|
|
|
459
463
|
*/
|
|
460
464
|
async resolveTrafficFilterCheck(env, recordIpInfo, trafficFilter, currentIp, enrichedDnsEvent) {
|
|
461
465
|
const effective = {
|
|
462
|
-
|
|
466
|
+
abuser: { action: TrafficFilterAction.Block },
|
|
463
467
|
...trafficFilter
|
|
464
468
|
};
|
|
465
|
-
if (!
|
|
469
|
+
if (!(effective.vpn !== void 0 || effective.proxy !== void 0 || effective.tor !== void 0 || effective.abuser !== void 0 || effective.datacenter !== void 0 || effective.mobile !== void 0 || effective.satellite !== void 0 || effective.crawler !== void 0)) return {
|
|
470
|
+
isBlocked: false,
|
|
471
|
+
matches: []
|
|
472
|
+
};
|
|
466
473
|
return checkTrafficFilter(currentIp ? await env.ipInfoService.lookup(currentIp) : recordIpInfo, effective, extraIpInfosFromEnrichedDnsEvent(enrichedDnsEvent), enrichedDnsEvent?.pathValid);
|
|
467
474
|
}
|
|
468
475
|
static canClientSeeScore(tier, score) {
|