@prosopo/provider 4.15.14 → 4.15.16
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 +106 -106
- package/.turbo/turbo-build$colon$tsc.log +24 -24
- package/.turbo/turbo-build.log +107 -104
- package/CHANGELOG.md +63 -0
- package/dist/_virtual/_rolldown/runtime.js +3 -0
- package/dist/api/admin/apiAdminRoutesProvider.js +27 -46
- package/dist/api/admin/apiClearAllCountersEndpoint.js +62 -57
- package/dist/api/admin/apiDnsEventEndpoint.js +79 -92
- package/dist/api/admin/apiGetAllDecisionMachinesEndpoint.js +43 -45
- package/dist/api/admin/apiGetDecisionMachineEndpoint.js +40 -40
- package/dist/api/admin/apiRegisterSiteKeyEndpoint.js +23 -22
- package/dist/api/admin/apiRegisterSiteKeysEndpoint.js +26 -30
- package/dist/api/admin/apiRemoveAllDecisionMachinesEndpoint.js +35 -37
- package/dist/api/admin/apiRemoveDecisionMachineEndpoint.js +40 -40
- package/dist/api/admin/apiRemoveDetectorKeyEndpoint.d.ts.map +1 -1
- package/dist/api/admin/apiRemoveDetectorKeyEndpoint.js +29 -31
- package/dist/api/admin/apiRemoveSiteKeyEndpoint.js +33 -29
- package/dist/api/admin/apiRemoveSiteKeysEndpoint.js +32 -31
- package/dist/api/admin/apiToggleMaintenanceModeEndpoint.js +43 -35
- package/dist/api/admin/apiUpdateDecisionMachineEndpoint.js +43 -63
- package/dist/api/admin/apiUpdateDetectorKeyEndpoint.js +36 -35
- package/dist/api/admin/createApiAdminRoutesProvider.js +6 -8
- package/dist/api/blacklistRequestInspector.js +285 -377
- package/dist/api/block.js +17 -30
- package/dist/api/captcha/checkSpamEmail.js +88 -98
- package/dist/api/captcha/contextAwareValidation.js +21 -15
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.js +126 -140
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/constants.js +11 -12
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.js +236 -299
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/decryptSimdReadings.js +10 -13
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js +336 -462
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/honeypotResponse.js +17 -14
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/sessionDedup.js +27 -27
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.js +42 -63
- package/dist/api/captcha/getFrictionlessCaptchaChallenge.js +2 -4
- package/dist/api/captcha/getImageCaptchaChallenge.js +136 -207
- package/dist/api/captcha/getPoWCaptchaChallenge.js +128 -198
- package/dist/api/captcha/getPuzzleCaptchaChallenge.js +132 -201
- package/dist/api/captcha/maintenanceModeResponses.js +38 -47
- package/dist/api/captcha/submitImageCaptchaSolution.js +75 -101
- package/dist/api/captcha/submitPoWCaptchaSolution.js +137 -207
- package/dist/api/captcha/submitPuzzleCaptchaSolution.js +73 -105
- package/dist/api/captcha.js +76 -58
- package/dist/api/dnsEventUrl.js +20 -22
- package/dist/api/domainMiddleware.js +104 -115
- package/dist/api/forwardVerify.js +62 -48
- package/dist/api/handshakeTimingMiddleware.js +43 -62
- package/dist/api/hardBlockVerdictCache.js +114 -117
- package/dist/api/headerCheckMiddleware.js +34 -34
- package/dist/api/ignoreMiddleware.js +13 -15
- package/dist/api/ipInfoMiddleware.js +17 -19
- package/dist/api/ja4.js +149 -188
- package/dist/api/ja4Middleware.js +39 -54
- package/dist/api/metrics.js +245 -257
- package/dist/api/public.js +52 -54
- package/dist/api/robotsMiddleware.js +10 -10
- package/dist/api/startProviderApi.js +205 -232
- package/dist/api/testSiteKey.js +16 -16
- package/dist/api/validateAddress.js +23 -21
- package/dist/api/verify.js +334 -377
- package/dist/cjs/_virtual/_rolldown/runtime.cjs +23 -0
- package/dist/cjs/api/admin/apiAdminRoutesProvider.cjs +40 -59
- package/dist/cjs/api/admin/apiClearAllCountersEndpoint.cjs +63 -58
- package/dist/cjs/api/admin/apiDnsEventEndpoint.cjs +82 -93
- package/dist/cjs/api/admin/apiGetAllDecisionMachinesEndpoint.cjs +46 -48
- package/dist/cjs/api/admin/apiGetDecisionMachineEndpoint.cjs +41 -41
- package/dist/cjs/api/admin/apiRegisterSiteKeyEndpoint.cjs +25 -24
- package/dist/cjs/api/admin/apiRegisterSiteKeysEndpoint.cjs +28 -32
- package/dist/cjs/api/admin/apiRemoveAllDecisionMachinesEndpoint.cjs +38 -40
- package/dist/cjs/api/admin/apiRemoveDecisionMachineEndpoint.cjs +41 -41
- package/dist/cjs/api/admin/apiRemoveDetectorKeyEndpoint.cjs +31 -33
- package/dist/cjs/api/admin/apiRemoveSiteKeyEndpoint.cjs +35 -31
- package/dist/cjs/api/admin/apiRemoveSiteKeysEndpoint.cjs +34 -33
- package/dist/cjs/api/admin/apiToggleMaintenanceModeEndpoint.cjs +45 -35
- package/dist/cjs/api/admin/apiUpdateDecisionMachineEndpoint.cjs +45 -65
- package/dist/cjs/api/admin/apiUpdateDetectorKeyEndpoint.cjs +38 -37
- package/dist/cjs/api/admin/createApiAdminRoutesProvider.cjs +7 -8
- package/dist/cjs/api/blacklistRequestInspector.cjs +288 -376
- package/dist/cjs/api/block.cjs +18 -30
- package/dist/cjs/api/captcha/checkSpamEmail.cjs +90 -100
- package/dist/cjs/api/captcha/contextAwareValidation.cjs +21 -14
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.cjs +125 -140
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/constants.cjs +10 -10
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.cjs +238 -301
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/decryptSimdReadings.cjs +9 -12
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/handler.cjs +349 -473
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/honeypotResponse.cjs +18 -14
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/sessionDedup.cjs +26 -26
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.cjs +42 -63
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge.cjs +2 -3
- package/dist/cjs/api/captcha/getImageCaptchaChallenge.cjs +138 -208
- package/dist/cjs/api/captcha/getPoWCaptchaChallenge.cjs +131 -200
- package/dist/cjs/api/captcha/getPuzzleCaptchaChallenge.cjs +135 -203
- package/dist/cjs/api/captcha/maintenanceModeResponses.cjs +38 -44
- package/dist/cjs/api/captcha/submitImageCaptchaSolution.cjs +77 -102
- package/dist/cjs/api/captcha/submitPoWCaptchaSolution.cjs +140 -209
- package/dist/cjs/api/captcha/submitPuzzleCaptchaSolution.cjs +76 -107
- package/dist/cjs/api/captcha.cjs +79 -58
- package/dist/cjs/api/dnsEventUrl.cjs +20 -21
- package/dist/cjs/api/domainMiddleware.cjs +106 -117
- package/dist/cjs/api/forwardVerify.cjs +63 -49
- package/dist/cjs/api/handshakeTimingMiddleware.cjs +43 -61
- package/dist/cjs/api/hardBlockVerdictCache.cjs +114 -114
- package/dist/cjs/api/headerCheckMiddleware.cjs +34 -34
- package/dist/cjs/api/ignoreMiddleware.cjs +12 -14
- package/dist/cjs/api/ipInfoMiddleware.cjs +16 -18
- package/dist/cjs/api/ja4.cjs +149 -187
- package/dist/cjs/api/ja4Middleware.cjs +40 -53
- package/dist/cjs/api/metrics.cjs +229 -228
- package/dist/cjs/api/public.cjs +57 -57
- package/dist/cjs/api/robotsMiddleware.cjs +9 -9
- package/dist/cjs/api/startProviderApi.cjs +219 -237
- package/dist/cjs/api/testSiteKey.cjs +15 -14
- package/dist/cjs/api/validateAddress.cjs +24 -21
- package/dist/cjs/api/verify.cjs +338 -379
- package/dist/cjs/compositeIpAddress.cjs +40 -47
- package/dist/cjs/index.cjs +65 -71
- package/dist/cjs/pairs.cjs +31 -35
- package/dist/cjs/rules/lang.cjs +10 -14
- package/dist/cjs/schedulers/captchaScheduler.cjs +26 -32
- package/dist/cjs/schedulers/getClientList.cjs +35 -33
- package/dist/cjs/schedulers/setClientEntropy.cjs +35 -33
- package/dist/cjs/schedulers/updateSpamEmailDomains.cjs +39 -43
- package/dist/cjs/services/ipComparison.cjs +122 -119
- package/dist/cjs/tasks/captchaManager.cjs +407 -486
- package/dist/cjs/tasks/client/clientTasks.cjs +347 -470
- package/dist/cjs/tasks/dataset/datasetTasks.cjs +25 -29
- package/dist/cjs/tasks/dataset/datasetTasksUtils.cjs +16 -32
- package/dist/cjs/tasks/decisionMachine/decisionMachineRunner.cjs +220 -307
- package/dist/cjs/tasks/detection/decodeBehavior.cjs +408 -317
- package/dist/cjs/tasks/detection/decodePayload.cjs +686 -468
- package/dist/cjs/tasks/detection/decodeSimd.cjs +462 -304
- package/dist/cjs/tasks/detection/getBotScore.cjs +37 -43
- package/dist/cjs/tasks/dnsEvent/enrichDnsEvent.cjs +88 -71
- package/dist/cjs/tasks/frictionless/frictionlessTasks.cjs +348 -486
- package/dist/cjs/tasks/frictionless/frictionlessTasksUtils.cjs +13 -30
- package/dist/cjs/tasks/frictionless/routingMachine.cjs +71 -55
- package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasks.cjs +555 -806
- package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasksUtils.cjs +22 -23
- package/dist/cjs/tasks/index.cjs +2 -4
- package/dist/cjs/tasks/powCaptcha/powTasks.cjs +491 -687
- package/dist/cjs/tasks/powCaptcha/powTasksUtils.cjs +17 -29
- package/dist/cjs/tasks/puzzleCaptcha/puzzleTasks.cjs +422 -591
- package/dist/cjs/tasks/puzzleCaptcha/puzzleTasksUtils.cjs +8 -5
- package/dist/cjs/tasks/spam/checkSpamEmail.cjs +133 -141
- package/dist/cjs/tasks/spam/checkTrafficFilter.cjs +118 -76
- package/dist/cjs/tasks/spam/evaluateEmailSpamRules.cjs +75 -84
- package/dist/cjs/tasks/spam/updateSpamEmailDomains.cjs +40 -56
- package/dist/cjs/tasks/tasks.cjs +107 -158
- package/dist/cjs/util/usageCounters.cjs +220 -211
- package/dist/cjs/util.cjs +329 -359
- package/dist/cjs/utils/devicePlatform.cjs +14 -8
- package/dist/cjs/utils/dns.cjs +68 -93
- package/dist/cjs/utils/hashUserAgent.cjs +14 -7
- package/dist/cjs/utils/hashUserIp.cjs +14 -6
- package/dist/cjs/utils/honeypot/encoders.cjs +74 -80
- package/dist/cjs/utils/honeypot/phraseBank.cjs +51 -44
- package/dist/cjs/utils/normalizeRequestIp.cjs +33 -25
- package/dist/compositeIpAddress.js +38 -46
- package/dist/index.js +12 -56
- package/dist/pairs.js +31 -37
- package/dist/rules/lang.js +11 -15
- package/dist/schedulers/captchaScheduler.js +25 -31
- package/dist/schedulers/getClientList.js +34 -32
- package/dist/schedulers/setClientEntropy.js +34 -32
- package/dist/schedulers/updateSpamEmailDomains.js +38 -42
- package/dist/services/ipComparison.js +122 -119
- package/dist/tasks/captchaManager.js +403 -483
- package/dist/tasks/client/clientTasks.js +343 -467
- package/dist/tasks/dataset/datasetTasks.js +24 -28
- package/dist/tasks/dataset/datasetTasksUtils.js +15 -31
- package/dist/tasks/decisionMachine/decisionMachineRunner.js +217 -306
- package/dist/tasks/detection/getBotScore.js +37 -43
- package/dist/tasks/dnsEvent/enrichDnsEvent.d.ts.map +1 -1
- package/dist/tasks/dnsEvent/enrichDnsEvent.js +88 -74
- package/dist/tasks/dnsEvent/enrichDnsEvent.js.map +1 -1
- package/dist/tasks/frictionless/frictionlessTasks.js +342 -479
- package/dist/tasks/frictionless/frictionlessTasksUtils.js +14 -32
- package/dist/tasks/frictionless/routingMachine.js +72 -56
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.js +550 -801
- package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.js +21 -22
- package/dist/tasks/index.js +1 -3
- package/dist/tasks/powCaptcha/powTasks.d.ts.map +1 -1
- package/dist/tasks/powCaptcha/powTasks.js +486 -682
- package/dist/tasks/powCaptcha/powTasks.js.map +1 -1
- package/dist/tasks/powCaptcha/powTasksUtils.js +15 -28
- package/dist/tasks/puzzleCaptcha/puzzleTasks.d.ts.map +1 -1
- package/dist/tasks/puzzleCaptcha/puzzleTasks.js +417 -586
- package/dist/tasks/puzzleCaptcha/puzzleTasks.js.map +1 -1
- package/dist/tasks/puzzleCaptcha/puzzleTasksUtils.js +9 -6
- package/dist/tasks/spam/checkSpamEmail.js +133 -141
- package/dist/tasks/spam/checkTrafficFilter.d.ts +1 -0
- package/dist/tasks/spam/checkTrafficFilter.d.ts.map +1 -1
- package/dist/tasks/spam/checkTrafficFilter.js +117 -77
- package/dist/tasks/spam/checkTrafficFilter.js.map +1 -1
- package/dist/tasks/spam/evaluateEmailSpamRules.js +75 -86
- package/dist/tasks/spam/updateSpamEmailDomains.js +36 -54
- package/dist/tasks/tasks.js +98 -150
- package/dist/tests/integration/api/blacklistRequestInspector.integration.test.js +4 -1
- package/dist/tests/integration/api/blacklistRequestInspector.integration.test.js.map +1 -1
- package/dist/tests/unit/api/block.unit.test.js +8 -4
- package/dist/tests/unit/api/block.unit.test.js.map +1 -1
- package/dist/tests/unit/api/captcha/maintenanceModeShortCircuit.unit.test.js +8 -6
- package/dist/tests/unit/api/captcha/maintenanceModeShortCircuit.unit.test.js.map +1 -1
- package/dist/tests/unit/api/forwardVerify.unit.test.js +3 -1
- package/dist/tests/unit/api/forwardVerify.unit.test.js.map +1 -1
- package/dist/tests/unit/api/getFrictionlessCaptchaChallenge.unit.test.js +15 -7
- package/dist/tests/unit/api/getFrictionlessCaptchaChallenge.unit.test.js.map +1 -1
- package/dist/tests/unit/schedulers/captchaScheduler.unit.test.js +24 -18
- package/dist/tests/unit/schedulers/captchaScheduler.unit.test.js.map +1 -1
- package/dist/tests/unit/schedulers/getClientList.unit.test.js +19 -13
- package/dist/tests/unit/schedulers/getClientList.unit.test.js.map +1 -1
- package/dist/tests/unit/schedulers/setClientEntropy.unit.test.js +19 -13
- package/dist/tests/unit/schedulers/setClientEntropy.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/client/clientTasks.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.unit.test.js +22 -16
- package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/spam/checkTrafficFilter.unit.test.js +140 -0
- package/dist/tests/unit/tasks/spam/checkTrafficFilter.unit.test.js.map +1 -1
- package/dist/tests/unit/testUtils/mockProviderEnv.d.ts +7 -7
- package/dist/util/usageCounters.js +220 -212
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +327 -364
- package/dist/util.js.map +1 -1
- package/dist/utils/devicePlatform.js +15 -9
- package/dist/utils/dns.js +64 -77
- package/dist/utils/hashUserAgent.js +14 -7
- package/dist/utils/hashUserIp.js +14 -6
- package/dist/utils/honeypot/encoders.js +74 -80
- package/dist/utils/honeypot/phraseBank.js +49 -44
- package/dist/utils/normalizeRequestIp.js +34 -26
- package/package.json +24 -24
|
@@ -1,30 +1,26 @@
|
|
|
1
|
-
import { parseCaptchaDataset } from "@prosopo/datasets";
|
|
2
1
|
import { providerValidateDataset } from "./datasetTasksUtils.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
await this.db?.storeDataset(dataset);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
export {
|
|
29
|
-
DatasetManager
|
|
2
|
+
import { parseCaptchaDataset } from "@prosopo/datasets";
|
|
3
|
+
//#region src/tasks/dataset/datasetTasks.ts
|
|
4
|
+
var DatasetManager = class {
|
|
5
|
+
constructor(config, logger, captchaConfig, db) {
|
|
6
|
+
this.config = config;
|
|
7
|
+
this.logger = logger;
|
|
8
|
+
this.captchaConfig = captchaConfig;
|
|
9
|
+
this.db = db;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* @description Set the provider dataset from a file
|
|
13
|
+
*
|
|
14
|
+
* @param file JSON of the captcha dataset
|
|
15
|
+
*/
|
|
16
|
+
async providerSetDatasetFromFile(file) {
|
|
17
|
+
const datasetRaw = parseCaptchaDataset(file);
|
|
18
|
+
return await this.providerSetDataset(datasetRaw);
|
|
19
|
+
}
|
|
20
|
+
async providerSetDataset(datasetRaw) {
|
|
21
|
+
const dataset = await providerValidateDataset(datasetRaw, this.captchaConfig.solved.count, this.captchaConfig.unsolved.count);
|
|
22
|
+
await this.db?.storeDataset(dataset);
|
|
23
|
+
}
|
|
30
24
|
};
|
|
25
|
+
//#endregion
|
|
26
|
+
export { DatasetManager };
|
|
@@ -1,34 +1,18 @@
|
|
|
1
1
|
import { ProsopoEnvError } from "@prosopo/common";
|
|
2
2
|
import { buildDataset } from "@prosopo/datasets";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
throw new ProsopoEnvError("DATASET.SOLUTIONS_COUNT_LESS_THAN_CONFIGURED", {
|
|
17
|
-
context: { failedFuncName: providerValidateDataset.name }
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
const dataset = await buildDataset(datasetRaw);
|
|
21
|
-
if (!dataset.datasetId || !dataset.datasetContentId) {
|
|
22
|
-
throw new ProsopoEnvError("DATASET.DATASET_ID_UNDEFINED", {
|
|
23
|
-
context: {
|
|
24
|
-
failedFuncName: providerValidateDataset.name,
|
|
25
|
-
datasetId: dataset.datasetId,
|
|
26
|
-
datasetContentId: dataset.datasetContentId
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
return dataset;
|
|
31
|
-
};
|
|
32
|
-
export {
|
|
33
|
-
providerValidateDataset
|
|
3
|
+
//#region src/tasks/dataset/datasetTasksUtils.ts
|
|
4
|
+
var providerValidateDataset = async (datasetRaw, minSolvedCaptchas, minUnsolvedCaptchas) => {
|
|
5
|
+
if (datasetRaw.captchas.length < minSolvedCaptchas + minUnsolvedCaptchas) throw new ProsopoEnvError("DATASET.CAPTCHAS_COUNT_LESS_THAN_CONFIGURED", { context: { failedFuncName: providerValidateDataset.name } });
|
|
6
|
+
const solutions = datasetRaw.captchas.map((captcha) => captcha.solution ? 1 : 0).reduce((partialSum, b) => partialSum + b, 0);
|
|
7
|
+
if (solutions < minSolvedCaptchas) throw new ProsopoEnvError("DATASET.SOLUTIONS_COUNT_LESS_THAN_CONFIGURED", { context: { failedFuncName: providerValidateDataset.name } });
|
|
8
|
+
if (solutions < minUnsolvedCaptchas) throw new ProsopoEnvError("DATASET.SOLUTIONS_COUNT_LESS_THAN_CONFIGURED", { context: { failedFuncName: providerValidateDataset.name } });
|
|
9
|
+
const dataset = await buildDataset(datasetRaw);
|
|
10
|
+
if (!dataset.datasetId || !dataset.datasetContentId) throw new ProsopoEnvError("DATASET.DATASET_ID_UNDEFINED", { context: {
|
|
11
|
+
failedFuncName: providerValidateDataset.name,
|
|
12
|
+
datasetId: dataset.datasetId,
|
|
13
|
+
datasetContentId: dataset.datasetContentId
|
|
14
|
+
} });
|
|
15
|
+
return dataset;
|
|
34
16
|
};
|
|
17
|
+
//#endregion
|
|
18
|
+
export { providerValidateDataset };
|
|
@@ -1,309 +1,220 @@
|
|
|
1
|
+
import { CounterSpecSchema, DecisionMachineDecision, DecisionMachineKind, DecisionMachineOutputSchema, DecisionMachineRuntime, DecisionMachineScope, RoutingMachineOutputSchema } from "@prosopo/types";
|
|
1
2
|
import vm from "node:vm";
|
|
2
|
-
import { DecisionMachineDecision, CounterSpecSchema, DecisionMachineKind, DecisionMachineRuntime, DecisionMachineOutputSchema, RoutingMachineOutputSchema, DecisionMachineScope } from "@prosopo/types";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
return globalArtifact;
|
|
219
|
-
}
|
|
220
|
-
return void 0;
|
|
221
|
-
}
|
|
222
|
-
/**
|
|
223
|
-
* Checks if a decision machine artifact matches the requested captcha type.
|
|
224
|
-
* - If artifact has no captchaType filter: matches all captcha types
|
|
225
|
-
* - If artifact has captchaType filter: only matches if types are equal
|
|
226
|
-
*
|
|
227
|
-
* @param artifact - The decision machine artifact to check
|
|
228
|
-
* @param captchaType - The captcha type to match against (optional)
|
|
229
|
-
* @returns True if the artifact should run for this captcha type
|
|
230
|
-
*/
|
|
231
|
-
matchesCaptchaType(artifact, captchaType) {
|
|
232
|
-
if (!artifact.captchaType) {
|
|
233
|
-
return true;
|
|
234
|
-
}
|
|
235
|
-
return artifact.captchaType === captchaType;
|
|
236
|
-
}
|
|
237
|
-
/**
|
|
238
|
-
* Load the artifact source into a fresh vm sandbox, locate a callable
|
|
239
|
-
* matching one of {exportNames} (or `module.exports` itself if it's a
|
|
240
|
-
* function), invoke it with {input}, and validate the result with
|
|
241
|
-
* {schema}. Returns undefined when {options.optional} is true and no
|
|
242
|
-
* matching export exists. Otherwise throws on missing export, invalid
|
|
243
|
-
* output, or sandbox failure.
|
|
244
|
-
*/
|
|
245
|
-
async runArtifactExport(artifact, exportNames, options, schema) {
|
|
246
|
-
const sandbox = {
|
|
247
|
-
module: { exports: {} },
|
|
248
|
-
exports: {}
|
|
249
|
-
};
|
|
250
|
-
const context = vm.createContext(sandbox);
|
|
251
|
-
const script = new vm.Script(artifact.source, {
|
|
252
|
-
filename: "decision-machine.js"
|
|
253
|
-
});
|
|
254
|
-
script.runInContext(context, { timeout: LOAD_TIMEOUT_MS });
|
|
255
|
-
const exported = sandbox.module.exports;
|
|
256
|
-
const named = this.findExport(exported, exportNames);
|
|
257
|
-
if (!named) {
|
|
258
|
-
if (options.optional) return void 0;
|
|
259
|
-
throw new Error(
|
|
260
|
-
`Decision machine must export one of: ${exportNames.join(", ")}`
|
|
261
|
-
);
|
|
262
|
-
}
|
|
263
|
-
const result = await this.withTimeout(
|
|
264
|
-
Promise.resolve(named.fn(options.input)),
|
|
265
|
-
EXEC_TIMEOUT_MS
|
|
266
|
-
);
|
|
267
|
-
const parsed = schema.safeParse(result);
|
|
268
|
-
if (!parsed.success) {
|
|
269
|
-
throw new Error(
|
|
270
|
-
`Decision machine '${named.name}' output failed validation`
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
|
-
return parsed.data;
|
|
274
|
-
}
|
|
275
|
-
findExport(exported, exportNames) {
|
|
276
|
-
if (typeof exported === "function" && (exportNames.includes("default") || exportNames.includes("decide"))) {
|
|
277
|
-
return {
|
|
278
|
-
name: "default",
|
|
279
|
-
fn: exported
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
for (const name of exportNames) {
|
|
283
|
-
const candidate = exported?.[name];
|
|
284
|
-
if (typeof candidate === "function") {
|
|
285
|
-
return { name, fn: candidate };
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
return void 0;
|
|
289
|
-
}
|
|
290
|
-
async withTimeout(promise, timeoutMs) {
|
|
291
|
-
let timeoutId;
|
|
292
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
293
|
-
timeoutId = setTimeout(
|
|
294
|
-
() => reject(new Error("Decision machine timeout")),
|
|
295
|
-
timeoutMs
|
|
296
|
-
);
|
|
297
|
-
});
|
|
298
|
-
try {
|
|
299
|
-
return await Promise.race([promise, timeoutPromise]);
|
|
300
|
-
} finally {
|
|
301
|
-
if (timeoutId) {
|
|
302
|
-
clearTimeout(timeoutId);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
export {
|
|
308
|
-
DecisionMachineRunner
|
|
4
|
+
//#region src/tasks/decisionMachine/decisionMachineRunner.ts
|
|
5
|
+
var LOAD_TIMEOUT_MS = Number.parseInt(process.env.DECISION_MACHINE_LOAD_TIMEOUT_MS ?? "", 10) || 1e3;
|
|
6
|
+
var EXEC_TIMEOUT_MS = Number.parseInt(process.env.DECISION_MACHINE_EXEC_TIMEOUT_MS ?? "", 10) || 2e3;
|
|
7
|
+
/** How long cached artifacts are considered fresh (ms). */
|
|
8
|
+
var ARTIFACT_CACHE_TTL_MS = Number.parseInt(process.env.DECISION_MACHINE_ARTIFACT_CACHE_TTL_MS ?? "", 10) || 300 * 1e3;
|
|
9
|
+
var DEFAULT_DECISION = { decision: DecisionMachineDecision.Allow };
|
|
10
|
+
var RequiredCountersSchema = z.array(CounterSpecSchema);
|
|
11
|
+
var DecisionMachineRunner = class DecisionMachineRunner {
|
|
12
|
+
constructor(db) {
|
|
13
|
+
this.db = db;
|
|
14
|
+
this.artifactCache = /* @__PURE__ */ new Map();
|
|
15
|
+
}
|
|
16
|
+
/** Build a cache key for a given scope + kind + dappAccount tuple. */
|
|
17
|
+
static cacheKey(scope, kind, dappAccount) {
|
|
18
|
+
return `${scope}:${kind}:${dappAccount ?? ""}`;
|
|
19
|
+
}
|
|
20
|
+
/** Return a cached artifact if still fresh, or undefined. */
|
|
21
|
+
getCachedArtifact(scope, kind, dappAccount) {
|
|
22
|
+
const entry = this.artifactCache.get(DecisionMachineRunner.cacheKey(scope, kind, dappAccount));
|
|
23
|
+
if (!entry) return null;
|
|
24
|
+
if (Date.now() - entry.cachedAt > ARTIFACT_CACHE_TTL_MS) {
|
|
25
|
+
this.artifactCache.delete(DecisionMachineRunner.cacheKey(scope, kind, dappAccount));
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
return entry.artifact;
|
|
29
|
+
}
|
|
30
|
+
/** Store an artifact (or undefined for negative cache) in the cache. */
|
|
31
|
+
setCachedArtifact(scope, kind, dappAccount, artifact) {
|
|
32
|
+
this.artifactCache.set(DecisionMachineRunner.cacheKey(scope, kind, dappAccount), {
|
|
33
|
+
artifact,
|
|
34
|
+
cachedAt: Date.now()
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Evaluates a single decision machine artifact and returns a decision.
|
|
39
|
+
* Only one decision machine is selected and executed per request based on scope priority.
|
|
40
|
+
*
|
|
41
|
+
* @param input - The decision machine input containing user, dapp, and behavioral data
|
|
42
|
+
* @param logger - Optional logger for warnings and errors
|
|
43
|
+
* @returns A decision (allow/deny) with optional metadata
|
|
44
|
+
*/
|
|
45
|
+
async decide(input, logger) {
|
|
46
|
+
try {
|
|
47
|
+
const artifact = await this.selectArtifact(input.dappAccount, DecisionMachineKind.Decision, input.captchaType);
|
|
48
|
+
if (!artifact) return DEFAULT_DECISION;
|
|
49
|
+
if (artifact.runtime !== DecisionMachineRuntime.Node) {
|
|
50
|
+
logger?.warn?.(() => ({
|
|
51
|
+
msg: "Unsupported decision machine runtime, defaulting to allow",
|
|
52
|
+
data: { runtime: artifact.runtime }
|
|
53
|
+
}));
|
|
54
|
+
return DEFAULT_DECISION;
|
|
55
|
+
}
|
|
56
|
+
return await this.runArtifactExport(artifact, ["decide", "default"], { input: {
|
|
57
|
+
...input,
|
|
58
|
+
phase: input.phase ?? "verify"
|
|
59
|
+
} }, DecisionMachineOutputSchema) ?? DEFAULT_DECISION;
|
|
60
|
+
} catch (error) {
|
|
61
|
+
logger?.error?.(() => ({
|
|
62
|
+
msg: "Decision machine failed, defaulting to allow",
|
|
63
|
+
err: error,
|
|
64
|
+
data: { dappAccount: input.dappAccount }
|
|
65
|
+
}));
|
|
66
|
+
return DEFAULT_DECISION;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Routing phase: ask the configured machine which concrete captcha type to
|
|
71
|
+
* serve. Returns undefined when no machine is configured, the machine has
|
|
72
|
+
* no route export, the machine throws or times out, or the output fails
|
|
73
|
+
* schema validation — caller should fall back to its baseline in any of
|
|
74
|
+
* these cases.
|
|
75
|
+
*/
|
|
76
|
+
async route(input, logger) {
|
|
77
|
+
try {
|
|
78
|
+
const artifact = await this.selectArtifact(input.dappAccount, DecisionMachineKind.Routing);
|
|
79
|
+
if (!artifact) return void 0;
|
|
80
|
+
if (artifact.runtime !== DecisionMachineRuntime.Node) {
|
|
81
|
+
logger?.warn?.(() => ({
|
|
82
|
+
msg: "Unsupported routing machine runtime, falling back to baseline",
|
|
83
|
+
data: { runtime: artifact.runtime }
|
|
84
|
+
}));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
return await this.runArtifactExport(artifact, ["route"], {
|
|
88
|
+
input,
|
|
89
|
+
optional: true
|
|
90
|
+
}, RoutingMachineOutputSchema);
|
|
91
|
+
} catch (error) {
|
|
92
|
+
logger?.error?.(() => ({
|
|
93
|
+
msg: "Routing machine failed, falling back to baseline",
|
|
94
|
+
err: error,
|
|
95
|
+
data: { dappAccount: input.dappAccount }
|
|
96
|
+
}));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Pre-fetch hook: ask the configured machine which counters it needs read
|
|
102
|
+
* into its input before {@link route} is invoked. Returns [] when no machine
|
|
103
|
+
* is configured or it doesn't declare any.
|
|
104
|
+
*/
|
|
105
|
+
async getRequiredCounters(input, logger) {
|
|
106
|
+
try {
|
|
107
|
+
const artifact = await this.selectArtifact(input.dappAccount, DecisionMachineKind.Routing);
|
|
108
|
+
if (!artifact) return [];
|
|
109
|
+
if (artifact.runtime !== DecisionMachineRuntime.Node) return [];
|
|
110
|
+
return await this.runArtifactExport(artifact, ["requiredCounters"], {
|
|
111
|
+
input,
|
|
112
|
+
optional: true
|
|
113
|
+
}, RequiredCountersSchema) ?? [];
|
|
114
|
+
} catch (error) {
|
|
115
|
+
logger?.warn?.(() => ({
|
|
116
|
+
msg: "requiredCounters() failed, proceeding with no counters",
|
|
117
|
+
err: error,
|
|
118
|
+
data: { dappAccount: input.dappAccount }
|
|
119
|
+
}));
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Selects a single decision machine artifact based on scope priority.
|
|
125
|
+
* Currently supports two scopes with the following priority:
|
|
126
|
+
* 1. Dapp-specific: Custom decision machine for a specific dapp account
|
|
127
|
+
* 2. Global: Default decision machine applied to all dapps
|
|
128
|
+
*
|
|
129
|
+
* Decision machines can optionally specify a captchaType filter:
|
|
130
|
+
* - If artifact has no captchaType: runs on all captcha types
|
|
131
|
+
* - If artifact has captchaType: only runs on matching captcha type
|
|
132
|
+
*
|
|
133
|
+
* @param dappAccount - The dapp account identifier to check for dapp-specific artifacts
|
|
134
|
+
* @param captchaType - The captcha type to filter by (optional)
|
|
135
|
+
* @returns The single highest-priority artifact, or undefined if none exists
|
|
136
|
+
*/
|
|
137
|
+
async selectArtifact(dappAccount, kind, captchaType) {
|
|
138
|
+
const cachedDapp = this.getCachedArtifact(DecisionMachineScope.Dapp, kind, dappAccount);
|
|
139
|
+
const cachedGlobal = this.getCachedArtifact(DecisionMachineScope.Global, kind);
|
|
140
|
+
if (cachedDapp !== null && cachedGlobal !== null) {
|
|
141
|
+
if (cachedDapp && this.matchesCaptchaType(cachedDapp, captchaType)) return cachedDapp;
|
|
142
|
+
if (cachedGlobal && this.matchesCaptchaType(cachedGlobal, captchaType)) return cachedGlobal;
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const [dappArtifact, globalArtifact] = await Promise.all([cachedDapp !== null ? Promise.resolve(cachedDapp) : this.db.getDecisionMachineArtifact(DecisionMachineScope.Dapp, dappAccount, kind).then((a) => {
|
|
146
|
+
this.setCachedArtifact(DecisionMachineScope.Dapp, kind, dappAccount, a ?? void 0);
|
|
147
|
+
return a ?? void 0;
|
|
148
|
+
}), cachedGlobal !== null ? Promise.resolve(cachedGlobal) : this.db.getDecisionMachineArtifact(DecisionMachineScope.Global, void 0, kind).then((a) => {
|
|
149
|
+
this.setCachedArtifact(DecisionMachineScope.Global, kind, void 0, a ?? void 0);
|
|
150
|
+
return a ?? void 0;
|
|
151
|
+
})]);
|
|
152
|
+
if (dappArtifact && this.matchesCaptchaType(dappArtifact, captchaType)) return dappArtifact;
|
|
153
|
+
if (globalArtifact && this.matchesCaptchaType(globalArtifact, captchaType)) return globalArtifact;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Checks if a decision machine artifact matches the requested captcha type.
|
|
157
|
+
* - If artifact has no captchaType filter: matches all captcha types
|
|
158
|
+
* - If artifact has captchaType filter: only matches if types are equal
|
|
159
|
+
*
|
|
160
|
+
* @param artifact - The decision machine artifact to check
|
|
161
|
+
* @param captchaType - The captcha type to match against (optional)
|
|
162
|
+
* @returns True if the artifact should run for this captcha type
|
|
163
|
+
*/
|
|
164
|
+
matchesCaptchaType(artifact, captchaType) {
|
|
165
|
+
if (!artifact.captchaType) return true;
|
|
166
|
+
return artifact.captchaType === captchaType;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Load the artifact source into a fresh vm sandbox, locate a callable
|
|
170
|
+
* matching one of {exportNames} (or `module.exports` itself if it's a
|
|
171
|
+
* function), invoke it with {input}, and validate the result with
|
|
172
|
+
* {schema}. Returns undefined when {options.optional} is true and no
|
|
173
|
+
* matching export exists. Otherwise throws on missing export, invalid
|
|
174
|
+
* output, or sandbox failure.
|
|
175
|
+
*/
|
|
176
|
+
async runArtifactExport(artifact, exportNames, options, schema) {
|
|
177
|
+
const sandbox = {
|
|
178
|
+
module: { exports: {} },
|
|
179
|
+
exports: {}
|
|
180
|
+
};
|
|
181
|
+
const context = vm.createContext(sandbox);
|
|
182
|
+
new vm.Script(artifact.source, { filename: "decision-machine.js" }).runInContext(context, { timeout: LOAD_TIMEOUT_MS });
|
|
183
|
+
const exported = sandbox.module.exports;
|
|
184
|
+
const named = this.findExport(exported, exportNames);
|
|
185
|
+
if (!named) {
|
|
186
|
+
if (options.optional) return void 0;
|
|
187
|
+
throw new Error(`Decision machine must export one of: ${exportNames.join(", ")}`);
|
|
188
|
+
}
|
|
189
|
+
const result = await this.withTimeout(Promise.resolve(named.fn(options.input)), EXEC_TIMEOUT_MS);
|
|
190
|
+
const parsed = schema.safeParse(result);
|
|
191
|
+
if (!parsed.success) throw new Error(`Decision machine '${named.name}' output failed validation`);
|
|
192
|
+
return parsed.data;
|
|
193
|
+
}
|
|
194
|
+
findExport(exported, exportNames) {
|
|
195
|
+
if (typeof exported === "function" && (exportNames.includes("default") || exportNames.includes("decide"))) return {
|
|
196
|
+
name: "default",
|
|
197
|
+
fn: exported
|
|
198
|
+
};
|
|
199
|
+
for (const name of exportNames) {
|
|
200
|
+
const candidate = exported?.[name];
|
|
201
|
+
if (typeof candidate === "function") return {
|
|
202
|
+
name,
|
|
203
|
+
fn: candidate
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
async withTimeout(promise, timeoutMs) {
|
|
208
|
+
let timeoutId;
|
|
209
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
210
|
+
timeoutId = setTimeout(() => reject(/* @__PURE__ */ new Error("Decision machine timeout")), timeoutMs);
|
|
211
|
+
});
|
|
212
|
+
try {
|
|
213
|
+
return await Promise.race([promise, timeoutPromise]);
|
|
214
|
+
} finally {
|
|
215
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
309
218
|
};
|
|
219
|
+
//#endregion
|
|
220
|
+
export { DecisionMachineRunner };
|