@prosopo/provider 4.15.14 → 4.15.15
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 +49 -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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
);
|
|
27
|
-
await this.db?.storeDataset(dataset);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
1
|
+
const require_datasetTasksUtils = require("./datasetTasksUtils.cjs");
|
|
2
|
+
let _prosopo_datasets = require("@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 = (0, _prosopo_datasets.parseCaptchaDataset)(file);
|
|
18
|
+
return await this.providerSetDataset(datasetRaw);
|
|
19
|
+
}
|
|
20
|
+
async providerSetDataset(datasetRaw) {
|
|
21
|
+
const dataset = await require_datasetTasksUtils.providerValidateDataset(datasetRaw, this.captchaConfig.solved.count, this.captchaConfig.unsolved.count);
|
|
22
|
+
await this.db?.storeDataset(dataset);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
//#endregion
|
|
30
26
|
exports.DatasetManager = DatasetManager;
|
|
@@ -1,34 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
if (solutions < minUnsolvedCaptchas) {
|
|
18
|
-
throw new common.ProsopoEnvError("DATASET.SOLUTIONS_COUNT_LESS_THAN_CONFIGURED", {
|
|
19
|
-
context: { failedFuncName: providerValidateDataset.name }
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
const dataset = await datasets.buildDataset(datasetRaw);
|
|
23
|
-
if (!dataset.datasetId || !dataset.datasetContentId) {
|
|
24
|
-
throw new common.ProsopoEnvError("DATASET.DATASET_ID_UNDEFINED", {
|
|
25
|
-
context: {
|
|
26
|
-
failedFuncName: providerValidateDataset.name,
|
|
27
|
-
datasetId: dataset.datasetId,
|
|
28
|
-
datasetContentId: dataset.datasetContentId
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
return dataset;
|
|
1
|
+
let _prosopo_common = require("@prosopo/common");
|
|
2
|
+
let _prosopo_datasets = require("@prosopo/datasets");
|
|
3
|
+
//#region src/tasks/dataset/datasetTasksUtils.ts
|
|
4
|
+
var providerValidateDataset = async (datasetRaw, minSolvedCaptchas, minUnsolvedCaptchas) => {
|
|
5
|
+
if (datasetRaw.captchas.length < minSolvedCaptchas + minUnsolvedCaptchas) throw new _prosopo_common.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 _prosopo_common.ProsopoEnvError("DATASET.SOLUTIONS_COUNT_LESS_THAN_CONFIGURED", { context: { failedFuncName: providerValidateDataset.name } });
|
|
8
|
+
if (solutions < minUnsolvedCaptchas) throw new _prosopo_common.ProsopoEnvError("DATASET.SOLUTIONS_COUNT_LESS_THAN_CONFIGURED", { context: { failedFuncName: providerValidateDataset.name } });
|
|
9
|
+
const dataset = await (0, _prosopo_datasets.buildDataset)(datasetRaw);
|
|
10
|
+
if (!dataset.datasetId || !dataset.datasetContentId) throw new _prosopo_common.ProsopoEnvError("DATASET.DATASET_ID_UNDEFINED", { context: {
|
|
11
|
+
failedFuncName: providerValidateDataset.name,
|
|
12
|
+
datasetId: dataset.datasetId,
|
|
13
|
+
datasetContentId: dataset.datasetContentId
|
|
14
|
+
} });
|
|
15
|
+
return dataset;
|
|
33
16
|
};
|
|
17
|
+
//#endregion
|
|
34
18
|
exports.providerValidateDataset = providerValidateDataset;
|
|
@@ -1,309 +1,222 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
const require_runtime = require("../../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
let _prosopo_types = require("@prosopo/types");
|
|
3
|
+
let node_vm = require("node:vm");
|
|
4
|
+
node_vm = require_runtime.__toESM(node_vm, 1);
|
|
5
|
+
let zod = require("zod");
|
|
6
|
+
//#region src/tasks/decisionMachine/decisionMachineRunner.ts
|
|
7
|
+
var LOAD_TIMEOUT_MS = Number.parseInt(process.env.DECISION_MACHINE_LOAD_TIMEOUT_MS ?? "", 10) || 1e3;
|
|
8
|
+
var EXEC_TIMEOUT_MS = Number.parseInt(process.env.DECISION_MACHINE_EXEC_TIMEOUT_MS ?? "", 10) || 2e3;
|
|
9
|
+
/** How long cached artifacts are considered fresh (ms). */
|
|
10
|
+
var ARTIFACT_CACHE_TTL_MS = Number.parseInt(process.env.DECISION_MACHINE_ARTIFACT_CACHE_TTL_MS ?? "", 10) || 300 * 1e3;
|
|
11
|
+
var DEFAULT_DECISION = { decision: _prosopo_types.DecisionMachineDecision.Allow };
|
|
12
|
+
var RequiredCountersSchema = zod.z.array(_prosopo_types.CounterSpecSchema);
|
|
13
|
+
var DecisionMachineRunner = class DecisionMachineRunner {
|
|
14
|
+
constructor(db) {
|
|
15
|
+
this.db = db;
|
|
16
|
+
this.artifactCache = /* @__PURE__ */ new Map();
|
|
17
|
+
}
|
|
18
|
+
/** Build a cache key for a given scope + kind + dappAccount tuple. */
|
|
19
|
+
static cacheKey(scope, kind, dappAccount) {
|
|
20
|
+
return `${scope}:${kind}:${dappAccount ?? ""}`;
|
|
21
|
+
}
|
|
22
|
+
/** Return a cached artifact if still fresh, or undefined. */
|
|
23
|
+
getCachedArtifact(scope, kind, dappAccount) {
|
|
24
|
+
const entry = this.artifactCache.get(DecisionMachineRunner.cacheKey(scope, kind, dappAccount));
|
|
25
|
+
if (!entry) return null;
|
|
26
|
+
if (Date.now() - entry.cachedAt > ARTIFACT_CACHE_TTL_MS) {
|
|
27
|
+
this.artifactCache.delete(DecisionMachineRunner.cacheKey(scope, kind, dappAccount));
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
return entry.artifact;
|
|
31
|
+
}
|
|
32
|
+
/** Store an artifact (or undefined for negative cache) in the cache. */
|
|
33
|
+
setCachedArtifact(scope, kind, dappAccount, artifact) {
|
|
34
|
+
this.artifactCache.set(DecisionMachineRunner.cacheKey(scope, kind, dappAccount), {
|
|
35
|
+
artifact,
|
|
36
|
+
cachedAt: Date.now()
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Evaluates a single decision machine artifact and returns a decision.
|
|
41
|
+
* Only one decision machine is selected and executed per request based on scope priority.
|
|
42
|
+
*
|
|
43
|
+
* @param input - The decision machine input containing user, dapp, and behavioral data
|
|
44
|
+
* @param logger - Optional logger for warnings and errors
|
|
45
|
+
* @returns A decision (allow/deny) with optional metadata
|
|
46
|
+
*/
|
|
47
|
+
async decide(input, logger) {
|
|
48
|
+
try {
|
|
49
|
+
const artifact = await this.selectArtifact(input.dappAccount, _prosopo_types.DecisionMachineKind.Decision, input.captchaType);
|
|
50
|
+
if (!artifact) return DEFAULT_DECISION;
|
|
51
|
+
if (artifact.runtime !== _prosopo_types.DecisionMachineRuntime.Node) {
|
|
52
|
+
logger?.warn?.(() => ({
|
|
53
|
+
msg: "Unsupported decision machine runtime, defaulting to allow",
|
|
54
|
+
data: { runtime: artifact.runtime }
|
|
55
|
+
}));
|
|
56
|
+
return DEFAULT_DECISION;
|
|
57
|
+
}
|
|
58
|
+
return await this.runArtifactExport(artifact, ["decide", "default"], { input: {
|
|
59
|
+
...input,
|
|
60
|
+
phase: input.phase ?? "verify"
|
|
61
|
+
} }, _prosopo_types.DecisionMachineOutputSchema) ?? DEFAULT_DECISION;
|
|
62
|
+
} catch (error) {
|
|
63
|
+
logger?.error?.(() => ({
|
|
64
|
+
msg: "Decision machine failed, defaulting to allow",
|
|
65
|
+
err: error,
|
|
66
|
+
data: { dappAccount: input.dappAccount }
|
|
67
|
+
}));
|
|
68
|
+
return DEFAULT_DECISION;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Routing phase: ask the configured machine which concrete captcha type to
|
|
73
|
+
* serve. Returns undefined when no machine is configured, the machine has
|
|
74
|
+
* no route export, the machine throws or times out, or the output fails
|
|
75
|
+
* schema validation — caller should fall back to its baseline in any of
|
|
76
|
+
* these cases.
|
|
77
|
+
*/
|
|
78
|
+
async route(input, logger) {
|
|
79
|
+
try {
|
|
80
|
+
const artifact = await this.selectArtifact(input.dappAccount, _prosopo_types.DecisionMachineKind.Routing);
|
|
81
|
+
if (!artifact) return void 0;
|
|
82
|
+
if (artifact.runtime !== _prosopo_types.DecisionMachineRuntime.Node) {
|
|
83
|
+
logger?.warn?.(() => ({
|
|
84
|
+
msg: "Unsupported routing machine runtime, falling back to baseline",
|
|
85
|
+
data: { runtime: artifact.runtime }
|
|
86
|
+
}));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
return await this.runArtifactExport(artifact, ["route"], {
|
|
90
|
+
input,
|
|
91
|
+
optional: true
|
|
92
|
+
}, _prosopo_types.RoutingMachineOutputSchema);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
logger?.error?.(() => ({
|
|
95
|
+
msg: "Routing machine failed, falling back to baseline",
|
|
96
|
+
err: error,
|
|
97
|
+
data: { dappAccount: input.dappAccount }
|
|
98
|
+
}));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Pre-fetch hook: ask the configured machine which counters it needs read
|
|
104
|
+
* into its input before {@link route} is invoked. Returns [] when no machine
|
|
105
|
+
* is configured or it doesn't declare any.
|
|
106
|
+
*/
|
|
107
|
+
async getRequiredCounters(input, logger) {
|
|
108
|
+
try {
|
|
109
|
+
const artifact = await this.selectArtifact(input.dappAccount, _prosopo_types.DecisionMachineKind.Routing);
|
|
110
|
+
if (!artifact) return [];
|
|
111
|
+
if (artifact.runtime !== _prosopo_types.DecisionMachineRuntime.Node) return [];
|
|
112
|
+
return await this.runArtifactExport(artifact, ["requiredCounters"], {
|
|
113
|
+
input,
|
|
114
|
+
optional: true
|
|
115
|
+
}, RequiredCountersSchema) ?? [];
|
|
116
|
+
} catch (error) {
|
|
117
|
+
logger?.warn?.(() => ({
|
|
118
|
+
msg: "requiredCounters() failed, proceeding with no counters",
|
|
119
|
+
err: error,
|
|
120
|
+
data: { dappAccount: input.dappAccount }
|
|
121
|
+
}));
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Selects a single decision machine artifact based on scope priority.
|
|
127
|
+
* Currently supports two scopes with the following priority:
|
|
128
|
+
* 1. Dapp-specific: Custom decision machine for a specific dapp account
|
|
129
|
+
* 2. Global: Default decision machine applied to all dapps
|
|
130
|
+
*
|
|
131
|
+
* Decision machines can optionally specify a captchaType filter:
|
|
132
|
+
* - If artifact has no captchaType: runs on all captcha types
|
|
133
|
+
* - If artifact has captchaType: only runs on matching captcha type
|
|
134
|
+
*
|
|
135
|
+
* @param dappAccount - The dapp account identifier to check for dapp-specific artifacts
|
|
136
|
+
* @param captchaType - The captcha type to filter by (optional)
|
|
137
|
+
* @returns The single highest-priority artifact, or undefined if none exists
|
|
138
|
+
*/
|
|
139
|
+
async selectArtifact(dappAccount, kind, captchaType) {
|
|
140
|
+
const cachedDapp = this.getCachedArtifact(_prosopo_types.DecisionMachineScope.Dapp, kind, dappAccount);
|
|
141
|
+
const cachedGlobal = this.getCachedArtifact(_prosopo_types.DecisionMachineScope.Global, kind);
|
|
142
|
+
if (cachedDapp !== null && cachedGlobal !== null) {
|
|
143
|
+
if (cachedDapp && this.matchesCaptchaType(cachedDapp, captchaType)) return cachedDapp;
|
|
144
|
+
if (cachedGlobal && this.matchesCaptchaType(cachedGlobal, captchaType)) return cachedGlobal;
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const [dappArtifact, globalArtifact] = await Promise.all([cachedDapp !== null ? Promise.resolve(cachedDapp) : this.db.getDecisionMachineArtifact(_prosopo_types.DecisionMachineScope.Dapp, dappAccount, kind).then((a) => {
|
|
148
|
+
this.setCachedArtifact(_prosopo_types.DecisionMachineScope.Dapp, kind, dappAccount, a ?? void 0);
|
|
149
|
+
return a ?? void 0;
|
|
150
|
+
}), cachedGlobal !== null ? Promise.resolve(cachedGlobal) : this.db.getDecisionMachineArtifact(_prosopo_types.DecisionMachineScope.Global, void 0, kind).then((a) => {
|
|
151
|
+
this.setCachedArtifact(_prosopo_types.DecisionMachineScope.Global, kind, void 0, a ?? void 0);
|
|
152
|
+
return a ?? void 0;
|
|
153
|
+
})]);
|
|
154
|
+
if (dappArtifact && this.matchesCaptchaType(dappArtifact, captchaType)) return dappArtifact;
|
|
155
|
+
if (globalArtifact && this.matchesCaptchaType(globalArtifact, captchaType)) return globalArtifact;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Checks if a decision machine artifact matches the requested captcha type.
|
|
159
|
+
* - If artifact has no captchaType filter: matches all captcha types
|
|
160
|
+
* - If artifact has captchaType filter: only matches if types are equal
|
|
161
|
+
*
|
|
162
|
+
* @param artifact - The decision machine artifact to check
|
|
163
|
+
* @param captchaType - The captcha type to match against (optional)
|
|
164
|
+
* @returns True if the artifact should run for this captcha type
|
|
165
|
+
*/
|
|
166
|
+
matchesCaptchaType(artifact, captchaType) {
|
|
167
|
+
if (!artifact.captchaType) return true;
|
|
168
|
+
return artifact.captchaType === captchaType;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Load the artifact source into a fresh vm sandbox, locate a callable
|
|
172
|
+
* matching one of {exportNames} (or `module.exports` itself if it's a
|
|
173
|
+
* function), invoke it with {input}, and validate the result with
|
|
174
|
+
* {schema}. Returns undefined when {options.optional} is true and no
|
|
175
|
+
* matching export exists. Otherwise throws on missing export, invalid
|
|
176
|
+
* output, or sandbox failure.
|
|
177
|
+
*/
|
|
178
|
+
async runArtifactExport(artifact, exportNames, options, schema) {
|
|
179
|
+
const sandbox = {
|
|
180
|
+
module: { exports: {} },
|
|
181
|
+
exports: {}
|
|
182
|
+
};
|
|
183
|
+
const context = node_vm.default.createContext(sandbox);
|
|
184
|
+
new node_vm.default.Script(artifact.source, { filename: "decision-machine.js" }).runInContext(context, { timeout: LOAD_TIMEOUT_MS });
|
|
185
|
+
const exported = sandbox.module.exports;
|
|
186
|
+
const named = this.findExport(exported, exportNames);
|
|
187
|
+
if (!named) {
|
|
188
|
+
if (options.optional) return void 0;
|
|
189
|
+
throw new Error(`Decision machine must export one of: ${exportNames.join(", ")}`);
|
|
190
|
+
}
|
|
191
|
+
const result = await this.withTimeout(Promise.resolve(named.fn(options.input)), EXEC_TIMEOUT_MS);
|
|
192
|
+
const parsed = schema.safeParse(result);
|
|
193
|
+
if (!parsed.success) throw new Error(`Decision machine '${named.name}' output failed validation`);
|
|
194
|
+
return parsed.data;
|
|
195
|
+
}
|
|
196
|
+
findExport(exported, exportNames) {
|
|
197
|
+
if (typeof exported === "function" && (exportNames.includes("default") || exportNames.includes("decide"))) return {
|
|
198
|
+
name: "default",
|
|
199
|
+
fn: exported
|
|
200
|
+
};
|
|
201
|
+
for (const name of exportNames) {
|
|
202
|
+
const candidate = exported?.[name];
|
|
203
|
+
if (typeof candidate === "function") return {
|
|
204
|
+
name,
|
|
205
|
+
fn: candidate
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
async withTimeout(promise, timeoutMs) {
|
|
210
|
+
let timeoutId;
|
|
211
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
212
|
+
timeoutId = setTimeout(() => reject(/* @__PURE__ */ new Error("Decision machine timeout")), timeoutMs);
|
|
213
|
+
});
|
|
214
|
+
try {
|
|
215
|
+
return await Promise.race([promise, timeoutPromise]);
|
|
216
|
+
} finally {
|
|
217
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
14
220
|
};
|
|
15
|
-
|
|
16
|
-
class DecisionMachineRunner {
|
|
17
|
-
constructor(db) {
|
|
18
|
-
this.db = db;
|
|
19
|
-
this.artifactCache = /* @__PURE__ */ new Map();
|
|
20
|
-
}
|
|
21
|
-
/** Build a cache key for a given scope + kind + dappAccount tuple. */
|
|
22
|
-
static cacheKey(scope, kind, dappAccount) {
|
|
23
|
-
return `${scope}:${kind}:${dappAccount ?? ""}`;
|
|
24
|
-
}
|
|
25
|
-
/** Return a cached artifact if still fresh, or undefined. */
|
|
26
|
-
getCachedArtifact(scope, kind, dappAccount) {
|
|
27
|
-
const entry = this.artifactCache.get(
|
|
28
|
-
DecisionMachineRunner.cacheKey(scope, kind, dappAccount)
|
|
29
|
-
);
|
|
30
|
-
if (!entry) return null;
|
|
31
|
-
if (Date.now() - entry.cachedAt > ARTIFACT_CACHE_TTL_MS) {
|
|
32
|
-
this.artifactCache.delete(
|
|
33
|
-
DecisionMachineRunner.cacheKey(scope, kind, dappAccount)
|
|
34
|
-
);
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
return entry.artifact;
|
|
38
|
-
}
|
|
39
|
-
/** Store an artifact (or undefined for negative cache) in the cache. */
|
|
40
|
-
setCachedArtifact(scope, kind, dappAccount, artifact) {
|
|
41
|
-
this.artifactCache.set(
|
|
42
|
-
DecisionMachineRunner.cacheKey(scope, kind, dappAccount),
|
|
43
|
-
{
|
|
44
|
-
artifact,
|
|
45
|
-
cachedAt: Date.now()
|
|
46
|
-
}
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Evaluates a single decision machine artifact and returns a decision.
|
|
51
|
-
* Only one decision machine is selected and executed per request based on scope priority.
|
|
52
|
-
*
|
|
53
|
-
* @param input - The decision machine input containing user, dapp, and behavioral data
|
|
54
|
-
* @param logger - Optional logger for warnings and errors
|
|
55
|
-
* @returns A decision (allow/deny) with optional metadata
|
|
56
|
-
*/
|
|
57
|
-
async decide(input, logger) {
|
|
58
|
-
try {
|
|
59
|
-
const artifact = await this.selectArtifact(
|
|
60
|
-
input.dappAccount,
|
|
61
|
-
types.DecisionMachineKind.Decision,
|
|
62
|
-
input.captchaType
|
|
63
|
-
);
|
|
64
|
-
if (!artifact) {
|
|
65
|
-
return DEFAULT_DECISION;
|
|
66
|
-
}
|
|
67
|
-
if (artifact.runtime !== types.DecisionMachineRuntime.Node) {
|
|
68
|
-
logger?.warn?.(() => ({
|
|
69
|
-
msg: "Unsupported decision machine runtime, defaulting to allow",
|
|
70
|
-
data: { runtime: artifact.runtime }
|
|
71
|
-
}));
|
|
72
|
-
return DEFAULT_DECISION;
|
|
73
|
-
}
|
|
74
|
-
const decision = await this.runArtifactExport(
|
|
75
|
-
artifact,
|
|
76
|
-
["decide", "default"],
|
|
77
|
-
{ input: { ...input, phase: input.phase ?? "verify" } },
|
|
78
|
-
types.DecisionMachineOutputSchema
|
|
79
|
-
);
|
|
80
|
-
return decision ?? DEFAULT_DECISION;
|
|
81
|
-
} catch (error) {
|
|
82
|
-
logger?.error?.(() => ({
|
|
83
|
-
msg: "Decision machine failed, defaulting to allow",
|
|
84
|
-
err: error,
|
|
85
|
-
data: { dappAccount: input.dappAccount }
|
|
86
|
-
}));
|
|
87
|
-
return DEFAULT_DECISION;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Routing phase: ask the configured machine which concrete captcha type to
|
|
92
|
-
* serve. Returns undefined when no machine is configured, the machine has
|
|
93
|
-
* no route export, the machine throws or times out, or the output fails
|
|
94
|
-
* schema validation — caller should fall back to its baseline in any of
|
|
95
|
-
* these cases.
|
|
96
|
-
*/
|
|
97
|
-
async route(input, logger) {
|
|
98
|
-
try {
|
|
99
|
-
const artifact = await this.selectArtifact(
|
|
100
|
-
input.dappAccount,
|
|
101
|
-
types.DecisionMachineKind.Routing
|
|
102
|
-
);
|
|
103
|
-
if (!artifact) return void 0;
|
|
104
|
-
if (artifact.runtime !== types.DecisionMachineRuntime.Node) {
|
|
105
|
-
logger?.warn?.(() => ({
|
|
106
|
-
msg: "Unsupported routing machine runtime, falling back to baseline",
|
|
107
|
-
data: { runtime: artifact.runtime }
|
|
108
|
-
}));
|
|
109
|
-
return void 0;
|
|
110
|
-
}
|
|
111
|
-
return await this.runArtifactExport(
|
|
112
|
-
artifact,
|
|
113
|
-
["route"],
|
|
114
|
-
{ input, optional: true },
|
|
115
|
-
types.RoutingMachineOutputSchema
|
|
116
|
-
);
|
|
117
|
-
} catch (error) {
|
|
118
|
-
logger?.error?.(() => ({
|
|
119
|
-
msg: "Routing machine failed, falling back to baseline",
|
|
120
|
-
err: error,
|
|
121
|
-
data: { dappAccount: input.dappAccount }
|
|
122
|
-
}));
|
|
123
|
-
return void 0;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Pre-fetch hook: ask the configured machine which counters it needs read
|
|
128
|
-
* into its input before {@link route} is invoked. Returns [] when no machine
|
|
129
|
-
* is configured or it doesn't declare any.
|
|
130
|
-
*/
|
|
131
|
-
async getRequiredCounters(input, logger) {
|
|
132
|
-
try {
|
|
133
|
-
const artifact = await this.selectArtifact(
|
|
134
|
-
input.dappAccount,
|
|
135
|
-
types.DecisionMachineKind.Routing
|
|
136
|
-
);
|
|
137
|
-
if (!artifact) return [];
|
|
138
|
-
if (artifact.runtime !== types.DecisionMachineRuntime.Node) return [];
|
|
139
|
-
const specs = await this.runArtifactExport(
|
|
140
|
-
artifact,
|
|
141
|
-
["requiredCounters"],
|
|
142
|
-
{ input, optional: true },
|
|
143
|
-
RequiredCountersSchema
|
|
144
|
-
);
|
|
145
|
-
return specs ?? [];
|
|
146
|
-
} catch (error) {
|
|
147
|
-
logger?.warn?.(() => ({
|
|
148
|
-
msg: "requiredCounters() failed, proceeding with no counters",
|
|
149
|
-
err: error,
|
|
150
|
-
data: { dappAccount: input.dappAccount }
|
|
151
|
-
}));
|
|
152
|
-
return [];
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Selects a single decision machine artifact based on scope priority.
|
|
157
|
-
* Currently supports two scopes with the following priority:
|
|
158
|
-
* 1. Dapp-specific: Custom decision machine for a specific dapp account
|
|
159
|
-
* 2. Global: Default decision machine applied to all dapps
|
|
160
|
-
*
|
|
161
|
-
* Decision machines can optionally specify a captchaType filter:
|
|
162
|
-
* - If artifact has no captchaType: runs on all captcha types
|
|
163
|
-
* - If artifact has captchaType: only runs on matching captcha type
|
|
164
|
-
*
|
|
165
|
-
* @param dappAccount - The dapp account identifier to check for dapp-specific artifacts
|
|
166
|
-
* @param captchaType - The captcha type to filter by (optional)
|
|
167
|
-
* @returns The single highest-priority artifact, or undefined if none exists
|
|
168
|
-
*/
|
|
169
|
-
async selectArtifact(dappAccount, kind, captchaType) {
|
|
170
|
-
const cachedDapp = this.getCachedArtifact(
|
|
171
|
-
types.DecisionMachineScope.Dapp,
|
|
172
|
-
kind,
|
|
173
|
-
dappAccount
|
|
174
|
-
);
|
|
175
|
-
const cachedGlobal = this.getCachedArtifact(
|
|
176
|
-
types.DecisionMachineScope.Global,
|
|
177
|
-
kind
|
|
178
|
-
);
|
|
179
|
-
if (cachedDapp !== null && cachedGlobal !== null) {
|
|
180
|
-
if (cachedDapp && this.matchesCaptchaType(cachedDapp, captchaType)) {
|
|
181
|
-
return cachedDapp;
|
|
182
|
-
}
|
|
183
|
-
if (cachedGlobal && this.matchesCaptchaType(cachedGlobal, captchaType)) {
|
|
184
|
-
return cachedGlobal;
|
|
185
|
-
}
|
|
186
|
-
return void 0;
|
|
187
|
-
}
|
|
188
|
-
const [dappArtifact, globalArtifact] = await Promise.all([
|
|
189
|
-
cachedDapp !== null ? Promise.resolve(cachedDapp) : this.db.getDecisionMachineArtifact(
|
|
190
|
-
types.DecisionMachineScope.Dapp,
|
|
191
|
-
dappAccount,
|
|
192
|
-
kind
|
|
193
|
-
).then((a) => {
|
|
194
|
-
this.setCachedArtifact(
|
|
195
|
-
types.DecisionMachineScope.Dapp,
|
|
196
|
-
kind,
|
|
197
|
-
dappAccount,
|
|
198
|
-
a ?? void 0
|
|
199
|
-
);
|
|
200
|
-
return a ?? void 0;
|
|
201
|
-
}),
|
|
202
|
-
cachedGlobal !== null ? Promise.resolve(cachedGlobal) : this.db.getDecisionMachineArtifact(
|
|
203
|
-
types.DecisionMachineScope.Global,
|
|
204
|
-
void 0,
|
|
205
|
-
kind
|
|
206
|
-
).then((a) => {
|
|
207
|
-
this.setCachedArtifact(
|
|
208
|
-
types.DecisionMachineScope.Global,
|
|
209
|
-
kind,
|
|
210
|
-
void 0,
|
|
211
|
-
a ?? void 0
|
|
212
|
-
);
|
|
213
|
-
return a ?? void 0;
|
|
214
|
-
})
|
|
215
|
-
]);
|
|
216
|
-
if (dappArtifact && this.matchesCaptchaType(dappArtifact, captchaType)) {
|
|
217
|
-
return dappArtifact;
|
|
218
|
-
}
|
|
219
|
-
if (globalArtifact && this.matchesCaptchaType(globalArtifact, captchaType)) {
|
|
220
|
-
return globalArtifact;
|
|
221
|
-
}
|
|
222
|
-
return void 0;
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* Checks if a decision machine artifact matches the requested captcha type.
|
|
226
|
-
* - If artifact has no captchaType filter: matches all captcha types
|
|
227
|
-
* - If artifact has captchaType filter: only matches if types are equal
|
|
228
|
-
*
|
|
229
|
-
* @param artifact - The decision machine artifact to check
|
|
230
|
-
* @param captchaType - The captcha type to match against (optional)
|
|
231
|
-
* @returns True if the artifact should run for this captcha type
|
|
232
|
-
*/
|
|
233
|
-
matchesCaptchaType(artifact, captchaType) {
|
|
234
|
-
if (!artifact.captchaType) {
|
|
235
|
-
return true;
|
|
236
|
-
}
|
|
237
|
-
return artifact.captchaType === captchaType;
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* Load the artifact source into a fresh vm sandbox, locate a callable
|
|
241
|
-
* matching one of {exportNames} (or `module.exports` itself if it's a
|
|
242
|
-
* function), invoke it with {input}, and validate the result with
|
|
243
|
-
* {schema}. Returns undefined when {options.optional} is true and no
|
|
244
|
-
* matching export exists. Otherwise throws on missing export, invalid
|
|
245
|
-
* output, or sandbox failure.
|
|
246
|
-
*/
|
|
247
|
-
async runArtifactExport(artifact, exportNames, options, schema) {
|
|
248
|
-
const sandbox = {
|
|
249
|
-
module: { exports: {} },
|
|
250
|
-
exports: {}
|
|
251
|
-
};
|
|
252
|
-
const context = vm.createContext(sandbox);
|
|
253
|
-
const script = new vm.Script(artifact.source, {
|
|
254
|
-
filename: "decision-machine.js"
|
|
255
|
-
});
|
|
256
|
-
script.runInContext(context, { timeout: LOAD_TIMEOUT_MS });
|
|
257
|
-
const exported = sandbox.module.exports;
|
|
258
|
-
const named = this.findExport(exported, exportNames);
|
|
259
|
-
if (!named) {
|
|
260
|
-
if (options.optional) return void 0;
|
|
261
|
-
throw new Error(
|
|
262
|
-
`Decision machine must export one of: ${exportNames.join(", ")}`
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
|
-
const result = await this.withTimeout(
|
|
266
|
-
Promise.resolve(named.fn(options.input)),
|
|
267
|
-
EXEC_TIMEOUT_MS
|
|
268
|
-
);
|
|
269
|
-
const parsed = schema.safeParse(result);
|
|
270
|
-
if (!parsed.success) {
|
|
271
|
-
throw new Error(
|
|
272
|
-
`Decision machine '${named.name}' output failed validation`
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
return parsed.data;
|
|
276
|
-
}
|
|
277
|
-
findExport(exported, exportNames) {
|
|
278
|
-
if (typeof exported === "function" && (exportNames.includes("default") || exportNames.includes("decide"))) {
|
|
279
|
-
return {
|
|
280
|
-
name: "default",
|
|
281
|
-
fn: exported
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
for (const name of exportNames) {
|
|
285
|
-
const candidate = exported?.[name];
|
|
286
|
-
if (typeof candidate === "function") {
|
|
287
|
-
return { name, fn: candidate };
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
return void 0;
|
|
291
|
-
}
|
|
292
|
-
async withTimeout(promise, timeoutMs) {
|
|
293
|
-
let timeoutId;
|
|
294
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
295
|
-
timeoutId = setTimeout(
|
|
296
|
-
() => reject(new Error("Decision machine timeout")),
|
|
297
|
-
timeoutMs
|
|
298
|
-
);
|
|
299
|
-
});
|
|
300
|
-
try {
|
|
301
|
-
return await Promise.race([promise, timeoutPromise]);
|
|
302
|
-
} finally {
|
|
303
|
-
if (timeoutId) {
|
|
304
|
-
clearTimeout(timeoutId);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
221
|
+
//#endregion
|
|
309
222
|
exports.DecisionMachineRunner = DecisionMachineRunner;
|