@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.
Files changed (49) hide show
  1. package/CHANGELOG.md +214 -0
  2. package/dist/api/admin/apiAdminRoutesProvider.js +13 -18
  3. package/dist/api/admin/apiToggleMaintenanceModeEndpoint.js +40 -0
  4. package/dist/api/blacklistRequestInspector.js +4 -4
  5. package/dist/api/captcha/getFrictionlessCaptchaChallenge.js +338 -0
  6. package/dist/api/captcha/getImageCaptchaChallenge.js +150 -0
  7. package/dist/api/captcha/getPoWCaptchaChallenge.js +156 -0
  8. package/dist/api/captcha/submitImageCaptchaSolution.js +87 -0
  9. package/dist/api/captcha/submitPoWCaptchaSolution.js +77 -0
  10. package/dist/api/captcha.js +18 -606
  11. package/dist/api/verify.js +24 -1
  12. package/dist/cjs/api/admin/apiAdminRoutesProvider.cjs +13 -18
  13. package/dist/cjs/api/admin/apiRegisterSiteKeyEndpoint.cjs +2 -1
  14. package/dist/cjs/api/admin/apiRemoveDetectorKeyEndpoint.cjs +3 -2
  15. package/dist/cjs/api/admin/apiToggleMaintenanceModeEndpoint.cjs +41 -0
  16. package/dist/cjs/api/blacklistRequestInspector.cjs +3 -3
  17. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge.cjs +337 -0
  18. package/dist/cjs/api/captcha/getImageCaptchaChallenge.cjs +149 -0
  19. package/dist/cjs/api/captcha/getPoWCaptchaChallenge.cjs +155 -0
  20. package/dist/cjs/api/captcha/submitImageCaptchaSolution.cjs +86 -0
  21. package/dist/cjs/api/captcha/submitPoWCaptchaSolution.cjs +76 -0
  22. package/dist/cjs/api/captcha.cjs +17 -605
  23. package/dist/cjs/api/ja4Middleware.cjs +2 -1
  24. package/dist/cjs/api/verify.cjs +24 -1
  25. package/dist/cjs/index.cjs +2 -0
  26. package/dist/cjs/schedulers/setClientEntropy.cjs +36 -0
  27. package/dist/cjs/tasks/captchaManager.cjs +7 -22
  28. package/dist/cjs/tasks/client/clientTasks.cjs +18 -36
  29. package/dist/cjs/tasks/detection/decodePayload.cjs +385 -714
  30. package/dist/cjs/tasks/detection/getBotScore.cjs +15 -2
  31. package/dist/cjs/tasks/frictionless/frictionlessTasks.cjs +136 -30
  32. package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasks.cjs +25 -13
  33. package/dist/cjs/tasks/powCaptcha/powTasks.cjs +8 -8
  34. package/dist/cjs/tasks/tasks.cjs +1 -0
  35. package/dist/cjs/util.cjs +14 -1
  36. package/dist/cjs/utils/hashUserIp.cjs +9 -0
  37. package/dist/index.js +2 -0
  38. package/dist/schedulers/setClientEntropy.js +36 -0
  39. package/dist/tasks/captchaManager.js +5 -21
  40. package/dist/tasks/client/clientTasks.js +19 -37
  41. package/dist/tasks/detection/decodePayload.js +385 -714
  42. package/dist/tasks/detection/getBotScore.js +17 -4
  43. package/dist/tasks/frictionless/frictionlessTasks.js +137 -31
  44. package/dist/tasks/imgCaptcha/imgCaptchaTasks.js +25 -13
  45. package/dist/tasks/powCaptcha/powTasks.js +8 -8
  46. package/dist/tasks/tasks.js +1 -0
  47. package/dist/util.js +14 -1
  48. package/dist/utils/hashUserIp.js +9 -0
  49. package/package.json +24 -25
@@ -1,8 +1,9 @@
1
- import GGaYiU from "./decodePayload.js";
1
+ import fYWyhW from "./decodePayload.js";
2
2
  const DEFAULT_ENTROPY = 13837;
3
- const getBotScore = async (payload, privateKeyString) => {
4
- const result = await GGaYiU(
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 { baseBotScore, timestamp, providerSelectEntropy, userId, userAgent };
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(tokenId, captchaType, solvedImagesCount, powDifficulty) {
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
- tokenId,
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(tokenId, solvedImagesCount) {
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
- tokenId,
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(tokenId, powDifficulty) {
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
- tokenId,
131
+ effectiveParams.token,
132
+ effectiveParams.score,
133
+ effectiveParams.threshold,
134
+ effectiveParams.scoreComponents,
135
+ effectiveParams.providerSelectEntropy,
136
+ effectiveParams.ipAddress,
71
137
  CaptchaType.pow,
72
- powDifficulty
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
- async scoreIncreaseAccessPolicy(accessPolicy, baseBotScore, botScore, tokenId) {
152
+ scoreIncreaseAccessPolicy(accessPolicy, baseBotScore, botScore, scoreComponents) {
81
153
  const accessPolicyPenalty = accessPolicy?.frictionlessScore || this.config.penalties.PENALTY_ACCESS_RULE;
82
154
  botScore += accessPolicyPenalty;
83
- await this.db.updateFrictionlessTokenRecord(tokenId, {
155
+ return {
84
156
  score: botScore,
85
157
  scoreComponents: {
86
- baseScore: baseBotScore,
158
+ ...scoreComponents,
87
159
  accessPolicy: accessPolicyPenalty
88
160
  }
89
- });
90
- return botScore;
161
+ };
91
162
  }
92
- async scoreIncreaseUnverifiedHost(host, baseBotScore, botScore, tokenId) {
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
- await this.db.updateFrictionlessTokenRecord(tokenId, {
169
+ return {
99
170
  score: botScore,
100
171
  scoreComponents: {
101
- baseScore: baseBotScore,
172
+ ...scoreComponents,
102
173
  unverifiedHost: this.config.penalties.PENALTY_UNVERIFIED_HOST
103
174
  }
104
- });
105
- return botScore;
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
- async scoreIncreaseTimestamp(timestamp, baseBotScore, botScore, tokenId) {
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
- await this.db.updateFrictionlessTokenRecord(tokenId, {
196
+ return {
114
197
  score: botScore,
115
198
  scoreComponents: {
116
- baseScore: baseBotScore,
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, frictionlessTokenId) {
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
- frictionlessTokenId
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
- frictionlessTokenId: pendingRecord.frictionlessTokenId,
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.frictionlessTokenId) {
393
- const tokenRecord = await this.db.getFrictionlessTokenRecordByTokenId(
394
- solution.frictionlessTokenId
393
+ if (solution.sessionId) {
394
+ const sessionRecord = await this.db.getSessionRecordBySessionId(
395
+ solution.sessionId
395
396
  );
396
- if (tokenRecord) {
397
- score = computeFrictionlessScore(tokenRecord?.scoreComponents);
397
+ if (sessionRecord) {
398
+ score = computeFrictionlessScore(sessionRecord?.scoreComponents);
398
399
  this.logger.info(() => ({
399
400
  data: {
400
- tscoreComponents: tokenRecord?.scoreComponents,
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.frictionlessTokenId) {
181
- const tokenRecord = await this.db.getFrictionlessTokenRecordByTokenId(
182
- challengeRecord.frictionlessTokenId
180
+ if (challengeRecord.sessionId) {
181
+ const sessionRecord = await this.db.getSessionRecordBySessionId(
182
+ challengeRecord.sessionId
183
183
  );
184
- if (tokenRecord) {
185
- score = computeFrictionlessScore(tokenRecord?.scoreComponents);
184
+ if (sessionRecord) {
185
+ score = computeFrictionlessScore(sessionRecord?.scoreComponents);
186
186
  this.logger.info(() => ({
187
187
  data: {
188
- tscoreComponents: { ...tokenRecord?.scoreComponents || {} },
188
+ scoreComponents: { ...sessionRecord?.scoreComponents || {} },
189
189
  score
190
190
  }
191
191
  }));
@@ -19,6 +19,7 @@ class Tasks {
19
19
  this.powCaptchaManager = new PowCaptchaManager(
20
20
  this.db,
21
21
  this.pair,
22
+ this.config,
22
23
  this.logger
23
24
  );
24
25
  this.datasetManager = new DatasetManager(
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
  }
@@ -0,0 +1,9 @@
1
+ import { createHash } from "node:crypto";
2
+ function hashUserIp(user, ip, sitekey) {
3
+ const hash = createHash("sha256");
4
+ hash.update(`${user}:${ip}:${sitekey}`, "utf8");
5
+ return hash.digest("hex");
6
+ }
7
+ export {
8
+ hashUserIp
9
+ };
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@prosopo/provider",
3
- "version": "3.12.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": "20",
9
- "npm": "10.8.2"
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": "12.6.2",
25
- "@prosopo/api": "3.1.24",
26
- "@prosopo/api-express-router": "3.0.25",
27
- "@prosopo/api-route": "2.6.28",
28
- "@prosopo/common": "3.1.20",
29
- "@prosopo/config": "3.1.20",
30
- "@prosopo/database": "3.4.5",
31
- "@prosopo/datasets": "3.0.34",
32
- "@prosopo/env": "3.2.13",
33
- "@prosopo/keyring": "2.8.27",
34
- "@prosopo/load-balancer": "2.8.0",
35
- "@prosopo/locale": "3.1.20",
36
- "@prosopo/types": "3.5.3",
37
- "@prosopo/types-database": "3.3.5",
38
- "@prosopo/types-env": "2.7.38",
39
- "@prosopo/user-access-policy": "3.5.19",
40
- "@prosopo/util": "3.1.5",
41
- "@prosopo/util-crypto": "13.5.22",
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.0.9",
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.3.5",
65
- "vitest": "3.0.9"
63
+ "vite": "6.4.1",
64
+ "vitest": "3.2.4"
66
65
  },
67
66
  "repository": {
68
67
  "type": "git",