@prosopo/provider 3.12.3 → 3.13.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/CHANGELOG.md +214 -0
- package/dist/api/admin/apiAdminRoutesProvider.js +13 -18
- package/dist/api/admin/apiToggleMaintenanceModeEndpoint.js +40 -0
- package/dist/api/blacklistRequestInspector.js +4 -4
- package/dist/api/captcha/getFrictionlessCaptchaChallenge.js +338 -0
- package/dist/api/captcha/getImageCaptchaChallenge.js +150 -0
- package/dist/api/captcha/getPoWCaptchaChallenge.js +156 -0
- package/dist/api/captcha/submitImageCaptchaSolution.js +87 -0
- package/dist/api/captcha/submitPoWCaptchaSolution.js +77 -0
- package/dist/api/captcha.js +18 -606
- package/dist/api/verify.js +24 -1
- package/dist/cjs/api/admin/apiAdminRoutesProvider.cjs +13 -18
- package/dist/cjs/api/admin/apiRegisterSiteKeyEndpoint.cjs +2 -1
- package/dist/cjs/api/admin/apiRemoveDetectorKeyEndpoint.cjs +3 -2
- package/dist/cjs/api/admin/apiToggleMaintenanceModeEndpoint.cjs +41 -0
- package/dist/cjs/api/blacklistRequestInspector.cjs +3 -3
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge.cjs +337 -0
- package/dist/cjs/api/captcha/getImageCaptchaChallenge.cjs +149 -0
- package/dist/cjs/api/captcha/getPoWCaptchaChallenge.cjs +155 -0
- package/dist/cjs/api/captcha/submitImageCaptchaSolution.cjs +86 -0
- package/dist/cjs/api/captcha/submitPoWCaptchaSolution.cjs +76 -0
- package/dist/cjs/api/captcha.cjs +17 -605
- package/dist/cjs/api/ja4Middleware.cjs +2 -1
- package/dist/cjs/api/verify.cjs +24 -1
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/schedulers/setClientEntropy.cjs +36 -0
- package/dist/cjs/tasks/captchaManager.cjs +7 -22
- package/dist/cjs/tasks/client/clientTasks.cjs +18 -36
- package/dist/cjs/tasks/detection/decodePayload.cjs +385 -714
- package/dist/cjs/tasks/detection/getBotScore.cjs +15 -2
- package/dist/cjs/tasks/frictionless/frictionlessTasks.cjs +136 -30
- package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasks.cjs +25 -13
- package/dist/cjs/tasks/powCaptcha/powTasks.cjs +8 -8
- package/dist/cjs/tasks/tasks.cjs +1 -0
- package/dist/cjs/util.cjs +14 -1
- package/dist/cjs/utils/hashUserIp.cjs +9 -0
- package/dist/index.js +2 -0
- package/dist/schedulers/setClientEntropy.js +36 -0
- package/dist/tasks/captchaManager.js +5 -21
- package/dist/tasks/client/clientTasks.js +19 -37
- package/dist/tasks/detection/decodePayload.js +385 -714
- package/dist/tasks/detection/getBotScore.js +17 -4
- package/dist/tasks/frictionless/frictionlessTasks.js +137 -31
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.js +25 -13
- package/dist/tasks/powCaptcha/powTasks.js +8 -8
- package/dist/tasks/tasks.js +1 -0
- package/dist/util.js +14 -1
- package/dist/utils/hashUserIp.js +9 -0
- package/package.json +24 -25
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fYWyhW from "./decodePayload.js";
|
|
2
2
|
const DEFAULT_ENTROPY = 13837;
|
|
3
|
-
const getBotScore = async (payload, privateKeyString) => {
|
|
4
|
-
const result = await
|
|
3
|
+
const getBotScore = async (payload, headHash, privateKeyString) => {
|
|
4
|
+
const result = await fYWyhW(
|
|
5
5
|
payload,
|
|
6
|
+
headHash,
|
|
6
7
|
privateKeyString
|
|
7
8
|
);
|
|
8
9
|
const baseBotScore = result.score;
|
|
@@ -10,6 +11,9 @@ const getBotScore = async (payload, privateKeyString) => {
|
|
|
10
11
|
const providerSelectEntropy = result.providerSelectEntropy;
|
|
11
12
|
const userId = result.userId;
|
|
12
13
|
const userAgent = result.userAgent;
|
|
14
|
+
const isWebView = result.isWebView ?? false;
|
|
15
|
+
const isIframe = result.isIframe ?? false;
|
|
16
|
+
const decryptedHeadHash = result.decryptedHeadHash;
|
|
13
17
|
if (baseBotScore === void 0) {
|
|
14
18
|
return {
|
|
15
19
|
baseBotScore: 1,
|
|
@@ -17,7 +21,16 @@ const getBotScore = async (payload, privateKeyString) => {
|
|
|
17
21
|
providerSelectEntropy: DEFAULT_ENTROPY
|
|
18
22
|
};
|
|
19
23
|
}
|
|
20
|
-
return {
|
|
24
|
+
return {
|
|
25
|
+
baseBotScore,
|
|
26
|
+
timestamp,
|
|
27
|
+
providerSelectEntropy,
|
|
28
|
+
userId,
|
|
29
|
+
userAgent,
|
|
30
|
+
isWebView,
|
|
31
|
+
isIframe,
|
|
32
|
+
decryptedHeadHash
|
|
33
|
+
};
|
|
21
34
|
};
|
|
22
35
|
export {
|
|
23
36
|
getBotScore
|
|
@@ -15,22 +15,60 @@ const getDefaultEntropy = () => {
|
|
|
15
15
|
};
|
|
16
16
|
const DEFAULT_MAX_TIMESTAMP_AGE = 60 * 10 * 1e3;
|
|
17
17
|
const DEFAULT_ENTROPY = getDefaultEntropy();
|
|
18
|
+
var FrictionlessReason = /* @__PURE__ */ ((FrictionlessReason2) => {
|
|
19
|
+
FrictionlessReason2["CONTEXT_AWARE_VALIDATION_FAILED"] = "CONTEXT_AWARE_VALIDATION_FAILED";
|
|
20
|
+
FrictionlessReason2["USER_ACCESS_POLICY"] = "USER_ACCESS_POLICY";
|
|
21
|
+
FrictionlessReason2["USER_AGENT_MISMATCH"] = "USER_AGENT_MISMATCH";
|
|
22
|
+
FrictionlessReason2["OLD_TIMESTAMP"] = "OLD_TIMESTAMP";
|
|
23
|
+
FrictionlessReason2["BOT_SCORE_ABOVE_THRESHOLD"] = "BOT_SCORE_ABOVE_THRESHOLD";
|
|
24
|
+
FrictionlessReason2["WEBVIEW_DETECTED"] = "WEBVIEW_DETECTED";
|
|
25
|
+
return FrictionlessReason2;
|
|
26
|
+
})(FrictionlessReason || {});
|
|
18
27
|
class FrictionlessManager extends CaptchaManager {
|
|
19
28
|
constructor(db, pair, config, logger) {
|
|
20
|
-
super(db, pair, logger);
|
|
29
|
+
super(db, pair, config, logger);
|
|
21
30
|
this.config = config;
|
|
22
31
|
}
|
|
32
|
+
setSessionParams(params) {
|
|
33
|
+
this.sessionParams = {
|
|
34
|
+
token: params.token,
|
|
35
|
+
score: params.score,
|
|
36
|
+
threshold: params.threshold,
|
|
37
|
+
scoreComponents: params.scoreComponents,
|
|
38
|
+
providerSelectEntropy: params.providerSelectEntropy,
|
|
39
|
+
ipAddress: params.ipAddress,
|
|
40
|
+
webView: params.webView ?? false,
|
|
41
|
+
iFrame: params.iFrame ?? false,
|
|
42
|
+
decryptedHeadHash: params.decryptedHeadHash
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
updateScore(score, scoreComponents) {
|
|
46
|
+
if (this.sessionParams) {
|
|
47
|
+
this.sessionParams.score = score;
|
|
48
|
+
this.sessionParams.scoreComponents = scoreComponents;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
23
51
|
checkLangRules(acceptLanguage) {
|
|
24
52
|
return checkLangRules(this.config, acceptLanguage);
|
|
25
53
|
}
|
|
26
|
-
async createSession(
|
|
54
|
+
async createSession(token, score, threshold, scoreComponents, providerSelectEntropy, ipAddress, captchaType, solvedImagesCount, powDifficulty, userSitekeyIpHash, webView = false, iFrame = false, decryptedHeadHash = "", reason) {
|
|
27
55
|
const sessionRecord = {
|
|
28
56
|
sessionId: v4(),
|
|
29
57
|
createdAt: /* @__PURE__ */ new Date(),
|
|
30
|
-
|
|
58
|
+
token,
|
|
59
|
+
score,
|
|
60
|
+
threshold,
|
|
61
|
+
scoreComponents,
|
|
62
|
+
providerSelectEntropy,
|
|
63
|
+
ipAddress,
|
|
31
64
|
captchaType,
|
|
32
65
|
solvedImagesCount,
|
|
33
|
-
powDifficulty
|
|
66
|
+
powDifficulty,
|
|
67
|
+
userSitekeyIpHash,
|
|
68
|
+
webView,
|
|
69
|
+
iFrame,
|
|
70
|
+
decryptedHeadHash,
|
|
71
|
+
reason
|
|
34
72
|
};
|
|
35
73
|
await this.db.storeSessionRecord(sessionRecord);
|
|
36
74
|
return sessionRecord;
|
|
@@ -53,11 +91,28 @@ class FrictionlessManager extends CaptchaManager {
|
|
|
53
91
|
}
|
|
54
92
|
return { verified: true, domain };
|
|
55
93
|
}
|
|
56
|
-
async sendImageCaptcha(
|
|
94
|
+
async sendImageCaptcha(params) {
|
|
95
|
+
const effectiveParams = { ...this.sessionParams, ...params };
|
|
96
|
+
if (!effectiveParams.token || effectiveParams.score === void 0 || effectiveParams.threshold === void 0 || !effectiveParams.scoreComponents || effectiveParams.providerSelectEntropy === void 0 || !effectiveParams.ipAddress) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
"Session parameters must be set before calling sendImageCaptcha"
|
|
99
|
+
);
|
|
100
|
+
}
|
|
57
101
|
const sessionRecord = await this.createSession(
|
|
58
|
-
|
|
102
|
+
effectiveParams.token,
|
|
103
|
+
effectiveParams.score,
|
|
104
|
+
effectiveParams.threshold,
|
|
105
|
+
effectiveParams.scoreComponents,
|
|
106
|
+
effectiveParams.providerSelectEntropy,
|
|
107
|
+
effectiveParams.ipAddress,
|
|
59
108
|
CaptchaType.image,
|
|
60
|
-
solvedImagesCount
|
|
109
|
+
effectiveParams.solvedImagesCount,
|
|
110
|
+
void 0,
|
|
111
|
+
effectiveParams.userSitekeyIpHash,
|
|
112
|
+
effectiveParams.webView ?? false,
|
|
113
|
+
effectiveParams.iFrame ?? false,
|
|
114
|
+
effectiveParams.decryptedHeadHash,
|
|
115
|
+
effectiveParams.reason
|
|
61
116
|
);
|
|
62
117
|
return {
|
|
63
118
|
[ApiParams.captchaType]: CaptchaType.image,
|
|
@@ -65,11 +120,28 @@ class FrictionlessManager extends CaptchaManager {
|
|
|
65
120
|
[ApiParams.status]: "ok"
|
|
66
121
|
};
|
|
67
122
|
}
|
|
68
|
-
async sendPowCaptcha(
|
|
123
|
+
async sendPowCaptcha(params) {
|
|
124
|
+
const effectiveParams = { ...this.sessionParams, ...params };
|
|
125
|
+
if (!effectiveParams.token || effectiveParams.score === void 0 || effectiveParams.threshold === void 0 || !effectiveParams.scoreComponents || effectiveParams.providerSelectEntropy === void 0 || !effectiveParams.ipAddress) {
|
|
126
|
+
throw new Error(
|
|
127
|
+
"Session parameters must be set before calling sendPowCaptcha"
|
|
128
|
+
);
|
|
129
|
+
}
|
|
69
130
|
const sessionRecord = await this.createSession(
|
|
70
|
-
|
|
131
|
+
effectiveParams.token,
|
|
132
|
+
effectiveParams.score,
|
|
133
|
+
effectiveParams.threshold,
|
|
134
|
+
effectiveParams.scoreComponents,
|
|
135
|
+
effectiveParams.providerSelectEntropy,
|
|
136
|
+
effectiveParams.ipAddress,
|
|
71
137
|
CaptchaType.pow,
|
|
72
|
-
|
|
138
|
+
void 0,
|
|
139
|
+
effectiveParams.powDifficulty,
|
|
140
|
+
effectiveParams.userSitekeyIpHash,
|
|
141
|
+
effectiveParams.webView ?? false,
|
|
142
|
+
effectiveParams.iFrame ?? false,
|
|
143
|
+
effectiveParams.decryptedHeadHash,
|
|
144
|
+
effectiveParams.reason
|
|
73
145
|
);
|
|
74
146
|
return {
|
|
75
147
|
[ApiParams.captchaType]: CaptchaType.pow,
|
|
@@ -77,47 +149,57 @@ class FrictionlessManager extends CaptchaManager {
|
|
|
77
149
|
[ApiParams.status]: "ok"
|
|
78
150
|
};
|
|
79
151
|
}
|
|
80
|
-
|
|
152
|
+
scoreIncreaseAccessPolicy(accessPolicy, baseBotScore, botScore, scoreComponents) {
|
|
81
153
|
const accessPolicyPenalty = accessPolicy?.frictionlessScore || this.config.penalties.PENALTY_ACCESS_RULE;
|
|
82
154
|
botScore += accessPolicyPenalty;
|
|
83
|
-
|
|
155
|
+
return {
|
|
84
156
|
score: botScore,
|
|
85
157
|
scoreComponents: {
|
|
86
|
-
|
|
158
|
+
...scoreComponents,
|
|
87
159
|
accessPolicy: accessPolicyPenalty
|
|
88
160
|
}
|
|
89
|
-
}
|
|
90
|
-
return botScore;
|
|
161
|
+
};
|
|
91
162
|
}
|
|
92
|
-
|
|
163
|
+
scoreIncreaseUnverifiedHost(host, baseBotScore, botScore, scoreComponents) {
|
|
93
164
|
this.logger.info(() => ({
|
|
94
165
|
msg: "Host not verified",
|
|
95
166
|
data: { requested: this.config.host, selected: host }
|
|
96
167
|
}));
|
|
97
168
|
botScore += this.config.penalties.PENALTY_UNVERIFIED_HOST;
|
|
98
|
-
|
|
169
|
+
return {
|
|
99
170
|
score: botScore,
|
|
100
171
|
scoreComponents: {
|
|
101
|
-
|
|
172
|
+
...scoreComponents,
|
|
102
173
|
unverifiedHost: this.config.penalties.PENALTY_UNVERIFIED_HOST
|
|
103
174
|
}
|
|
104
|
-
}
|
|
105
|
-
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
scoreIncreaseWebView(baseBotScore, botScore, scoreComponents) {
|
|
178
|
+
this.logger.debug(() => ({
|
|
179
|
+
msg: "WebView detected"
|
|
180
|
+
}));
|
|
181
|
+
botScore += this.config.penalties.PENALTY_WEBVIEW;
|
|
182
|
+
return {
|
|
183
|
+
score: botScore,
|
|
184
|
+
scoreComponents: {
|
|
185
|
+
...scoreComponents,
|
|
186
|
+
webView: this.config.penalties.PENALTY_WEBVIEW
|
|
187
|
+
}
|
|
188
|
+
};
|
|
106
189
|
}
|
|
107
|
-
|
|
190
|
+
scoreIncreaseTimestamp(timestamp, baseBotScore, botScore, scoreComponents) {
|
|
108
191
|
this.logger.info(() => ({
|
|
109
192
|
msg: "Timestamp is older than 10 minutes",
|
|
110
193
|
data: { timestamp: new Date(timestamp) }
|
|
111
194
|
}));
|
|
112
195
|
botScore += this.config.penalties.PENALTY_OLD_TIMESTAMP;
|
|
113
|
-
|
|
196
|
+
return {
|
|
114
197
|
score: botScore,
|
|
115
198
|
scoreComponents: {
|
|
116
|
-
|
|
199
|
+
...scoreComponents,
|
|
117
200
|
timeout: this.config.penalties.PENALTY_OLD_TIMESTAMP
|
|
118
201
|
}
|
|
119
|
-
}
|
|
120
|
-
return botScore;
|
|
202
|
+
};
|
|
121
203
|
}
|
|
122
204
|
static timestampTooOld(timestamp) {
|
|
123
205
|
const now = Date.now();
|
|
@@ -139,7 +221,7 @@ class FrictionlessManager extends CaptchaManager {
|
|
|
139
221
|
const end = key.slice(-5);
|
|
140
222
|
return `${start}...${middle}...${end}`;
|
|
141
223
|
}
|
|
142
|
-
async decryptPayload(token) {
|
|
224
|
+
async decryptPayload(token, headHash) {
|
|
143
225
|
const decryptKeys = [
|
|
144
226
|
// Process DB keys first, then env var key last as env key will likely be invalid
|
|
145
227
|
...await this.getDetectorKeys(),
|
|
@@ -162,6 +244,9 @@ class FrictionlessManager extends CaptchaManager {
|
|
|
162
244
|
let providerSelectEntropy;
|
|
163
245
|
let userId;
|
|
164
246
|
let userAgent;
|
|
247
|
+
let webView;
|
|
248
|
+
let iFrame;
|
|
249
|
+
let decryptedHeadHash = "";
|
|
165
250
|
for (const [keyIndex, key] of decryptKeys.entries()) {
|
|
166
251
|
try {
|
|
167
252
|
this.logger.info(() => ({
|
|
@@ -170,12 +255,15 @@ class FrictionlessManager extends CaptchaManager {
|
|
|
170
255
|
key: this.redactKeyForLogging(key)
|
|
171
256
|
}
|
|
172
257
|
}));
|
|
173
|
-
const decrypted = await getBotScore(token, key);
|
|
258
|
+
const decrypted = await getBotScore(token, headHash, key);
|
|
259
|
+
decryptedHeadHash = decrypted.decryptedHeadHash || "";
|
|
174
260
|
const s = decrypted.baseBotScore;
|
|
175
261
|
const t = decrypted.timestamp;
|
|
176
262
|
const p = decrypted.providerSelectEntropy;
|
|
177
263
|
const a = decrypted.userId;
|
|
178
264
|
const u = decrypted.userAgent;
|
|
265
|
+
const w = decrypted.isWebView;
|
|
266
|
+
const i = decrypted.isIframe;
|
|
179
267
|
this.logger.debug(() => ({
|
|
180
268
|
msg: "Successfully decrypted score",
|
|
181
269
|
data: {
|
|
@@ -184,7 +272,9 @@ class FrictionlessManager extends CaptchaManager {
|
|
|
184
272
|
timestamp: t,
|
|
185
273
|
entropy: p,
|
|
186
274
|
userId: a,
|
|
187
|
-
userAgent: u
|
|
275
|
+
userAgent: u,
|
|
276
|
+
webView: w,
|
|
277
|
+
iFrame: i
|
|
188
278
|
}
|
|
189
279
|
}));
|
|
190
280
|
baseBotScore = s;
|
|
@@ -192,6 +282,8 @@ class FrictionlessManager extends CaptchaManager {
|
|
|
192
282
|
providerSelectEntropy = p;
|
|
193
283
|
userId = a;
|
|
194
284
|
userAgent = u;
|
|
285
|
+
webView = w;
|
|
286
|
+
iFrame = i;
|
|
195
287
|
break;
|
|
196
288
|
} catch (err) {
|
|
197
289
|
if (keyIndex === decryptKeys.length - 1) {
|
|
@@ -201,6 +293,7 @@ class FrictionlessManager extends CaptchaManager {
|
|
|
201
293
|
baseBotScore = 1;
|
|
202
294
|
timestamp = 0;
|
|
203
295
|
providerSelectEntropy = DEFAULT_ENTROPY + 1;
|
|
296
|
+
decryptedHeadHash = "";
|
|
204
297
|
}
|
|
205
298
|
}
|
|
206
299
|
}
|
|
@@ -215,13 +308,19 @@ class FrictionlessManager extends CaptchaManager {
|
|
|
215
308
|
baseBotScore = 1;
|
|
216
309
|
timestamp = 0;
|
|
217
310
|
providerSelectEntropy = DEFAULT_ENTROPY - undefinedCount;
|
|
311
|
+
decryptedHeadHash = "";
|
|
218
312
|
}
|
|
219
313
|
this.logger.info(() => ({
|
|
220
314
|
msg: "decryptPayload result",
|
|
221
315
|
data: {
|
|
222
316
|
baseBotScore,
|
|
223
317
|
timestamp,
|
|
224
|
-
entropy: providerSelectEntropy
|
|
318
|
+
entropy: providerSelectEntropy,
|
|
319
|
+
userId,
|
|
320
|
+
userAgent,
|
|
321
|
+
webView,
|
|
322
|
+
iFrame,
|
|
323
|
+
decryptedHeadHash
|
|
225
324
|
}
|
|
226
325
|
}));
|
|
227
326
|
return {
|
|
@@ -229,11 +328,18 @@ class FrictionlessManager extends CaptchaManager {
|
|
|
229
328
|
timestamp: Number(timestamp),
|
|
230
329
|
providerSelectEntropy: Number(providerSelectEntropy),
|
|
231
330
|
userId,
|
|
232
|
-
userAgent
|
|
331
|
+
userAgent,
|
|
332
|
+
webView: webView || false,
|
|
333
|
+
iFrame: iFrame || false,
|
|
334
|
+
decryptedHeadHash
|
|
233
335
|
};
|
|
234
336
|
}
|
|
337
|
+
async getClientEntropy(siteKey) {
|
|
338
|
+
return this.db.getClientEntropy(siteKey);
|
|
339
|
+
}
|
|
235
340
|
}
|
|
236
341
|
export {
|
|
237
342
|
DEFAULT_ENTROPY,
|
|
238
|
-
FrictionlessManager
|
|
343
|
+
FrictionlessManager,
|
|
344
|
+
FrictionlessReason
|
|
239
345
|
};
|
|
@@ -9,11 +9,12 @@ import { constructPairList, containsIdenticalPairs } from "../../pairs.js";
|
|
|
9
9
|
import { checkLangRules } from "../../rules/lang.js";
|
|
10
10
|
import { shuffleArray, deepValidateIpAddress } from "../../util.js";
|
|
11
11
|
import { CaptchaManager } from "../captchaManager.js";
|
|
12
|
+
import { FrictionlessReason } from "../frictionless/frictionlessTasks.js";
|
|
12
13
|
import { computeFrictionlessScore } from "../frictionless/frictionlessTasksUtils.js";
|
|
13
14
|
import { buildTreeAndGetCommitmentId } from "./imgCaptchaTasksUtils.js";
|
|
14
15
|
class ImgCaptchaManager extends CaptchaManager {
|
|
15
16
|
constructor(db, pair, config, logger) {
|
|
16
|
-
super(db, pair, logger);
|
|
17
|
+
super(db, pair, config, logger);
|
|
17
18
|
this.config = config;
|
|
18
19
|
}
|
|
19
20
|
async getCaptchaWithProof(datasetId, solved, size) {
|
|
@@ -30,7 +31,7 @@ class ImgCaptchaManager extends CaptchaManager {
|
|
|
30
31
|
}
|
|
31
32
|
return captchaDocs;
|
|
32
33
|
}
|
|
33
|
-
async getRandomCaptchasAndRequestHash(datasetId, userAccount, ipAddress, captchaConfig, threshold,
|
|
34
|
+
async getRandomCaptchasAndRequestHash(datasetId, userAccount, ipAddress, captchaConfig, threshold, sessionId) {
|
|
34
35
|
const dataset = await this.db.getDatasetDetails(datasetId);
|
|
35
36
|
if (!dataset) {
|
|
36
37
|
throw new ProsopoEnvError("DATABASE.DATASET_GET_FAILED", {
|
|
@@ -80,7 +81,7 @@ class ImgCaptchaManager extends CaptchaManager {
|
|
|
80
81
|
currentTime,
|
|
81
82
|
getCompositeIpAddress(ipAddress),
|
|
82
83
|
threshold,
|
|
83
|
-
|
|
84
|
+
sessionId
|
|
84
85
|
);
|
|
85
86
|
return {
|
|
86
87
|
captchas,
|
|
@@ -172,10 +173,10 @@ class ImgCaptchaManager extends CaptchaManager {
|
|
|
172
173
|
userSignature: userTimestampSignature,
|
|
173
174
|
userSubmitted: true,
|
|
174
175
|
serverChecked: false,
|
|
175
|
-
requestedAtTimestamp: timestamp,
|
|
176
|
+
requestedAtTimestamp: new Date(timestamp),
|
|
176
177
|
ipAddress: getCompositeIpAddress(ipAddress),
|
|
177
178
|
headers,
|
|
178
|
-
|
|
179
|
+
sessionId: pendingRecord.sessionId,
|
|
179
180
|
ja4
|
|
180
181
|
};
|
|
181
182
|
await this.db.storeUserImageCaptchaSolution(receivedCaptchas, commit);
|
|
@@ -332,7 +333,7 @@ class ImgCaptchaManager extends CaptchaManager {
|
|
|
332
333
|
}
|
|
333
334
|
return void 0;
|
|
334
335
|
}
|
|
335
|
-
async verifyImageCaptchaSolution(user, dapp, commitmentId, env, maxVerifiedTime, ip) {
|
|
336
|
+
async verifyImageCaptchaSolution(user, dapp, commitmentId, env, maxVerifiedTime, ip, disallowWebView, contextAwareEnabled = false) {
|
|
336
337
|
const solution = await (commitmentId ? this.getDappUserCommitmentById(commitmentId) : this.getDappUserCommitmentByAccount(user, dapp));
|
|
337
338
|
if (!solution) {
|
|
338
339
|
this.logger.debug(() => ({
|
|
@@ -349,7 +350,7 @@ class ImgCaptchaManager extends CaptchaManager {
|
|
|
349
350
|
}
|
|
350
351
|
maxVerifiedTime = maxVerifiedTime || 60 * 1e3;
|
|
351
352
|
const currentTime = Date.now();
|
|
352
|
-
const timeSinceCompletion = currentTime - solution.requestedAtTimestamp;
|
|
353
|
+
const timeSinceCompletion = currentTime - solution.requestedAtTimestamp.getTime();
|
|
353
354
|
if (timeSinceCompletion > maxVerifiedTime) {
|
|
354
355
|
this.logger.debug(() => ({
|
|
355
356
|
msg: "Not verified - timed out"
|
|
@@ -389,18 +390,29 @@ class ImgCaptchaManager extends CaptchaManager {
|
|
|
389
390
|
}
|
|
390
391
|
const isApproved = solution.result.status === CaptchaStatus.approved;
|
|
391
392
|
let score;
|
|
392
|
-
if (solution.
|
|
393
|
-
const
|
|
394
|
-
solution.
|
|
393
|
+
if (solution.sessionId) {
|
|
394
|
+
const sessionRecord = await this.db.getSessionRecordBySessionId(
|
|
395
|
+
solution.sessionId
|
|
395
396
|
);
|
|
396
|
-
if (
|
|
397
|
-
score = computeFrictionlessScore(
|
|
397
|
+
if (sessionRecord) {
|
|
398
|
+
score = computeFrictionlessScore(sessionRecord?.scoreComponents);
|
|
398
399
|
this.logger.info(() => ({
|
|
399
400
|
data: {
|
|
400
|
-
|
|
401
|
+
scoreComponents: sessionRecord?.scoreComponents,
|
|
401
402
|
score
|
|
402
403
|
}
|
|
403
404
|
}));
|
|
405
|
+
if (disallowWebView === true && (sessionRecord.scoreComponents.webView || 0) > 0) {
|
|
406
|
+
this.logger.info(() => ({
|
|
407
|
+
msg: "Disallowing webview access - user not verified"
|
|
408
|
+
}));
|
|
409
|
+
return { status: "API.USER_NOT_VERIFIED", verified: false };
|
|
410
|
+
}
|
|
411
|
+
if (contextAwareEnabled && sessionRecord.reason === FrictionlessReason.CONTEXT_AWARE_VALIDATION_FAILED) {
|
|
412
|
+
this.logger.info(() => ({
|
|
413
|
+
msg: "Context aware validation failed"
|
|
414
|
+
}));
|
|
415
|
+
}
|
|
404
416
|
}
|
|
405
417
|
}
|
|
406
418
|
return {
|
|
@@ -9,8 +9,8 @@ import { computeFrictionlessScore } from "../frictionless/frictionlessTasksUtils
|
|
|
9
9
|
import { checkPowSignature, validateSolution } from "./powTasksUtils.js";
|
|
10
10
|
const DEFAULT_POW_DIFFICULTY = 4;
|
|
11
11
|
class PowCaptchaManager extends CaptchaManager {
|
|
12
|
-
constructor(db, pair, logger) {
|
|
13
|
-
super(db, pair, logger);
|
|
12
|
+
constructor(db, pair, config, logger) {
|
|
13
|
+
super(db, pair, config, logger);
|
|
14
14
|
this.POW_SEPARATOR = POW_SEPARATOR;
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
@@ -177,15 +177,15 @@ class PowCaptchaManager extends CaptchaManager {
|
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
let score;
|
|
180
|
-
if (challengeRecord.
|
|
181
|
-
const
|
|
182
|
-
challengeRecord.
|
|
180
|
+
if (challengeRecord.sessionId) {
|
|
181
|
+
const sessionRecord = await this.db.getSessionRecordBySessionId(
|
|
182
|
+
challengeRecord.sessionId
|
|
183
183
|
);
|
|
184
|
-
if (
|
|
185
|
-
score = computeFrictionlessScore(
|
|
184
|
+
if (sessionRecord) {
|
|
185
|
+
score = computeFrictionlessScore(sessionRecord?.scoreComponents);
|
|
186
186
|
this.logger.info(() => ({
|
|
187
187
|
data: {
|
|
188
|
-
|
|
188
|
+
scoreComponents: { ...sessionRecord?.scoreComponents || {} },
|
|
189
189
|
score
|
|
190
190
|
}
|
|
191
191
|
}));
|
package/dist/tasks/tasks.js
CHANGED
package/dist/util.js
CHANGED
|
@@ -32,7 +32,7 @@ async function checkIfTaskIsRunning(taskName, db) {
|
|
|
32
32
|
ScheduledTaskStatus.Running
|
|
33
33
|
);
|
|
34
34
|
const twoMinutesAgo = (/* @__PURE__ */ new Date()).getTime() - 1e3 * 60 * 2;
|
|
35
|
-
if (runningTask && runningTask.datetime > twoMinutesAgo) {
|
|
35
|
+
if (runningTask && runningTask.datetime.getTime() > twoMinutesAgo) {
|
|
36
36
|
const completedTask = await db.getScheduledTaskStatus(
|
|
37
37
|
runningTask._id,
|
|
38
38
|
ScheduledTaskStatus.Completed
|
|
@@ -229,6 +229,19 @@ const deepValidateIpAddress = async (ip, challengeIpAddress, logger, apiKey, api
|
|
|
229
229
|
if (standardValidation.errorMessage?.includes("Invalid IP address")) {
|
|
230
230
|
return standardValidation;
|
|
231
231
|
}
|
|
232
|
+
if (ipValidationRules?.forceConsistentIp === true) {
|
|
233
|
+
logger.info(() => ({
|
|
234
|
+
msg: "IP validation failed - forceConsistentIp is true",
|
|
235
|
+
data: {
|
|
236
|
+
challengeIp: challengeIpAddress.address,
|
|
237
|
+
providedIp: ip
|
|
238
|
+
}
|
|
239
|
+
}));
|
|
240
|
+
return {
|
|
241
|
+
isValid: false,
|
|
242
|
+
errorMessage: standardValidation.errorMessage
|
|
243
|
+
};
|
|
244
|
+
}
|
|
232
245
|
} else {
|
|
233
246
|
return { isValid: true };
|
|
234
247
|
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prosopo/provider",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.13.0",
|
|
4
4
|
"author": "PROSOPO LIMITED <info@prosopo.io>",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
8
|
-
"node": "
|
|
9
|
-
"npm": "10.
|
|
8
|
+
"node": ">=v20.0.0",
|
|
9
|
+
"npm": ">=10.6.0"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
12
|
"clean": "del-cli --verbose dist tsconfig.tsbuildinfo",
|
|
@@ -21,30 +21,29 @@
|
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@noble/hashes": "1.8.0",
|
|
24
|
-
"@polkadot/util": "
|
|
25
|
-
"@prosopo/api": "3.1.
|
|
26
|
-
"@prosopo/api-express-router": "3.0.
|
|
27
|
-
"@prosopo/api-route": "2.6.
|
|
28
|
-
"@prosopo/common": "3.1.
|
|
29
|
-
"@prosopo/config": "3.1.
|
|
30
|
-
"@prosopo/database": "3.
|
|
31
|
-
"@prosopo/datasets": "3.0.
|
|
32
|
-
"@prosopo/env": "3.2.
|
|
33
|
-
"@prosopo/keyring": "2.8.
|
|
34
|
-
"@prosopo/load-balancer": "2.8.
|
|
35
|
-
"@prosopo/locale": "3.1.
|
|
36
|
-
"@prosopo/types": "3.
|
|
37
|
-
"@prosopo/types-database": "
|
|
38
|
-
"@prosopo/types-env": "2.7.
|
|
39
|
-
"@prosopo/user-access-policy": "3.5.
|
|
40
|
-
"@prosopo/util": "3.
|
|
41
|
-
"@prosopo/util-crypto": "13.5.
|
|
24
|
+
"@polkadot/util": "13.5.7",
|
|
25
|
+
"@prosopo/api": "3.1.33",
|
|
26
|
+
"@prosopo/api-express-router": "3.0.34",
|
|
27
|
+
"@prosopo/api-route": "2.6.30",
|
|
28
|
+
"@prosopo/common": "3.1.22",
|
|
29
|
+
"@prosopo/config": "3.1.22",
|
|
30
|
+
"@prosopo/database": "3.5.0",
|
|
31
|
+
"@prosopo/datasets": "3.0.43",
|
|
32
|
+
"@prosopo/env": "3.2.22",
|
|
33
|
+
"@prosopo/keyring": "2.8.36",
|
|
34
|
+
"@prosopo/load-balancer": "2.8.9",
|
|
35
|
+
"@prosopo/locale": "3.1.22",
|
|
36
|
+
"@prosopo/types": "3.6.0",
|
|
37
|
+
"@prosopo/types-database": "4.0.0",
|
|
38
|
+
"@prosopo/types-env": "2.7.47",
|
|
39
|
+
"@prosopo/user-access-policy": "3.5.28",
|
|
40
|
+
"@prosopo/util": "3.2.0",
|
|
41
|
+
"@prosopo/util-crypto": "13.5.24",
|
|
42
42
|
"cron": "3.1.7",
|
|
43
43
|
"express": "4.21.2",
|
|
44
44
|
"geolib": "3.3.4",
|
|
45
45
|
"i18next": "24.1.0",
|
|
46
46
|
"ip-address": "10.0.1",
|
|
47
|
-
"mongodb": "6.15.0",
|
|
48
47
|
"mongoose": "8.13.0",
|
|
49
48
|
"read-tls-client-hello": "1.1.0",
|
|
50
49
|
"uuid": "11.1.0",
|
|
@@ -53,7 +52,7 @@
|
|
|
53
52
|
"devDependencies": {
|
|
54
53
|
"@types/node": "22.5.5",
|
|
55
54
|
"@types/uuid": "10.0.0",
|
|
56
|
-
"@vitest/coverage-v8": "3.
|
|
55
|
+
"@vitest/coverage-v8": "3.2.4",
|
|
57
56
|
"concurrently": "9.0.1",
|
|
58
57
|
"del-cli": "6.0.0",
|
|
59
58
|
"dotenv": "16.4.5",
|
|
@@ -61,8 +60,8 @@
|
|
|
61
60
|
"tslib": "2.7.0",
|
|
62
61
|
"tsx": "4.20.3",
|
|
63
62
|
"typescript": "5.6.2",
|
|
64
|
-
"vite": "6.
|
|
65
|
-
"vitest": "3.
|
|
63
|
+
"vite": "6.4.1",
|
|
64
|
+
"vitest": "3.2.4"
|
|
66
65
|
},
|
|
67
66
|
"repository": {
|
|
68
67
|
"type": "git",
|