@prosopo/provider 4.11.6 → 4.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build$colon$cjs.log +44 -41
- package/.turbo/turbo-build$colon$tsc.log +24 -24
- package/.turbo/turbo-build.log +45 -42
- package/CHANGELOG.md +148 -0
- package/dist/api/admin/apiClearAllCountersEndpoint.d.ts.map +1 -1
- package/dist/api/admin/apiClearAllCountersEndpoint.js +1 -1
- package/dist/api/admin/apiClearAllCountersEndpoint.js.map +1 -1
- package/dist/api/admin/apiDnsEventEndpoint.d.ts.map +1 -1
- package/dist/api/admin/apiDnsEventEndpoint.js +1 -1
- package/dist/api/admin/apiDnsEventEndpoint.js.map +1 -1
- package/dist/api/admin/apiGetAllDecisionMachinesEndpoint.d.ts.map +1 -1
- package/dist/api/admin/apiGetAllDecisionMachinesEndpoint.js +1 -1
- package/dist/api/admin/apiGetAllDecisionMachinesEndpoint.js.map +1 -1
- package/dist/api/admin/apiGetDecisionMachineEndpoint.d.ts.map +1 -1
- package/dist/api/admin/apiGetDecisionMachineEndpoint.js +1 -1
- package/dist/api/admin/apiGetDecisionMachineEndpoint.js.map +1 -1
- package/dist/api/admin/apiRegisterSiteKeyEndpoint.d.ts.map +1 -1
- package/dist/api/admin/apiRegisterSiteKeyEndpoint.js +1 -1
- package/dist/api/admin/apiRegisterSiteKeyEndpoint.js.map +1 -1
- package/dist/api/admin/apiRegisterSiteKeysEndpoint.d.ts.map +1 -1
- package/dist/api/admin/apiRegisterSiteKeysEndpoint.js +1 -1
- package/dist/api/admin/apiRegisterSiteKeysEndpoint.js.map +1 -1
- package/dist/api/admin/apiRemoveAllDecisionMachinesEndpoint.d.ts.map +1 -1
- package/dist/api/admin/apiRemoveAllDecisionMachinesEndpoint.js +1 -1
- package/dist/api/admin/apiRemoveAllDecisionMachinesEndpoint.js.map +1 -1
- package/dist/api/admin/apiRemoveDecisionMachineEndpoint.d.ts.map +1 -1
- package/dist/api/admin/apiRemoveDecisionMachineEndpoint.js +1 -1
- package/dist/api/admin/apiRemoveDecisionMachineEndpoint.js.map +1 -1
- package/dist/api/admin/apiRemoveDetectorKeyEndpoint.d.ts.map +1 -1
- package/dist/api/admin/apiRemoveDetectorKeyEndpoint.js +1 -2
- package/dist/api/admin/apiRemoveDetectorKeyEndpoint.js.map +1 -1
- package/dist/api/admin/apiRemoveSiteKeyEndpoint.d.ts.map +1 -1
- package/dist/api/admin/apiRemoveSiteKeyEndpoint.js +1 -1
- package/dist/api/admin/apiRemoveSiteKeyEndpoint.js.map +1 -1
- package/dist/api/admin/apiRemoveSiteKeysEndpoint.d.ts.map +1 -1
- package/dist/api/admin/apiRemoveSiteKeysEndpoint.js +1 -1
- package/dist/api/admin/apiRemoveSiteKeysEndpoint.js.map +1 -1
- package/dist/api/admin/apiToggleMaintenanceModeEndpoint.d.ts.map +1 -1
- package/dist/api/admin/apiToggleMaintenanceModeEndpoint.js +3 -1
- package/dist/api/admin/apiToggleMaintenanceModeEndpoint.js.map +1 -1
- package/dist/api/admin/apiUpdateDecisionMachineEndpoint.d.ts.map +1 -1
- package/dist/api/admin/apiUpdateDecisionMachineEndpoint.js +1 -1
- package/dist/api/admin/apiUpdateDecisionMachineEndpoint.js.map +1 -1
- package/dist/api/admin/apiUpdateDetectorKeyEndpoint.js +1 -2
- package/dist/api/admin/apiUpdateDetectorKeyEndpoint.js.map +1 -1
- package/dist/api/blacklistRequestInspector.d.ts.map +1 -1
- package/dist/api/blacklistRequestInspector.js +7 -4
- package/dist/api/blacklistRequestInspector.js.map +1 -1
- package/dist/api/captcha/checkSpamEmail.d.ts.map +1 -1
- package/dist/api/captcha/checkSpamEmail.js +19 -4
- package/dist/api/captcha/checkSpamEmail.js.map +1 -1
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.d.ts +0 -1
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.d.ts.map +1 -1
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.js +25 -6
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.js.map +1 -1
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.d.ts +0 -1
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.d.ts.map +1 -1
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.js +8 -21
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.js.map +1 -1
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.d.ts +1 -1
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.d.ts.map +1 -1
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js +68 -32
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js.map +1 -1
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.d.ts +0 -1
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.d.ts.map +1 -1
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.js +0 -2
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.js.map +1 -1
- package/dist/api/captcha/getImageCaptchaChallenge.d.ts.map +1 -1
- package/dist/api/captcha/getImageCaptchaChallenge.js +23 -1
- package/dist/api/captcha/getImageCaptchaChallenge.js.map +1 -1
- package/dist/api/captcha/getPoWCaptchaChallenge.d.ts.map +1 -1
- package/dist/api/captcha/getPoWCaptchaChallenge.js +3 -0
- package/dist/api/captcha/getPoWCaptchaChallenge.js.map +1 -1
- package/dist/api/captcha/getPuzzleCaptchaChallenge.d.ts.map +1 -1
- package/dist/api/captcha/getPuzzleCaptchaChallenge.js +3 -0
- package/dist/api/captcha/getPuzzleCaptchaChallenge.js.map +1 -1
- package/dist/api/captcha/submitPoWCaptchaSolution.js +0 -1
- package/dist/api/captcha/submitPoWCaptchaSolution.js.map +1 -1
- package/dist/api/captcha.d.ts +2 -1
- package/dist/api/captcha.d.ts.map +1 -1
- package/dist/api/captcha.js +12 -15
- package/dist/api/captcha.js.map +1 -1
- package/dist/api/domainMiddleware.d.ts.map +1 -1
- package/dist/api/domainMiddleware.js +4 -0
- package/dist/api/domainMiddleware.js.map +1 -1
- package/dist/api/forwardVerify.d.ts +14 -0
- package/dist/api/forwardVerify.d.ts.map +1 -0
- package/dist/api/forwardVerify.js +54 -0
- package/dist/api/forwardVerify.js.map +1 -0
- package/dist/api/headerCheckMiddleware.d.ts.map +1 -1
- package/dist/api/headerCheckMiddleware.js +7 -4
- package/dist/api/headerCheckMiddleware.js.map +1 -1
- package/dist/api/ja4.d.ts +5 -0
- package/dist/api/ja4.d.ts.map +1 -0
- package/dist/api/ja4.js +203 -0
- package/dist/api/ja4.js.map +1 -0
- package/dist/api/ja4Middleware.d.ts.map +1 -1
- package/dist/api/ja4Middleware.js +9 -18
- package/dist/api/ja4Middleware.js.map +1 -1
- package/dist/api/metrics.d.ts +40 -0
- package/dist/api/metrics.d.ts.map +1 -0
- package/dist/api/metrics.js +260 -0
- package/dist/api/metrics.js.map +1 -0
- package/dist/api/public.d.ts.map +1 -1
- package/dist/api/public.js +6 -1
- package/dist/api/public.js.map +1 -1
- package/dist/api/startProviderApi.d.ts.map +1 -1
- package/dist/api/startProviderApi.js +19 -22
- package/dist/api/startProviderApi.js.map +1 -1
- package/dist/api/verify.d.ts.map +1 -1
- package/dist/api/verify.js +85 -8
- package/dist/api/verify.js.map +1 -1
- package/dist/cjs/api/admin/apiClearAllCountersEndpoint.cjs +1 -1
- package/dist/cjs/api/admin/apiDnsEventEndpoint.cjs +1 -2
- package/dist/cjs/api/admin/apiGetAllDecisionMachinesEndpoint.cjs +1 -1
- package/dist/cjs/api/admin/apiGetDecisionMachineEndpoint.cjs +1 -1
- package/dist/cjs/api/admin/apiRegisterSiteKeyEndpoint.cjs +1 -2
- package/dist/cjs/api/admin/apiRegisterSiteKeysEndpoint.cjs +1 -2
- package/dist/cjs/api/admin/apiRemoveAllDecisionMachinesEndpoint.cjs +1 -1
- package/dist/cjs/api/admin/apiRemoveDecisionMachineEndpoint.cjs +1 -1
- package/dist/cjs/api/admin/apiRemoveDetectorKeyEndpoint.cjs +1 -3
- package/dist/cjs/api/admin/apiRemoveSiteKeyEndpoint.cjs +1 -2
- package/dist/cjs/api/admin/apiRemoveSiteKeysEndpoint.cjs +1 -2
- package/dist/cjs/api/admin/apiToggleMaintenanceModeEndpoint.cjs +3 -2
- package/dist/cjs/api/admin/apiUpdateDecisionMachineEndpoint.cjs +1 -1
- package/dist/cjs/api/admin/apiUpdateDetectorKeyEndpoint.cjs +1 -2
- package/dist/cjs/api/blacklistRequestInspector.cjs +7 -4
- package/dist/cjs/api/captcha/checkSpamEmail.cjs +18 -3
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.cjs +25 -6
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.cjs +8 -21
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/handler.cjs +70 -34
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.cjs +0 -2
- package/dist/cjs/api/captcha/getImageCaptchaChallenge.cjs +23 -1
- package/dist/cjs/api/captcha/getPoWCaptchaChallenge.cjs +3 -0
- package/dist/cjs/api/captcha/getPuzzleCaptchaChallenge.cjs +3 -0
- package/dist/cjs/api/captcha/submitPoWCaptchaSolution.cjs +0 -1
- package/dist/cjs/api/captcha.cjs +12 -15
- package/dist/cjs/api/domainMiddleware.cjs +4 -0
- package/dist/cjs/api/forwardVerify.cjs +54 -0
- package/dist/cjs/api/headerCheckMiddleware.cjs +7 -4
- package/dist/cjs/api/ja4.cjs +203 -0
- package/dist/cjs/api/ja4Middleware.cjs +11 -21
- package/dist/cjs/api/metrics.cjs +260 -0
- package/dist/cjs/api/public.cjs +6 -1
- package/dist/cjs/api/startProviderApi.cjs +19 -44
- package/dist/cjs/api/verify.cjs +83 -6
- package/dist/cjs/index.cjs +1 -0
- package/dist/cjs/tasks/captchaManager.cjs +1 -2
- package/dist/cjs/tasks/detection/getBotScore.cjs +1 -5
- package/dist/cjs/tasks/frictionless/frictionlessTasks.cjs +5 -63
- package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasks.cjs +20 -23
- package/dist/cjs/tasks/powCaptcha/powTasks.cjs +17 -24
- package/dist/cjs/tasks/puzzleCaptcha/puzzleTasks.cjs +15 -22
- package/dist/index.js +2 -1
- package/dist/tasks/captchaManager.js +1 -1
- package/dist/tasks/captchaManager.js.map +1 -1
- package/dist/tasks/detection/getBotScore.d.ts +0 -2
- package/dist/tasks/detection/getBotScore.d.ts.map +1 -1
- package/dist/tasks/detection/getBotScore.js +1 -5
- package/dist/tasks/detection/getBotScore.js.map +1 -1
- package/dist/tasks/frictionless/frictionlessTasks.d.ts +1 -11
- package/dist/tasks/frictionless/frictionlessTasks.d.ts.map +1 -1
- package/dist/tasks/frictionless/frictionlessTasks.js +5 -63
- package/dist/tasks/frictionless/frictionlessTasks.js.map +1 -1
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.js +20 -23
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.js.map +1 -1
- package/dist/tasks/powCaptcha/powTasks.d.ts.map +1 -1
- package/dist/tasks/powCaptcha/powTasks.js +17 -24
- package/dist/tasks/powCaptcha/powTasks.js.map +1 -1
- package/dist/tasks/puzzleCaptcha/puzzleTasks.d.ts.map +1 -1
- package/dist/tasks/puzzleCaptcha/puzzleTasks.js +15 -22
- package/dist/tasks/puzzleCaptcha/puzzleTasks.js.map +1 -1
- package/dist/tests/integration/api/blacklistRequestInspector.integration.test.js +2 -2
- package/dist/tests/integration/api/blacklistRequestInspector.integration.test.js.map +1 -1
- package/dist/tests/integration/clientSettingsPersistence.integration.test.js +1 -1
- package/dist/tests/integration/decisionMachines.integration.test.js +2 -2
- package/dist/tests/integration/decisionMachines.integration.test.js.map +1 -1
- package/dist/tests/integration/imgCaptcha.integration.test.js +2 -2
- package/dist/tests/integration/imgCaptcha.integration.test.js.map +1 -1
- package/dist/tests/integration/powCaptcha.integration.test.js +2 -2
- package/dist/tests/integration/powCaptcha.integration.test.js.map +1 -1
- package/dist/tests/integration/routingDecisionMachines.integration.test.js +0 -1
- package/dist/tests/integration/routingDecisionMachines.integration.test.js.map +1 -1
- package/dist/tests/unit/api/admin/apiClearAllCountersEndpoint.unit.test.js +1 -0
- package/dist/tests/unit/api/admin/apiClearAllCountersEndpoint.unit.test.js.map +1 -1
- package/dist/tests/unit/api/admin/apiDnsEventEndpoint.unit.test.js +5 -1
- package/dist/tests/unit/api/admin/apiDnsEventEndpoint.unit.test.js.map +1 -1
- package/dist/tests/unit/api/admin/apiRegisterSiteKeyEndpoint.unit.test.js +1 -0
- package/dist/tests/unit/api/admin/apiRegisterSiteKeyEndpoint.unit.test.js.map +1 -1
- package/dist/tests/unit/api/admin/apiRegisterSiteKeysEndpoint.unit.test.js +1 -0
- package/dist/tests/unit/api/admin/apiRegisterSiteKeysEndpoint.unit.test.js.map +1 -1
- package/dist/tests/unit/api/admin/apiRemoveDetectorKeyEndpoint.unit.test.js +1 -0
- package/dist/tests/unit/api/admin/apiRemoveDetectorKeyEndpoint.unit.test.js.map +1 -1
- package/dist/tests/unit/api/admin/apiToggleMaintenanceModeEndpoint.unit.test.js +1 -0
- package/dist/tests/unit/api/admin/apiToggleMaintenanceModeEndpoint.unit.test.js.map +1 -1
- package/dist/tests/unit/api/admin/apiUpdateDetectorKeyEndpoint.unit.test.js +1 -0
- package/dist/tests/unit/api/admin/apiUpdateDetectorKeyEndpoint.unit.test.js.map +1 -1
- package/dist/tests/unit/api/adminRoutes.unit.test.js +1 -0
- package/dist/tests/unit/api/adminRoutes.unit.test.js.map +1 -1
- package/dist/tests/unit/api/blacklistRequestInspector.unit.test.js +43 -1
- package/dist/tests/unit/api/blacklistRequestInspector.unit.test.js.map +1 -1
- package/dist/tests/unit/api/captcha/checkSpamEmail.unit.test.d.ts +2 -0
- package/dist/tests/unit/api/captcha/checkSpamEmail.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/api/captcha/checkSpamEmail.unit.test.js +62 -0
- package/dist/tests/unit/api/captcha/checkSpamEmail.unit.test.js.map +1 -0
- package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.unit.test.js +63 -1
- package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.unit.test.js.map +1 -1
- package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.unit.test.js +0 -75
- package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.unit.test.js.map +1 -1
- package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.unit.test.js +0 -1
- package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.unit.test.js.map +1 -1
- package/dist/tests/unit/api/captcha.unit.test.js +31 -1
- package/dist/tests/unit/api/captcha.unit.test.js.map +1 -1
- package/dist/tests/unit/api/forwardVerify.unit.test.d.ts +2 -0
- package/dist/tests/unit/api/forwardVerify.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/api/forwardVerify.unit.test.js +151 -0
- package/dist/tests/unit/api/forwardVerify.unit.test.js.map +1 -0
- package/dist/tests/unit/api/getFrictionlessCaptchaChallenge.unit.test.js +68 -20
- package/dist/tests/unit/api/getFrictionlessCaptchaChallenge.unit.test.js.map +1 -1
- package/dist/tests/unit/api/headerCheckMiddleware.unit.test.js +2 -2
- package/dist/tests/unit/api/headerCheckMiddleware.unit.test.js.map +1 -1
- package/dist/tests/unit/api/ja4.unit.test.d.ts +2 -0
- package/dist/tests/unit/api/ja4.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/api/ja4.unit.test.js +233 -0
- package/dist/tests/unit/api/ja4.unit.test.js.map +1 -0
- package/dist/tests/unit/api/ja4Middleware.unit.test.js +11 -8
- package/dist/tests/unit/api/ja4Middleware.unit.test.js.map +1 -1
- package/dist/tests/unit/api/public.unit.test.js +72 -0
- package/dist/tests/unit/api/public.unit.test.js.map +1 -1
- package/dist/tests/unit/schedulers/captchaScheduler.unit.test.js +1 -1
- package/dist/tests/unit/schedulers/captchaScheduler.unit.test.js.map +1 -1
- package/dist/tests/unit/schedulers/getClientList.unit.test.js +1 -1
- package/dist/tests/unit/schedulers/getClientList.unit.test.js.map +1 -1
- package/dist/tests/unit/schedulers/setClientEntropy.unit.test.js +1 -1
- package/dist/tests/unit/schedulers/setClientEntropy.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/captchaManager.unit.test.js +1 -1
- package/dist/tests/unit/tasks/captchaManager.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/client/clientTasks.unit.test.js +1 -1
- package/dist/tests/unit/tasks/client/clientTasks.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/dataset/datasetTasks.unit.test.js +1 -1
- package/dist/tests/unit/tasks/dataset/datasetTasks.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/decisionMachine/decisionMachineRunner.unit.test.js +0 -3
- package/dist/tests/unit/tasks/decisionMachine/decisionMachineRunner.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/detection/getBotScore.unit.test.js +0 -10
- package/dist/tests/unit/tasks/detection/getBotScore.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/frictionless/decryptPayload.unit.test.js +0 -82
- package/dist/tests/unit/tasks/frictionless/decryptPayload.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/frictionless/frictionlessTasks.unit.test.js +1 -13
- package/dist/tests/unit/tasks/frictionless/frictionlessTasks.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.unit.test.js +4 -3
- package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.js +0 -4
- package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/puzzleCaptcha/puzzleTasks.unit.test.js +0 -2
- package/dist/tests/unit/tasks/puzzleCaptcha/puzzleTasks.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/writeQueueIntegration.unit.test.js +0 -3
- package/dist/tests/unit/tasks/writeQueueIntegration.unit.test.js.map +1 -1
- package/package.json +23 -23
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const api = require("@prosopo/api");
|
|
4
|
+
const loadBalancer = require("@prosopo/load-balancer");
|
|
5
|
+
function normalizeProviderUrl(url) {
|
|
6
|
+
try {
|
|
7
|
+
const parsed = new URL(url);
|
|
8
|
+
parsed.hostname = parsed.hostname.replace(/^(ipv4|ipv6)\./, "");
|
|
9
|
+
return parsed.toString().replace(/\/$/, "");
|
|
10
|
+
} catch {
|
|
11
|
+
return url.replace(/\/$/, "");
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
async function forwardVerifyIfNotIssuer(args) {
|
|
15
|
+
const { env, logger, path, providerUrl, dapp, user, body, alreadyForwarded } = args;
|
|
16
|
+
if (alreadyForwarded) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
if (!providerUrl) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
let providers;
|
|
23
|
+
try {
|
|
24
|
+
providers = await loadBalancer.getProviders(env.defaultEnvironment);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
logger.warn(() => ({
|
|
27
|
+
msg: "Failed to load provider list; verifying locally",
|
|
28
|
+
err: error instanceof Error ? error : new Error(String(error)),
|
|
29
|
+
data: { providerUrl }
|
|
30
|
+
}));
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const normalizedProviderUrl = normalizeProviderUrl(providerUrl);
|
|
34
|
+
const issuer = providers.find(
|
|
35
|
+
(provider) => normalizeProviderUrl(provider.url) === normalizedProviderUrl
|
|
36
|
+
);
|
|
37
|
+
if (!issuer) {
|
|
38
|
+
logger.warn(() => ({
|
|
39
|
+
msg: "Token providerUrl is not a known provider; verifying locally",
|
|
40
|
+
data: { providerUrl }
|
|
41
|
+
}));
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
if (env.pair?.address && issuer.address === env.pair.address) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
logger.info(() => ({
|
|
48
|
+
msg: "Forwarding verify request to issuing provider",
|
|
49
|
+
data: { providerUrl, path, dapp }
|
|
50
|
+
}));
|
|
51
|
+
const providerApi = new api.ProviderApi(issuer.url, dapp);
|
|
52
|
+
return providerApi.forwardVerify(path, body, user);
|
|
53
|
+
}
|
|
54
|
+
exports.forwardVerifyIfNotIssuer = forwardVerifyIfNotIssuer;
|
|
@@ -19,10 +19,13 @@ const headerCheckMiddleware = (env) => {
|
|
|
19
19
|
validateAddress.validateAddr(user, void 0, req.logger);
|
|
20
20
|
req.user = user;
|
|
21
21
|
req.siteKey = siteKey;
|
|
22
|
-
req.logger = req.logger.with(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
req.logger = req.logger.with(
|
|
23
|
+
{
|
|
24
|
+
user,
|
|
25
|
+
siteKey
|
|
26
|
+
},
|
|
27
|
+
"header-check"
|
|
28
|
+
);
|
|
26
29
|
next();
|
|
27
30
|
} catch (err) {
|
|
28
31
|
return apiExpressRouter.handleErrors(err, req, res, next);
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const node_crypto = require("node:crypto");
|
|
4
|
+
const RECORD_HEADER_LENGTH = 5;
|
|
5
|
+
const HANDSHAKE_HEADER_LENGTH = 4;
|
|
6
|
+
const HANDSHAKE_RECORD_TYPE = 22;
|
|
7
|
+
const HANDSHAKE_MESSAGE_TYPE = 1;
|
|
8
|
+
const EXT_SERVER_NAME = 0;
|
|
9
|
+
const EXT_SIGNATURE_ALGORITHMS = 13;
|
|
10
|
+
const EXT_ALPN = 16;
|
|
11
|
+
const EXT_SUPPORTED_VERSIONS = 43;
|
|
12
|
+
function isGrease(id) {
|
|
13
|
+
return (id & 3855) === 2570 && id >> 8 === (id & 255);
|
|
14
|
+
}
|
|
15
|
+
function hash12(input) {
|
|
16
|
+
return node_crypto.createHash("sha256").update(input).digest("hex").slice(0, 12);
|
|
17
|
+
}
|
|
18
|
+
function formatIdList(ids) {
|
|
19
|
+
return ids.map((id) => id.toString(16).padStart(4, "0")).join(",");
|
|
20
|
+
}
|
|
21
|
+
function alpnChar(b) {
|
|
22
|
+
if (b >= 48 && b <= 57 || // 0-9
|
|
23
|
+
b >= 65 && b <= 90 || // A-Z
|
|
24
|
+
b >= 97 && b <= 122) {
|
|
25
|
+
return String.fromCharCode(b);
|
|
26
|
+
}
|
|
27
|
+
return b.toString(16).padStart(2, "0");
|
|
28
|
+
}
|
|
29
|
+
function alpnFirstLast(protocol) {
|
|
30
|
+
const first = protocol.length > 0 ? alpnChar(protocol.readUInt8(0)) : "0";
|
|
31
|
+
const last = protocol.length > 1 ? alpnChar(protocol.readUInt8(protocol.length - 1)) : "0";
|
|
32
|
+
return [first, last];
|
|
33
|
+
}
|
|
34
|
+
function tlsVersionStr(version) {
|
|
35
|
+
switch (version) {
|
|
36
|
+
case 769:
|
|
37
|
+
return "10";
|
|
38
|
+
case 770:
|
|
39
|
+
return "11";
|
|
40
|
+
case 771:
|
|
41
|
+
return "12";
|
|
42
|
+
case 772:
|
|
43
|
+
return "13";
|
|
44
|
+
default:
|
|
45
|
+
return "00";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
class Ja4ParseError extends Error {
|
|
49
|
+
constructor(message) {
|
|
50
|
+
super(message);
|
|
51
|
+
this.name = "Ja4ParseError";
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function calculateJa4(data) {
|
|
55
|
+
let offset = 0;
|
|
56
|
+
if (data.length < RECORD_HEADER_LENGTH + HANDSHAKE_HEADER_LENGTH) {
|
|
57
|
+
throw new Ja4ParseError("ClientHello too short");
|
|
58
|
+
}
|
|
59
|
+
const recordType = data.readUInt8(offset);
|
|
60
|
+
offset += 1;
|
|
61
|
+
if (recordType !== HANDSHAKE_RECORD_TYPE) {
|
|
62
|
+
throw new Ja4ParseError(
|
|
63
|
+
`Invalid record type: 0x${recordType.toString(16)}`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
offset += 2;
|
|
67
|
+
const recordLen = data.readUInt16BE(offset);
|
|
68
|
+
offset += 2;
|
|
69
|
+
if (data.length < RECORD_HEADER_LENGTH + recordLen) {
|
|
70
|
+
throw new Ja4ParseError("Record length exceeds buffer");
|
|
71
|
+
}
|
|
72
|
+
data = data.subarray(0, RECORD_HEADER_LENGTH + recordLen);
|
|
73
|
+
if (data.length < RECORD_HEADER_LENGTH + HANDSHAKE_HEADER_LENGTH) {
|
|
74
|
+
throw new Ja4ParseError("Record too short for handshake header");
|
|
75
|
+
}
|
|
76
|
+
const msgType = data.readUInt8(offset);
|
|
77
|
+
offset += 1;
|
|
78
|
+
if (msgType !== HANDSHAKE_MESSAGE_TYPE) {
|
|
79
|
+
throw new Ja4ParseError(
|
|
80
|
+
`Not a ClientHello: handshake type 0x${msgType.toString(16)}`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
const msgLen = data.readUIntBE(offset, 3);
|
|
84
|
+
offset += 3;
|
|
85
|
+
if (data.length - offset < msgLen) {
|
|
86
|
+
throw new Ja4ParseError("Handshake message length exceeds record");
|
|
87
|
+
}
|
|
88
|
+
data = data.subarray(0, offset + msgLen);
|
|
89
|
+
if (data.length - offset < 35) {
|
|
90
|
+
throw new Ja4ParseError("ClientHello body too short");
|
|
91
|
+
}
|
|
92
|
+
let clientVersion = data.readUInt16BE(offset);
|
|
93
|
+
offset += 2;
|
|
94
|
+
offset += 32;
|
|
95
|
+
const sessionIdLen = data.readUInt8(offset);
|
|
96
|
+
offset += 1;
|
|
97
|
+
if (data.length - offset < sessionIdLen) {
|
|
98
|
+
throw new Ja4ParseError("Truncated at session ID");
|
|
99
|
+
}
|
|
100
|
+
offset += sessionIdLen;
|
|
101
|
+
if (data.length - offset < 2) {
|
|
102
|
+
throw new Ja4ParseError("Truncated at cipher suites length");
|
|
103
|
+
}
|
|
104
|
+
const cipherSuitesLen = data.readUInt16BE(offset);
|
|
105
|
+
offset += 2;
|
|
106
|
+
if (data.length - offset < cipherSuitesLen) {
|
|
107
|
+
throw new Ja4ParseError("Truncated at cipher suites");
|
|
108
|
+
}
|
|
109
|
+
if (cipherSuitesLen % 2 !== 0) {
|
|
110
|
+
throw new Ja4ParseError("Cipher suites length is not a multiple of 2");
|
|
111
|
+
}
|
|
112
|
+
const cipherSuites = [];
|
|
113
|
+
for (let i = 0; i < cipherSuitesLen; i += 2) {
|
|
114
|
+
cipherSuites.push(data.readUInt16BE(offset + i));
|
|
115
|
+
}
|
|
116
|
+
offset += cipherSuitesLen;
|
|
117
|
+
if (data.length - offset < 1) {
|
|
118
|
+
throw new Ja4ParseError("Truncated at compression methods");
|
|
119
|
+
}
|
|
120
|
+
const compressionLen = data.readUInt8(offset);
|
|
121
|
+
offset += 1;
|
|
122
|
+
if (data.length - offset < compressionLen) {
|
|
123
|
+
throw new Ja4ParseError("Truncated at compression methods data");
|
|
124
|
+
}
|
|
125
|
+
offset += compressionLen;
|
|
126
|
+
if (data.length - offset < 2) {
|
|
127
|
+
throw new Ja4ParseError("Truncated at extensions length");
|
|
128
|
+
}
|
|
129
|
+
const extensionsLen = data.readUInt16BE(offset);
|
|
130
|
+
offset += 2;
|
|
131
|
+
if (data.length - offset < extensionsLen) {
|
|
132
|
+
throw new Ja4ParseError("Truncated at extensions");
|
|
133
|
+
}
|
|
134
|
+
const extIds = [];
|
|
135
|
+
const sigAlgorithms = [];
|
|
136
|
+
const alpnProtocols = [];
|
|
137
|
+
let extOffset = offset;
|
|
138
|
+
const extEnd = offset + extensionsLen;
|
|
139
|
+
while (extOffset + 4 <= extEnd) {
|
|
140
|
+
const extId = data.readUInt16BE(extOffset);
|
|
141
|
+
extOffset += 2;
|
|
142
|
+
const extLen = data.readUInt16BE(extOffset);
|
|
143
|
+
extOffset += 2;
|
|
144
|
+
if (extOffset + extLen > extEnd) break;
|
|
145
|
+
const extData = data.subarray(extOffset, extOffset + extLen);
|
|
146
|
+
extOffset += extLen;
|
|
147
|
+
extIds.push(extId);
|
|
148
|
+
if (extId === EXT_SUPPORTED_VERSIONS) {
|
|
149
|
+
if (extData.length >= 1) {
|
|
150
|
+
const listLen = extData.readUInt8(0);
|
|
151
|
+
let best = 0;
|
|
152
|
+
for (let i = 1; i + 1 < extData.length && i + 1 <= listLen; i += 2) {
|
|
153
|
+
const v = extData.readUInt16BE(i);
|
|
154
|
+
if (!isGrease(v) && v > best) best = v;
|
|
155
|
+
}
|
|
156
|
+
if (best > 0) clientVersion = best;
|
|
157
|
+
}
|
|
158
|
+
} else if (extId === EXT_SIGNATURE_ALGORITHMS) {
|
|
159
|
+
if (extData.length >= 2) {
|
|
160
|
+
const algLen = extData.readUInt16BE(0);
|
|
161
|
+
for (let i = 2; i + 1 < extData.length && i + 1 < algLen + 2; i += 2) {
|
|
162
|
+
const alg = extData.readUInt16BE(i);
|
|
163
|
+
if (!isGrease(alg)) sigAlgorithms.push(alg);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
} else if (extId === EXT_ALPN) {
|
|
167
|
+
if (extData.length >= 2) {
|
|
168
|
+
const listLen = extData.readUInt16BE(0);
|
|
169
|
+
const listEnd = Math.min(extData.length, 2 + listLen);
|
|
170
|
+
let alpnOff = 2;
|
|
171
|
+
while (alpnOff < listEnd) {
|
|
172
|
+
const protoLen = extData.readUInt8(alpnOff);
|
|
173
|
+
alpnOff += 1;
|
|
174
|
+
if (alpnOff + protoLen > listEnd) break;
|
|
175
|
+
alpnProtocols.push(extData.subarray(alpnOff, alpnOff + protoLen));
|
|
176
|
+
alpnOff += protoLen;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
const versionStr = tlsVersionStr(clientVersion);
|
|
182
|
+
const sni = extIds.includes(EXT_SERVER_NAME) ? "d" : "i";
|
|
183
|
+
const firstAlpn = alpnProtocols[0];
|
|
184
|
+
const [alpnFirst, alpnLast] = firstAlpn ? alpnFirstLast(firstAlpn) : ["0", "0"];
|
|
185
|
+
const nonGreaseCiphers = cipherSuites.filter((c) => !isGrease(c));
|
|
186
|
+
const nonGreaseExts = extIds.filter((e) => !isGrease(e));
|
|
187
|
+
const cipherCount = Math.min(99, nonGreaseCiphers.length).toString().padStart(2, "0");
|
|
188
|
+
const extCount = Math.min(99, nonGreaseExts.length).toString().padStart(2, "0");
|
|
189
|
+
const firstChunk = `t${versionStr}${sni}${cipherCount}${extCount}${alpnFirst}${alpnLast}`;
|
|
190
|
+
const sortedCiphers = [...nonGreaseCiphers].sort((a, b) => a - b);
|
|
191
|
+
const cipherHash = hash12(formatIdList(sortedCiphers));
|
|
192
|
+
const filteredExts = nonGreaseExts.filter(
|
|
193
|
+
(e) => e !== EXT_SERVER_NAME && e !== EXT_ALPN
|
|
194
|
+
);
|
|
195
|
+
const sortedExts = [...filteredExts].sort((a, b) => a - b);
|
|
196
|
+
const extStr = formatIdList(sortedExts);
|
|
197
|
+
const sigAlgStr = formatIdList(sigAlgorithms);
|
|
198
|
+
const extHashInput = sigAlgStr.length > 0 ? `${extStr}_${sigAlgStr}` : extStr;
|
|
199
|
+
const extHash = hash12(extHashInput);
|
|
200
|
+
return `${firstChunk}_${cipherHash}_${extHash}`;
|
|
201
|
+
}
|
|
202
|
+
exports.Ja4ParseError = Ja4ParseError;
|
|
203
|
+
exports.calculateJa4 = calculateJa4;
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const node_stream = require("node:stream");
|
|
4
3
|
const apiExpressRouter = require("@prosopo/api-express-router");
|
|
5
4
|
const logger = require("@prosopo/logger");
|
|
6
5
|
const utilCrypto = require("@prosopo/util-crypto");
|
|
7
|
-
const
|
|
8
|
-
var _documentCurrentScript = typeof document !== "undefined" ? document.currentScript : null;
|
|
6
|
+
const ja4 = require("./ja4.cjs");
|
|
9
7
|
const DEFAULT_JA4 = "ja4";
|
|
10
8
|
const getJA4 = async (headers, logger$1) => {
|
|
11
|
-
logger$1 = logger$1 || logger.getLogger("info",
|
|
9
|
+
logger$1 = logger$1 || logger.getLogger("info", "provider:ja4");
|
|
12
10
|
if (process.env.NODE_ENV === "development") {
|
|
13
11
|
return {
|
|
14
12
|
ja4PlusFingerprint: `${DEFAULT_JA4}${utilCrypto.randomAsHex().slice(28, 32)}`
|
|
@@ -16,7 +14,6 @@ const getJA4 = async (headers, logger$1) => {
|
|
|
16
14
|
}
|
|
17
15
|
try {
|
|
18
16
|
const xTlsClientHello = (headers["x-tls-clienthello"] || "").toString();
|
|
19
|
-
const xTlsVersion = (headers["x-tls-version"] || "").toString().toLowerCase();
|
|
20
17
|
const clientHelloBuffer = Buffer.from(xTlsClientHello, "base64");
|
|
21
18
|
logger$1.debug(() => ({
|
|
22
19
|
msg: "ClientHello First Bytes:",
|
|
@@ -28,17 +25,7 @@ const getJA4 = async (headers, logger$1) => {
|
|
|
28
25
|
}));
|
|
29
26
|
return { ja4PlusFingerprint: DEFAULT_JA4 };
|
|
30
27
|
}
|
|
31
|
-
|
|
32
|
-
msg: "Headers TLS Version:",
|
|
33
|
-
data: { xTlsVersion }
|
|
34
|
-
}));
|
|
35
|
-
const readableStream = new node_stream.Readable({
|
|
36
|
-
read() {
|
|
37
|
-
this.push(clientHelloBuffer);
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
const clientHello = await readTlsClientHello.readTlsClientHello(readableStream);
|
|
41
|
-
const ja4PlusFingerprint = readTlsClientHello.calculateJa4FromHelloData(clientHello);
|
|
28
|
+
const ja4PlusFingerprint = ja4.calculateJa4(clientHelloBuffer);
|
|
42
29
|
return { ja4PlusFingerprint };
|
|
43
30
|
} catch (e) {
|
|
44
31
|
logger$1.error(() => ({
|
|
@@ -52,11 +39,14 @@ const ja4Middleware = (env) => {
|
|
|
52
39
|
return async (req, res, next) => {
|
|
53
40
|
try {
|
|
54
41
|
req.logger.debug(() => ({ data: { url: req.url } }));
|
|
55
|
-
const
|
|
56
|
-
req.ja4 =
|
|
57
|
-
req.logger = req.logger.with(
|
|
58
|
-
|
|
59
|
-
|
|
42
|
+
const ja42 = await getJA4(req.headers, req.logger);
|
|
43
|
+
req.ja4 = ja42.ja4PlusFingerprint || "";
|
|
44
|
+
req.logger = req.logger.with(
|
|
45
|
+
{
|
|
46
|
+
ja4: req.ja4
|
|
47
|
+
},
|
|
48
|
+
"ja4"
|
|
49
|
+
);
|
|
60
50
|
next();
|
|
61
51
|
} catch (err) {
|
|
62
52
|
return apiExpressRouter.handleErrors(err, req, res, next);
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const types = require("@prosopo/types");
|
|
4
|
+
const promClient = require("prom-client");
|
|
5
|
+
const metricsEnabled = () => process.env.PROSOPO_METRICS_ENABLED !== "false";
|
|
6
|
+
const PREFIX = "prosopo_";
|
|
7
|
+
const LATENCY_BUCKETS = [
|
|
8
|
+
5e-3,
|
|
9
|
+
0.01,
|
|
10
|
+
0.025,
|
|
11
|
+
0.05,
|
|
12
|
+
0.1,
|
|
13
|
+
0.25,
|
|
14
|
+
0.5,
|
|
15
|
+
1,
|
|
16
|
+
2.5,
|
|
17
|
+
5,
|
|
18
|
+
10
|
|
19
|
+
];
|
|
20
|
+
const SCORE_BUCKETS = [
|
|
21
|
+
0,
|
|
22
|
+
0.1,
|
|
23
|
+
0.2,
|
|
24
|
+
0.3,
|
|
25
|
+
0.4,
|
|
26
|
+
0.5,
|
|
27
|
+
0.6,
|
|
28
|
+
0.7,
|
|
29
|
+
0.8,
|
|
30
|
+
0.9,
|
|
31
|
+
1,
|
|
32
|
+
1.5,
|
|
33
|
+
2,
|
|
34
|
+
3,
|
|
35
|
+
5,
|
|
36
|
+
10
|
|
37
|
+
];
|
|
38
|
+
const KNOWN_ROUTES = /* @__PURE__ */ new Set([
|
|
39
|
+
...Object.values(types.ClientApiPaths),
|
|
40
|
+
...Object.values(types.PublicApiPaths),
|
|
41
|
+
...Object.values(types.AdminApiPaths)
|
|
42
|
+
]);
|
|
43
|
+
const normaliseRoute = (req) => {
|
|
44
|
+
const path = req.path;
|
|
45
|
+
return KNOWN_ROUTES.has(path) ? path : "other";
|
|
46
|
+
};
|
|
47
|
+
let metrics;
|
|
48
|
+
const buildMetrics = () => {
|
|
49
|
+
const registry = new promClient.Registry();
|
|
50
|
+
promClient.collectDefaultMetrics({ register: registry, prefix: PREFIX });
|
|
51
|
+
const httpRequestsTotal = new promClient.Counter({
|
|
52
|
+
name: `${PREFIX}http_requests_total`,
|
|
53
|
+
help: "Total HTTP requests handled by the provider API",
|
|
54
|
+
labelNames: ["route", "method", "status"],
|
|
55
|
+
registers: [registry]
|
|
56
|
+
});
|
|
57
|
+
const httpRequestDuration = new promClient.Histogram({
|
|
58
|
+
name: `${PREFIX}http_request_duration_seconds`,
|
|
59
|
+
help: "Provider API request duration in seconds",
|
|
60
|
+
labelNames: ["route", "method", "status"],
|
|
61
|
+
buckets: LATENCY_BUCKETS,
|
|
62
|
+
registers: [registry]
|
|
63
|
+
});
|
|
64
|
+
const captchaIssuedTotal = new promClient.Counter({
|
|
65
|
+
name: `${PREFIX}captcha_issued_total`,
|
|
66
|
+
help: "Captcha challenges issued, by type",
|
|
67
|
+
labelNames: ["type"],
|
|
68
|
+
registers: [registry]
|
|
69
|
+
});
|
|
70
|
+
const captchaIssueErrorsTotal = new promClient.Counter({
|
|
71
|
+
name: `${PREFIX}captcha_issue_errors_total`,
|
|
72
|
+
help: "Errors while issuing captcha challenges, by type",
|
|
73
|
+
labelNames: ["type"],
|
|
74
|
+
registers: [registry]
|
|
75
|
+
});
|
|
76
|
+
const captchaVerifyTotal = new promClient.Counter({
|
|
77
|
+
name: `${PREFIX}captcha_verify_total`,
|
|
78
|
+
help: "Captcha verification outcomes, by type/result/source",
|
|
79
|
+
labelNames: ["type", "result", "source"],
|
|
80
|
+
registers: [registry]
|
|
81
|
+
});
|
|
82
|
+
const frictionlessRoutedTotal = new promClient.Counter({
|
|
83
|
+
name: `${PREFIX}frictionless_routed_total`,
|
|
84
|
+
help: "Frictionless routing decisions (which captcha the user was sent to)",
|
|
85
|
+
labelNames: ["decision"],
|
|
86
|
+
registers: [registry]
|
|
87
|
+
});
|
|
88
|
+
const botScore = new promClient.Histogram({
|
|
89
|
+
name: `${PREFIX}bot_score`,
|
|
90
|
+
help: "Distribution of frictionless bot scores (0=human .. 1=bot; language-rule penalties can push it above 1)",
|
|
91
|
+
buckets: SCORE_BUCKETS,
|
|
92
|
+
registers: [registry]
|
|
93
|
+
});
|
|
94
|
+
const detectorTriggeredTotal = new promClient.Counter({
|
|
95
|
+
name: `${PREFIX}detector_triggered_total`,
|
|
96
|
+
help: "Count of times each bot detector was triggered",
|
|
97
|
+
labelNames: ["detector"],
|
|
98
|
+
registers: [registry]
|
|
99
|
+
});
|
|
100
|
+
const blockedRequestsTotal = new promClient.Counter({
|
|
101
|
+
name: `${PREFIX}blocked_requests_total`,
|
|
102
|
+
help: "Requests blocked before reaching a captcha handler, by reason",
|
|
103
|
+
labelNames: ["reason"],
|
|
104
|
+
registers: [registry]
|
|
105
|
+
});
|
|
106
|
+
const domainValidationTotal = new promClient.Counter({
|
|
107
|
+
name: `${PREFIX}domain_validation_total`,
|
|
108
|
+
help: "Domain/site-key validation outcomes",
|
|
109
|
+
labelNames: ["result"],
|
|
110
|
+
registers: [registry]
|
|
111
|
+
});
|
|
112
|
+
const spamEmailTotal = new promClient.Counter({
|
|
113
|
+
name: `${PREFIX}spam_email_total`,
|
|
114
|
+
help: "Spam email-domain check outcomes",
|
|
115
|
+
labelNames: ["result"],
|
|
116
|
+
registers: [registry]
|
|
117
|
+
});
|
|
118
|
+
const maintenanceMode = new promClient.Gauge({
|
|
119
|
+
name: `${PREFIX}maintenance_mode`,
|
|
120
|
+
help: "1 when the provider is in maintenance mode, else 0",
|
|
121
|
+
registers: [registry]
|
|
122
|
+
});
|
|
123
|
+
maintenanceMode.set(
|
|
124
|
+
process.env.MAINTENANCE_MODE?.toLowerCase() === "true" ? 1 : 0
|
|
125
|
+
);
|
|
126
|
+
const redisReady = new promClient.Gauge({
|
|
127
|
+
name: `${PREFIX}redis_ready`,
|
|
128
|
+
help: "1 when the given redis connection is ready, else 0",
|
|
129
|
+
labelNames: ["actor"],
|
|
130
|
+
registers: [registry]
|
|
131
|
+
});
|
|
132
|
+
return {
|
|
133
|
+
registry,
|
|
134
|
+
httpRequestsTotal,
|
|
135
|
+
httpRequestDuration,
|
|
136
|
+
captchaIssuedTotal,
|
|
137
|
+
captchaIssueErrorsTotal,
|
|
138
|
+
captchaVerifyTotal,
|
|
139
|
+
frictionlessRoutedTotal,
|
|
140
|
+
botScore,
|
|
141
|
+
detectorTriggeredTotal,
|
|
142
|
+
blockedRequestsTotal,
|
|
143
|
+
domainValidationTotal,
|
|
144
|
+
spamEmailTotal,
|
|
145
|
+
maintenanceMode,
|
|
146
|
+
redisReady
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
const getMetrics = () => {
|
|
150
|
+
if (!metrics) {
|
|
151
|
+
metrics = buildMetrics();
|
|
152
|
+
}
|
|
153
|
+
return metrics;
|
|
154
|
+
};
|
|
155
|
+
const recordCaptchaIssued = (type) => {
|
|
156
|
+
if (!metricsEnabled()) return;
|
|
157
|
+
getMetrics().captchaIssuedTotal.inc({ type });
|
|
158
|
+
};
|
|
159
|
+
const recordCaptchaIssueError = (type) => {
|
|
160
|
+
if (!metricsEnabled()) return;
|
|
161
|
+
getMetrics().captchaIssueErrorsTotal.inc({ type });
|
|
162
|
+
};
|
|
163
|
+
const recordCaptchaVerify = (args) => {
|
|
164
|
+
if (!metricsEnabled()) return;
|
|
165
|
+
getMetrics().captchaVerifyTotal.inc({
|
|
166
|
+
type: args.type,
|
|
167
|
+
result: args.verified ? "verified" : "failed",
|
|
168
|
+
source: args.source
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
const recordFrictionlessDecision = (decision) => {
|
|
172
|
+
if (!metricsEnabled()) return;
|
|
173
|
+
getMetrics().frictionlessRoutedTotal.inc({ decision });
|
|
174
|
+
};
|
|
175
|
+
const recordBotScore = (score) => {
|
|
176
|
+
if (!metricsEnabled()) return;
|
|
177
|
+
getMetrics().botScore.observe(score);
|
|
178
|
+
};
|
|
179
|
+
const recordDetectorTriggered = (detectors) => {
|
|
180
|
+
if (!metricsEnabled()) return;
|
|
181
|
+
const m = getMetrics();
|
|
182
|
+
for (const detector of detectors) {
|
|
183
|
+
m.detectorTriggeredTotal.inc({ detector: String(detector) });
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
const recordBlockedRequest = (reason) => {
|
|
187
|
+
if (!metricsEnabled()) return;
|
|
188
|
+
getMetrics().blockedRequestsTotal.inc({ reason });
|
|
189
|
+
};
|
|
190
|
+
const recordDomainValidation = (result) => {
|
|
191
|
+
if (!metricsEnabled()) return;
|
|
192
|
+
getMetrics().domainValidationTotal.inc({ result });
|
|
193
|
+
};
|
|
194
|
+
const recordSpamEmail = (result) => {
|
|
195
|
+
if (!metricsEnabled()) return;
|
|
196
|
+
getMetrics().spamEmailTotal.inc({ result });
|
|
197
|
+
};
|
|
198
|
+
const setMaintenanceModeGauge = (on) => {
|
|
199
|
+
if (!metricsEnabled()) return;
|
|
200
|
+
getMetrics().maintenanceMode.set(on ? 1 : 0);
|
|
201
|
+
};
|
|
202
|
+
const metricsMiddleware = () => {
|
|
203
|
+
return (req, res, next) => {
|
|
204
|
+
if (!metricsEnabled()) return next();
|
|
205
|
+
const start = process.hrtime.bigint();
|
|
206
|
+
res.on("finish", () => {
|
|
207
|
+
const route = normaliseRoute(req);
|
|
208
|
+
const labels = {
|
|
209
|
+
route,
|
|
210
|
+
method: req.method,
|
|
211
|
+
status: String(res.statusCode)
|
|
212
|
+
};
|
|
213
|
+
const durationSeconds = Number(process.hrtime.bigint() - start) / 1e9;
|
|
214
|
+
const m = getMetrics();
|
|
215
|
+
m.httpRequestsTotal.inc(labels);
|
|
216
|
+
m.httpRequestDuration.observe(labels, durationSeconds);
|
|
217
|
+
});
|
|
218
|
+
next();
|
|
219
|
+
};
|
|
220
|
+
};
|
|
221
|
+
const metricsHandler = (env) => {
|
|
222
|
+
return async (req, res) => {
|
|
223
|
+
const token = process.env.PROSOPO_METRICS_TOKEN;
|
|
224
|
+
if (token && req.headers.authorization !== `Bearer ${token}`) {
|
|
225
|
+
res.status(401).send("Unauthorized");
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const m = getMetrics();
|
|
229
|
+
try {
|
|
230
|
+
const db = env.getDb();
|
|
231
|
+
m.redisReady.set(
|
|
232
|
+
{ actor: "general" },
|
|
233
|
+
db.getRedisConnection().isReady() ? 1 : 0
|
|
234
|
+
);
|
|
235
|
+
m.redisReady.set(
|
|
236
|
+
{ actor: "uap" },
|
|
237
|
+
db.getRedisAccessRulesConnection().isReady() ? 1 : 0
|
|
238
|
+
);
|
|
239
|
+
} catch {
|
|
240
|
+
m.redisReady.set({ actor: "general" }, 0);
|
|
241
|
+
m.redisReady.set({ actor: "uap" }, 0);
|
|
242
|
+
}
|
|
243
|
+
res.set("Content-Type", m.registry.contentType);
|
|
244
|
+
res.end(await m.registry.metrics());
|
|
245
|
+
};
|
|
246
|
+
};
|
|
247
|
+
exports.getMetrics = getMetrics;
|
|
248
|
+
exports.metricsEnabled = metricsEnabled;
|
|
249
|
+
exports.metricsHandler = metricsHandler;
|
|
250
|
+
exports.metricsMiddleware = metricsMiddleware;
|
|
251
|
+
exports.recordBlockedRequest = recordBlockedRequest;
|
|
252
|
+
exports.recordBotScore = recordBotScore;
|
|
253
|
+
exports.recordCaptchaIssueError = recordCaptchaIssueError;
|
|
254
|
+
exports.recordCaptchaIssued = recordCaptchaIssued;
|
|
255
|
+
exports.recordCaptchaVerify = recordCaptchaVerify;
|
|
256
|
+
exports.recordDetectorTriggered = recordDetectorTriggered;
|
|
257
|
+
exports.recordDomainValidation = recordDomainValidation;
|
|
258
|
+
exports.recordFrictionlessDecision = recordFrictionlessDecision;
|
|
259
|
+
exports.recordSpamEmail = recordSpamEmail;
|
|
260
|
+
exports.setMaintenanceModeGauge = setMaintenanceModeGauge;
|
package/dist/cjs/api/public.cjs
CHANGED
|
@@ -5,11 +5,16 @@ const common = require("@prosopo/common");
|
|
|
5
5
|
const types = require("@prosopo/types");
|
|
6
6
|
const util = require("@prosopo/util");
|
|
7
7
|
const express = require("express");
|
|
8
|
+
const metrics = require("./metrics.cjs");
|
|
8
9
|
function publicRouter(env) {
|
|
9
10
|
const router = express.Router();
|
|
10
11
|
router.get(types.PublicApiPaths.Healthz, (req, res) => {
|
|
11
|
-
|
|
12
|
+
const host = env.config.host && env.config.host.length > 0 ? env.config.host : req.hostname;
|
|
13
|
+
return res.status(200).json({ ok: true, host });
|
|
12
14
|
});
|
|
15
|
+
if (metrics.metricsEnabled()) {
|
|
16
|
+
router.get(types.PublicApiPaths.Metrics, metrics.metricsHandler(env));
|
|
17
|
+
}
|
|
13
18
|
router.get(types.PublicApiPaths.GetProviderDetails, async (req, res, next) => {
|
|
14
19
|
try {
|
|
15
20
|
const db = env.getDb();
|