@prosopo/provider 5.13.4 → 5.15.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 +30 -30
- package/.turbo/turbo-build$colon$tsc.log +27 -30
- package/.turbo/turbo-build.log +30 -33
- package/CHANGELOG.md +233 -0
- package/LICENSE +201 -0
- package/dist/api/adminAuthOptions.d.ts +4 -0
- package/dist/api/adminAuthOptions.d.ts.map +1 -0
- package/dist/api/adminAuthOptions.js +34 -0
- package/dist/api/adminAuthOptions.js.map +1 -0
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/constants.d.ts +2 -1
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/constants.d.ts.map +1 -1
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/constants.js +11 -2
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/constants.js.map +1 -1
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.d.ts.map +1 -1
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js +16 -11
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js.map +1 -1
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/refreshLineage.d.ts +5 -0
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/refreshLineage.d.ts.map +1 -0
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/refreshLineage.js +20 -0
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/refreshLineage.js.map +1 -0
- package/dist/api/captcha/getPuzzleCaptchaChallenge.d.ts.map +1 -1
- package/dist/api/captcha/getPuzzleCaptchaChallenge.js +24 -0
- package/dist/api/captcha/getPuzzleCaptchaChallenge.js.map +1 -1
- package/dist/api/captcha/submitPoWCaptchaSolution.d.ts +4 -11
- package/dist/api/captcha/submitPoWCaptchaSolution.d.ts.map +1 -1
- package/dist/api/captcha/submitPoWCaptchaSolution.js +12 -17
- package/dist/api/captcha/submitPoWCaptchaSolution.js.map +1 -1
- package/dist/api/detectorPoolBodyParser.d.ts +4 -0
- package/dist/api/detectorPoolBodyParser.d.ts.map +1 -0
- package/dist/api/detectorPoolBodyParser.js +31 -0
- package/dist/api/detectorPoolBodyParser.js.map +1 -0
- package/dist/api/domainMiddleware.d.ts.map +1 -1
- package/dist/api/domainMiddleware.js +5 -1
- package/dist/api/domainMiddleware.js.map +1 -1
- package/dist/api/metrics.d.ts +2 -1
- package/dist/api/metrics.d.ts.map +1 -1
- package/dist/api/metrics.js +46 -4
- package/dist/api/metrics.js.map +1 -1
- package/dist/api/rawTlsSignalsMiddleware.d.ts +14 -1
- package/dist/api/rawTlsSignalsMiddleware.d.ts.map +1 -1
- package/dist/api/rawTlsSignalsMiddleware.js +111 -23
- package/dist/api/rawTlsSignalsMiddleware.js.map +1 -1
- package/dist/api/startProviderApi.d.ts.map +1 -1
- package/dist/api/startProviderApi.js +8 -5
- package/dist/api/startProviderApi.js.map +1 -1
- package/dist/api/verify.d.ts.map +1 -1
- package/dist/api/verify.js +50 -8
- package/dist/api/verify.js.map +1 -1
- package/dist/cjs/api/adminAuthOptions.cjs +35 -0
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/constants.cjs +10 -0
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/handler.cjs +14 -8
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/refreshLineage.cjs +20 -0
- package/dist/cjs/api/captcha/getPuzzleCaptchaChallenge.cjs +24 -0
- package/dist/cjs/api/captcha/submitPoWCaptchaSolution.cjs +10 -15
- package/dist/cjs/api/detectorPoolBodyParser.cjs +33 -0
- package/dist/cjs/api/domainMiddleware.cjs +5 -1
- package/dist/cjs/api/metrics.cjs +46 -3
- package/dist/cjs/api/rawTlsSignalsMiddleware.cjs +111 -22
- package/dist/cjs/api/startProviderApi.cjs +8 -5
- package/dist/cjs/api/verify.cjs +50 -8
- package/dist/cjs/compositeIpAddress.cjs +21 -7
- package/dist/cjs/tasks/captchaManager.cjs +15 -0
- package/dist/cjs/tasks/captchaTypeSelection.cjs +23 -2
- package/dist/cjs/tasks/client/clientTasks.cjs +25 -4
- package/dist/cjs/tasks/dataset/datasetTasksUtils.cjs +1 -1
- package/dist/cjs/tasks/frictionless/frictionlessTasks.cjs +26 -23
- package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasks.cjs +4 -11
- package/dist/cjs/tasks/powCaptcha/powTasks.cjs +7 -11
- package/dist/cjs/tasks/powCaptcha/powTasksUtils.cjs +2 -0
- package/dist/cjs/tasks/puzzle/backgroundBuffer.cjs +3 -3
- package/dist/cjs/tasks/puzzle/nativeBackground.cjs +12 -5
- package/dist/cjs/tasks/puzzle/puzzleRenderer.cjs +4 -3
- package/dist/cjs/tasks/puzzleCaptcha/puzzleTasks.cjs +2 -9
- package/dist/cjs/utils/tarpitPadding.cjs +81 -10
- package/dist/compositeIpAddress.d.ts.map +1 -1
- package/dist/compositeIpAddress.js +21 -7
- package/dist/compositeIpAddress.js.map +1 -1
- package/dist/tasks/captchaManager.d.ts.map +1 -1
- package/dist/tasks/captchaManager.js +16 -1
- package/dist/tasks/captchaManager.js.map +1 -1
- package/dist/tasks/captchaTypeSelection.d.ts +3 -1
- package/dist/tasks/captchaTypeSelection.d.ts.map +1 -1
- package/dist/tasks/captchaTypeSelection.js +23 -3
- package/dist/tasks/captchaTypeSelection.js.map +1 -1
- package/dist/tasks/client/clientTasks.d.ts +1 -0
- package/dist/tasks/client/clientTasks.d.ts.map +1 -1
- package/dist/tasks/client/clientTasks.js +25 -5
- package/dist/tasks/client/clientTasks.js.map +1 -1
- package/dist/tasks/dataset/datasetTasksUtils.js +1 -1
- package/dist/tasks/dataset/datasetTasksUtils.js.map +1 -1
- package/dist/tasks/frictionless/frictionlessTasks.d.ts +3 -1
- package/dist/tasks/frictionless/frictionlessTasks.d.ts.map +1 -1
- package/dist/tasks/frictionless/frictionlessTasks.js +28 -25
- package/dist/tasks/frictionless/frictionlessTasks.js.map +1 -1
- package/dist/tasks/frictionless/routingMachine.d.ts +1 -2
- package/dist/tasks/frictionless/routingMachine.d.ts.map +1 -1
- package/dist/tasks/frictionless/routingMachine.js.map +1 -1
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.d.ts.map +1 -1
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.js +4 -11
- 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 +8 -12
- package/dist/tasks/powCaptcha/powTasks.js.map +1 -1
- package/dist/tasks/powCaptcha/powTasksUtils.d.ts +1 -0
- package/dist/tasks/powCaptcha/powTasksUtils.d.ts.map +1 -1
- package/dist/tasks/powCaptcha/powTasksUtils.js +2 -1
- package/dist/tasks/powCaptcha/powTasksUtils.js.map +1 -1
- package/dist/tasks/puzzle/backgroundBuffer.d.ts +3 -2
- package/dist/tasks/puzzle/backgroundBuffer.d.ts.map +1 -1
- package/dist/tasks/puzzle/backgroundBuffer.js +3 -3
- package/dist/tasks/puzzle/backgroundBuffer.js.map +1 -1
- package/dist/tasks/puzzle/nativeBackground.d.ts +5 -1
- package/dist/tasks/puzzle/nativeBackground.d.ts.map +1 -1
- package/dist/tasks/puzzle/nativeBackground.js +12 -5
- package/dist/tasks/puzzle/nativeBackground.js.map +1 -1
- package/dist/tasks/puzzle/puzzleRenderer.d.ts +2 -0
- package/dist/tasks/puzzle/puzzleRenderer.d.ts.map +1 -1
- package/dist/tasks/puzzle/puzzleRenderer.js +4 -3
- package/dist/tasks/puzzle/puzzleRenderer.js.map +1 -1
- package/dist/tasks/puzzleCaptcha/puzzleTasks.d.ts.map +1 -1
- package/dist/tasks/puzzleCaptcha/puzzleTasks.js +2 -9
- package/dist/tasks/puzzleCaptcha/puzzleTasks.js.map +1 -1
- package/dist/tests/integration/clientSettingsPersistence.integration.test.js +2 -0
- package/dist/tests/integration/clientSettingsPersistence.integration.test.js.map +1 -1
- package/dist/tests/unit/api/adminAuthOptions.unit.test.d.ts +2 -0
- package/dist/tests/unit/api/adminAuthOptions.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/api/adminAuthOptions.unit.test.js +38 -0
- package/dist/tests/unit/api/adminAuthOptions.unit.test.js.map +1 -0
- package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/constants.unit.test.js +35 -2
- package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/constants.unit.test.js.map +1 -1
- package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/refreshLineage.unit.test.d.ts +2 -0
- package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/refreshLineage.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/refreshLineage.unit.test.js +45 -0
- package/dist/tests/unit/api/captcha/getFrictionlessCaptchaChallenge/refreshLineage.unit.test.js.map +1 -0
- package/dist/tests/unit/api/captcha/submitPoWCaptchaSolution.escalation.unit.test.js +15 -1
- package/dist/tests/unit/api/captcha/submitPoWCaptchaSolution.escalation.unit.test.js.map +1 -1
- package/dist/tests/unit/api/detectorPoolBodyParser.unit.test.d.ts +2 -0
- package/dist/tests/unit/api/detectorPoolBodyParser.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/api/detectorPoolBodyParser.unit.test.js +64 -0
- package/dist/tests/unit/api/detectorPoolBodyParser.unit.test.js.map +1 -0
- package/dist/tests/unit/api/domainMiddlewareErrorBody.unit.test.d.ts +2 -0
- package/dist/tests/unit/api/domainMiddlewareErrorBody.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/api/domainMiddlewareErrorBody.unit.test.js +67 -0
- package/dist/tests/unit/api/domainMiddlewareErrorBody.unit.test.js.map +1 -0
- package/dist/tests/unit/api/getFrictionlessCaptchaChallenge.unit.test.js +174 -23
- package/dist/tests/unit/api/getFrictionlessCaptchaChallenge.unit.test.js.map +1 -1
- package/dist/tests/unit/api/metricsAccess.unit.test.d.ts +2 -0
- package/dist/tests/unit/api/metricsAccess.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/api/metricsAccess.unit.test.js +87 -0
- package/dist/tests/unit/api/metricsAccess.unit.test.js.map +1 -0
- package/dist/tests/unit/api/public.unit.test.js +4 -1
- package/dist/tests/unit/api/public.unit.test.js.map +1 -1
- package/dist/tests/unit/api/rawTlsSignalsMiddleware.unit.test.js +96 -1
- package/dist/tests/unit/api/rawTlsSignalsMiddleware.unit.test.js.map +1 -1
- package/dist/tests/unit/api/validateAddress.unit.test.js +1 -1
- package/dist/tests/unit/api/validateAddress.unit.test.js.map +1 -1
- package/dist/tests/unit/api/verifyBadDappSignature.unit.test.d.ts +2 -0
- package/dist/tests/unit/api/verifyBadDappSignature.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/api/verifyBadDappSignature.unit.test.js +96 -0
- package/dist/tests/unit/api/verifyBadDappSignature.unit.test.js.map +1 -0
- package/dist/tests/unit/api/verifyMalformedToken.unit.test.d.ts +2 -0
- package/dist/tests/unit/api/verifyMalformedToken.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/api/verifyMalformedToken.unit.test.js +89 -0
- package/dist/tests/unit/api/verifyMalformedToken.unit.test.js.map +1 -0
- package/dist/tests/unit/compositeIpAddress.unit.test.js +12 -3
- package/dist/tests/unit/compositeIpAddress.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/captchaManager.unit.test.js +51 -0
- package/dist/tests/unit/tasks/captchaManager.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/captchaTypeSelection.unit.test.js +20 -1
- package/dist/tests/unit/tasks/captchaTypeSelection.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/client/clientTasks.unit.test.js +33 -1
- package/dist/tests/unit/tasks/client/clientTasks.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/dataset/datasetTasksUtils.unit.test.js +14 -1
- package/dist/tests/unit/tasks/dataset/datasetTasksUtils.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/frictionless/allowedCaptchaTypes.unit.test.d.ts +2 -0
- package/dist/tests/unit/tasks/frictionless/allowedCaptchaTypes.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/tasks/frictionless/allowedCaptchaTypes.unit.test.js +94 -0
- package/dist/tests/unit/tasks/frictionless/allowedCaptchaTypes.unit.test.js.map +1 -0
- package/dist/tests/unit/tasks/frictionless/refreshSwitch.unit.test.d.ts +2 -0
- package/dist/tests/unit/tasks/frictionless/refreshSwitch.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/tasks/frictionless/refreshSwitch.unit.test.js +103 -0
- package/dist/tests/unit/tasks/frictionless/refreshSwitch.unit.test.js.map +1 -0
- package/dist/tests/unit/tasks/powCaptcha/powTasksUtils.unit.test.js +23 -1
- package/dist/tests/unit/tasks/powCaptcha/powTasksUtils.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/puzzle/backgroundBuffer.unit.test.js +1 -4
- package/dist/tests/unit/tasks/puzzle/backgroundBuffer.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/puzzle/nativeBackground.unit.test.js +10 -3
- package/dist/tests/unit/tasks/puzzle/nativeBackground.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/puzzle/puzzleRenderer.unit.test.d.ts +2 -0
- package/dist/tests/unit/tasks/puzzle/puzzleRenderer.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/tasks/puzzle/puzzleRenderer.unit.test.js +35 -0
- package/dist/tests/unit/tasks/puzzle/puzzleRenderer.unit.test.js.map +1 -0
- package/dist/tests/unit/tasks/puzzleDifficultyDeviceCap.unit.test.d.ts +2 -0
- package/dist/tests/unit/tasks/puzzleDifficultyDeviceCap.unit.test.d.ts.map +1 -0
- package/dist/tests/unit/tasks/puzzleDifficultyDeviceCap.unit.test.js +49 -0
- package/dist/tests/unit/tasks/puzzleDifficultyDeviceCap.unit.test.js.map +1 -0
- package/dist/tests/unit/testUtils/mockProviderEnv.d.ts +17 -14
- package/dist/tests/unit/testUtils/mockProviderEnv.d.ts.map +1 -1
- package/dist/tests/unit/testUtils/mockProviderEnv.js.map +1 -1
- package/dist/tests/unit/utils/tarpitPadding.unit.test.js +118 -36
- package/dist/tests/unit/utils/tarpitPadding.unit.test.js.map +1 -1
- package/dist/utils/tarpitPadding.d.ts.map +1 -1
- package/dist/utils/tarpitPadding.js +81 -10
- package/dist/utils/tarpitPadding.js.map +1 -1
- package/package.json +105 -103
|
@@ -7,9 +7,44 @@ var HEADER_ACK_NS = "x-tls-ack-ns";
|
|
|
7
7
|
var HEADER_OBSERVED_TTL = "x-tls-observed-ttl";
|
|
8
8
|
var HEADER_TCP_MSS = "x-tls-tcp-mss";
|
|
9
9
|
var HEADER_TCP_WSCALE = "x-tls-tcp-wscale";
|
|
10
|
+
var HEADER_TCP_WINDOW = "x-tls-tcp-window";
|
|
11
|
+
var HEADER_TCP_OPTS_KINDS = "x-tls-tcp-opts-kinds";
|
|
12
|
+
var HEADER_TCP_OPTS_PRESENT = "x-tls-tcp-opts-present";
|
|
13
|
+
var HEADER_TCP_OPTS_COUNT = "x-tls-tcp-opts-count";
|
|
14
|
+
var HEADER_TCP_TSVAL = "x-tls-tcp-tsval";
|
|
15
|
+
var HEADER_TCP_TSECR = "x-tls-tcp-tsecr";
|
|
16
|
+
var HEADER_TCP_FLAGS = "x-tls-tcp-flags";
|
|
17
|
+
var HEADER_TCP_DATA_OFFSET_RESV = "x-tls-tcp-data-offset-resv";
|
|
18
|
+
var HEADER_TCP_URG_PTR = "x-tls-tcp-urg-ptr";
|
|
19
|
+
var HEADER_IP_IDENT = "x-tls-ip-ident";
|
|
20
|
+
var HEADER_IP_TOTAL_LEN = "x-tls-ip-total-len";
|
|
21
|
+
var HEADER_IP_FRAG_FLAGS = "x-tls-ip-frag-flags";
|
|
22
|
+
var HEADER_IP_TOS = "x-tls-ip-tos";
|
|
10
23
|
var HEADER_TCP_OPTS_FLAGS = "x-tls-tcp-opts-flags";
|
|
11
24
|
var HEADER_TCP_OPTS_ORDER = "x-tls-tcp-opts-order";
|
|
12
|
-
var
|
|
25
|
+
var RAW_TLS_SIGNAL_KEYS = [
|
|
26
|
+
"synNs",
|
|
27
|
+
"synackNs",
|
|
28
|
+
"ackNs",
|
|
29
|
+
"observedTtl",
|
|
30
|
+
"tcpMss",
|
|
31
|
+
"tcpWscale",
|
|
32
|
+
"tcpWindow",
|
|
33
|
+
"tcpOptsKinds",
|
|
34
|
+
"tcpOptsPresent",
|
|
35
|
+
"tcpOptsCount",
|
|
36
|
+
"tcpTsval",
|
|
37
|
+
"tcpTsecr",
|
|
38
|
+
"tcpFlags",
|
|
39
|
+
"tcpDataOffsetResv",
|
|
40
|
+
"tcpUrgPtr",
|
|
41
|
+
"ipIdent",
|
|
42
|
+
"ipTotalLen",
|
|
43
|
+
"ipFragFlags",
|
|
44
|
+
"ipTos",
|
|
45
|
+
"tcpOptsFlags",
|
|
46
|
+
"tcpOptsOrder"
|
|
47
|
+
];
|
|
13
48
|
var parseIntHeader = (raw, logger, headerName, max) => {
|
|
14
49
|
if (raw === void 0) return;
|
|
15
50
|
const value = Array.isArray(raw) ? raw[0] : raw;
|
|
@@ -31,6 +66,46 @@ var MAX_NS = 2 ** 63;
|
|
|
31
66
|
var MAX_U8 = 255;
|
|
32
67
|
var MAX_U16 = 65535;
|
|
33
68
|
var MAX_U32 = 4294967295;
|
|
69
|
+
var OPTS_KINDS_SLOTS = 8;
|
|
70
|
+
var MAX_U64 = (1n << 64n) - 1n;
|
|
71
|
+
var parseOptsKindsHeader = (raw, logger) => {
|
|
72
|
+
if (raw === void 0) return;
|
|
73
|
+
const value = Array.isArray(raw) ? raw[0] : raw;
|
|
74
|
+
if (value === void 0 || !/^\d+$/.test(value)) {
|
|
75
|
+
if (value !== void 0) logger.debug(() => ({
|
|
76
|
+
msg: "Ignoring malformed raw TLS signal header",
|
|
77
|
+
data: {
|
|
78
|
+
header: HEADER_TCP_OPTS_KINDS,
|
|
79
|
+
raw: value
|
|
80
|
+
}
|
|
81
|
+
}));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
let packed;
|
|
85
|
+
try {
|
|
86
|
+
packed = BigInt(value);
|
|
87
|
+
} catch {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (packed > MAX_U64) {
|
|
91
|
+
logger.debug(() => ({
|
|
92
|
+
msg: "Ignoring out-of-range raw TLS signal header",
|
|
93
|
+
data: {
|
|
94
|
+
header: HEADER_TCP_OPTS_KINDS,
|
|
95
|
+
raw: value
|
|
96
|
+
}
|
|
97
|
+
}));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (packed === 0n) return;
|
|
101
|
+
const kinds = [];
|
|
102
|
+
for (let slot = 0; slot < OPTS_KINDS_SLOTS; slot++) {
|
|
103
|
+
const kind = Number(packed >> BigInt(slot * 8) & 255n);
|
|
104
|
+
if (kind === 0) break;
|
|
105
|
+
kinds.push(kind);
|
|
106
|
+
}
|
|
107
|
+
return kinds.length > 0 ? kinds : void 0;
|
|
108
|
+
};
|
|
34
109
|
var getRawTlsSignals = (headers, logger) => {
|
|
35
110
|
const log = logger ?? (0, _prosopo_logger.getLogger)("info", "provider:raw-tls-signals");
|
|
36
111
|
return {
|
|
@@ -40,37 +115,50 @@ var getRawTlsSignals = (headers, logger) => {
|
|
|
40
115
|
observedTtl: parseIntHeader(headers[HEADER_OBSERVED_TTL], log, HEADER_OBSERVED_TTL, MAX_U8),
|
|
41
116
|
tcpMss: parseIntHeader(headers[HEADER_TCP_MSS], log, HEADER_TCP_MSS, MAX_U16),
|
|
42
117
|
tcpWscale: parseIntHeader(headers[HEADER_TCP_WSCALE], log, HEADER_TCP_WSCALE, MAX_U8),
|
|
118
|
+
tcpWindow: parseIntHeader(headers[HEADER_TCP_WINDOW], log, HEADER_TCP_WINDOW, MAX_U16),
|
|
119
|
+
tcpOptsKinds: parseOptsKindsHeader(headers[HEADER_TCP_OPTS_KINDS], log),
|
|
120
|
+
tcpOptsPresent: parseIntHeader(headers[HEADER_TCP_OPTS_PRESENT], log, HEADER_TCP_OPTS_PRESENT, MAX_U16),
|
|
121
|
+
tcpOptsCount: parseIntHeader(headers[HEADER_TCP_OPTS_COUNT], log, HEADER_TCP_OPTS_COUNT, MAX_U8),
|
|
122
|
+
tcpTsval: parseIntHeader(headers[HEADER_TCP_TSVAL], log, HEADER_TCP_TSVAL, MAX_U32),
|
|
123
|
+
tcpTsecr: parseIntHeader(headers[HEADER_TCP_TSECR], log, HEADER_TCP_TSECR, MAX_U32),
|
|
124
|
+
tcpFlags: parseIntHeader(headers[HEADER_TCP_FLAGS], log, HEADER_TCP_FLAGS, MAX_U8),
|
|
125
|
+
tcpDataOffsetResv: parseIntHeader(headers[HEADER_TCP_DATA_OFFSET_RESV], log, HEADER_TCP_DATA_OFFSET_RESV, MAX_U8),
|
|
126
|
+
tcpUrgPtr: parseIntHeader(headers[HEADER_TCP_URG_PTR], log, HEADER_TCP_URG_PTR, MAX_U16),
|
|
127
|
+
ipIdent: parseIntHeader(headers[HEADER_IP_IDENT], log, HEADER_IP_IDENT, MAX_U16),
|
|
128
|
+
ipTotalLen: parseIntHeader(headers[HEADER_IP_TOTAL_LEN], log, HEADER_IP_TOTAL_LEN, MAX_U16),
|
|
129
|
+
ipFragFlags: parseIntHeader(headers[HEADER_IP_FRAG_FLAGS], log, HEADER_IP_FRAG_FLAGS, MAX_U16),
|
|
130
|
+
ipTos: parseIntHeader(headers[HEADER_IP_TOS], log, HEADER_IP_TOS, MAX_U8),
|
|
43
131
|
tcpOptsFlags: parseIntHeader(headers[HEADER_TCP_OPTS_FLAGS], log, HEADER_TCP_OPTS_FLAGS, MAX_U8),
|
|
44
|
-
tcpOptsOrder: parseIntHeader(headers[HEADER_TCP_OPTS_ORDER], log, HEADER_TCP_OPTS_ORDER, MAX_U32)
|
|
45
|
-
tcpWindow: parseIntHeader(headers[HEADER_TCP_WINDOW], log, HEADER_TCP_WINDOW, MAX_U16)
|
|
132
|
+
tcpOptsOrder: parseIntHeader(headers[HEADER_TCP_OPTS_ORDER], log, HEADER_TCP_OPTS_ORDER, MAX_U32)
|
|
46
133
|
};
|
|
47
134
|
};
|
|
48
|
-
var rawTlsSignalsForSession = (req) =>
|
|
135
|
+
var rawTlsSignalsForSession = (req) => rawTlsSignalsFromRecord(req);
|
|
136
|
+
/**
|
|
137
|
+
* Pull the raw TLS signals out of a persisted Session — or anything shaped
|
|
138
|
+
* like one — for a decision machine's `raw` bag, or to carry forward onto a
|
|
139
|
+
* new Session record.
|
|
140
|
+
*
|
|
141
|
+
* Six call sites used to name every field by hand, so adding one to the
|
|
142
|
+
* probe meant remembering six places; the fields that were forgotten read as
|
|
143
|
+
* `undefined` and the rules gating on them silently never fired.
|
|
144
|
+
*/
|
|
145
|
+
var rawTlsSignalsFromRecord = (record) => {
|
|
49
146
|
const out = {};
|
|
50
|
-
if (
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (req.tcpWscale !== void 0) out.tcpWscale = req.tcpWscale;
|
|
56
|
-
if (req.tcpOptsFlags !== void 0) out.tcpOptsFlags = req.tcpOptsFlags;
|
|
57
|
-
if (req.tcpOptsOrder !== void 0) out.tcpOptsOrder = req.tcpOptsOrder;
|
|
58
|
-
if (req.tcpWindow !== void 0) out.tcpWindow = req.tcpWindow;
|
|
147
|
+
if (!record) return out;
|
|
148
|
+
for (const key of RAW_TLS_SIGNAL_KEYS) {
|
|
149
|
+
const value = record[key];
|
|
150
|
+
if (value !== void 0) Object.assign(out, { [key]: value });
|
|
151
|
+
}
|
|
59
152
|
return out;
|
|
60
153
|
};
|
|
61
154
|
var rawTlsSignalsMiddleware = (env) => {
|
|
62
155
|
return async (req, res, next) => {
|
|
63
156
|
try {
|
|
64
157
|
const signals = getRawTlsSignals(req.headers, req.logger);
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (signals.tcpMss !== void 0) req.tcpMss = signals.tcpMss;
|
|
70
|
-
if (signals.tcpWscale !== void 0) req.tcpWscale = signals.tcpWscale;
|
|
71
|
-
if (signals.tcpOptsFlags !== void 0) req.tcpOptsFlags = signals.tcpOptsFlags;
|
|
72
|
-
if (signals.tcpOptsOrder !== void 0) req.tcpOptsOrder = signals.tcpOptsOrder;
|
|
73
|
-
if (signals.tcpWindow !== void 0) req.tcpWindow = signals.tcpWindow;
|
|
158
|
+
for (const key of RAW_TLS_SIGNAL_KEYS) {
|
|
159
|
+
const value = signals[key];
|
|
160
|
+
if (value !== void 0) Object.assign(req, { [key]: value });
|
|
161
|
+
}
|
|
74
162
|
if (Object.values(signals).some((v) => v !== void 0)) req.logger = req.logger.with(signals, "rawTlsSignals");
|
|
75
163
|
next();
|
|
76
164
|
} catch (err) {
|
|
@@ -81,4 +169,5 @@ var rawTlsSignalsMiddleware = (env) => {
|
|
|
81
169
|
//#endregion
|
|
82
170
|
exports.getRawTlsSignals = getRawTlsSignals;
|
|
83
171
|
exports.rawTlsSignalsForSession = rawTlsSignalsForSession;
|
|
172
|
+
exports.rawTlsSignalsFromRecord = rawTlsSignalsFromRecord;
|
|
84
173
|
exports.rawTlsSignalsMiddleware = rawTlsSignalsMiddleware;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
2
2
|
const require_metrics = require("./metrics.cjs");
|
|
3
|
+
const require_rawTlsSignalsMiddleware = require("./rawTlsSignalsMiddleware.cjs");
|
|
3
4
|
const require_blacklistRequestInspector = require("./blacklistRequestInspector.cjs");
|
|
4
5
|
const require_bundlePool = require("../tasks/detection/bundlePool.cjs");
|
|
5
6
|
const require_block = require("./block.cjs");
|
|
6
|
-
const require_rawTlsSignalsMiddleware = require("./rawTlsSignalsMiddleware.cjs");
|
|
7
7
|
const require_captcha = require("./captcha.cjs");
|
|
8
8
|
const require_verify = require("./verify.cjs");
|
|
9
9
|
const require_ja4Middleware = require("./ja4Middleware.cjs");
|
|
@@ -11,20 +11,22 @@ const require_handshakeTimingMiddleware = require("./handshakeTimingMiddleware.c
|
|
|
11
11
|
const require_public = require("./public.cjs");
|
|
12
12
|
const require_domainMiddleware = require("./domainMiddleware.cjs");
|
|
13
13
|
const require_createApiAdminRoutesProvider = require("./admin/createApiAdminRoutesProvider.cjs");
|
|
14
|
+
const require_adminAuthOptions = require("./adminAuthOptions.cjs");
|
|
14
15
|
const require_cpuProfiler = require("./cpuProfiler.cjs");
|
|
16
|
+
const require_detectorPoolBodyParser = require("./detectorPoolBodyParser.cjs");
|
|
15
17
|
const require_headerCheckMiddleware = require("./headerCheckMiddleware.cjs");
|
|
16
18
|
const require_ignoreMiddleware = require("./ignoreMiddleware.cjs");
|
|
17
19
|
const require_ipInfoMiddleware = require("./ipInfoMiddleware.cjs");
|
|
18
20
|
const require_robotsMiddleware = require("./robotsMiddleware.cjs");
|
|
19
21
|
let _prosopo_logger = require("@prosopo/logger");
|
|
20
22
|
let _prosopo_types = require("@prosopo/types");
|
|
23
|
+
let _prosopo_api_express_router = require("@prosopo/api-express-router");
|
|
21
24
|
let node_fs = require("node:fs");
|
|
22
25
|
node_fs = require_runtime.__toESM(node_fs, 1);
|
|
23
26
|
let node_path = require("node:path");
|
|
24
27
|
node_path = require_runtime.__toESM(node_path, 1);
|
|
25
28
|
let node_https = require("node:https");
|
|
26
29
|
node_https = require_runtime.__toESM(node_https, 1);
|
|
27
|
-
let _prosopo_api_express_router = require("@prosopo/api-express-router");
|
|
28
30
|
let express = require("express");
|
|
29
31
|
express = require_runtime.__toESM(express, 1);
|
|
30
32
|
let node_url = require("node:url");
|
|
@@ -162,7 +164,7 @@ async function startProviderApi(env, admin = false, port) {
|
|
|
162
164
|
],
|
|
163
165
|
maxAge: 86400
|
|
164
166
|
}));
|
|
165
|
-
apiApp.use(_prosopo_types.AdminApiPaths.ReplaceDetectorPool,
|
|
167
|
+
apiApp.use(_prosopo_types.AdminApiPaths.ReplaceDetectorPool, require_detectorPoolBodyParser.detectorPoolBodyParser(DETECTOR_POOL_BODY_LIMIT, env.pair, env.authAccount));
|
|
166
168
|
apiApp.use(express.default.json({ limit: "1mb" }));
|
|
167
169
|
apiApp.use(require_public.publicRouter(env));
|
|
168
170
|
apiApp.use(require_metrics.metricsMiddleware());
|
|
@@ -218,10 +220,11 @@ async function startProviderApi(env, admin = false, port) {
|
|
|
218
220
|
apiApp.use(clientPathsExcludingVerify, require_headerCheckMiddleware.headerCheckMiddleware(env));
|
|
219
221
|
apiApp.use(require_verify.prosopoVerifyRouter(env));
|
|
220
222
|
env.logger.info(() => ({ msg: "Enabling admin auth middleware" }));
|
|
221
|
-
|
|
223
|
+
const adminAuth = (0, _prosopo_api_express_router.authMiddleware)(env.pair, env.authAccount, require_adminAuthOptions.adminAuthOptions(process.env, env.config.host));
|
|
224
|
+
apiApp.use("/v1/prosopo/provider/admin", adminAuth);
|
|
222
225
|
if (apiRuleRoutesProvider) {
|
|
223
226
|
const userAccessRuleRoutes = apiRuleRoutesProvider.getRoutes();
|
|
224
|
-
for (const userAccessRuleRoute in userAccessRuleRoutes) apiApp.use(userAccessRuleRoute,
|
|
227
|
+
for (const userAccessRuleRoute in userAccessRuleRoutes) apiApp.use(userAccessRuleRoute, adminAuth);
|
|
225
228
|
for (const userAccessRuleRoute in userAccessRuleRoutes) apiApp.use(userAccessRuleRoute, (req, res, next) => {
|
|
226
229
|
if (req.method === "POST") res.on("finish", () => {
|
|
227
230
|
if (res.statusCode >= 200 && res.statusCode < 300) require_blacklistRequestInspector.getVerdictCache().clear();
|
package/dist/cjs/api/verify.cjs
CHANGED
|
@@ -30,6 +30,36 @@ var VERIFY_PATH_TYPE = {
|
|
|
30
30
|
[_prosopo_types.ClientApiPaths.VerifyPowCaptchaSolution]: _prosopo_types.CaptchaType.pow,
|
|
31
31
|
[_prosopo_types.ClientApiPaths.VerifyPuzzleCaptchaSolution]: _prosopo_types.CaptchaType.puzzle
|
|
32
32
|
};
|
|
33
|
+
var decodeTokenOr400 = (token, i18n, logger, next) => {
|
|
34
|
+
try {
|
|
35
|
+
return (0, _prosopo_types.decodeProcaptchaOutput)(token);
|
|
36
|
+
} catch (error) {
|
|
37
|
+
next(new _prosopo_common.ProsopoApiError("CAPTCHA.PARSE_ERROR", {
|
|
38
|
+
context: {
|
|
39
|
+
code: 400,
|
|
40
|
+
error
|
|
41
|
+
},
|
|
42
|
+
i18n,
|
|
43
|
+
logger
|
|
44
|
+
}));
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
var dappSignatureError = (signature, message, pair, req) => {
|
|
49
|
+
try {
|
|
50
|
+
(0, _prosopo_api_express_router.verifySignature)(signature, message, pair);
|
|
51
|
+
return;
|
|
52
|
+
} catch {
|
|
53
|
+
return new _prosopo_common.ProsopoApiError("GENERAL.INVALID_SIGNATURE", {
|
|
54
|
+
context: {
|
|
55
|
+
code: 400,
|
|
56
|
+
siteKey: pair.address
|
|
57
|
+
},
|
|
58
|
+
i18n: req.i18n,
|
|
59
|
+
logger: req.logger
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
};
|
|
33
63
|
/**
|
|
34
64
|
* Returns a router connected to the database which can interact with the Proposo protocol
|
|
35
65
|
*
|
|
@@ -98,7 +128,9 @@ function prosopoVerifyRouter(env) {
|
|
|
98
128
|
}
|
|
99
129
|
const { dappSignature, token, ip, maxVerifiedTime, email, clientSessionId } = parsed;
|
|
100
130
|
try {
|
|
101
|
-
const
|
|
131
|
+
const decoded = decodeTokenOr400(token, req.i18n, req.logger, next);
|
|
132
|
+
if (decoded === null) return;
|
|
133
|
+
const { user, dapp, timestamp, commitmentId, providerUrl } = decoded;
|
|
102
134
|
const testVerdict = require_testSiteKey.resolveTestSiteKeyVerdict(dapp, req.logger);
|
|
103
135
|
if (testVerdict !== null) {
|
|
104
136
|
const verificationResponse = {
|
|
@@ -132,7 +164,8 @@ function prosopoVerifyRouter(env) {
|
|
|
132
164
|
logger: req.logger
|
|
133
165
|
}));
|
|
134
166
|
const keyPair = env.keyring.addFromAddress(dapp);
|
|
135
|
-
|
|
167
|
+
const signatureError = dappSignatureError(dappSignature, timestamp.toString(), keyPair, req);
|
|
168
|
+
if (signatureError) return next(signatureError);
|
|
136
169
|
const response = await tasks.imgCaptchaManager.verifyImageCaptchaSolution(user, dapp, commitmentId, env, maxVerifiedTime, ip, clientRecord.settings.disallowWebView, userAccessRulesStorage, email, clientRecord.settings.spamEmailDomainCheckEnabled, clientRecord.settings.spamFilter, clientRecord.settings.trafficFilter, clientRecord.settings.storeMetadata, clientSessionId);
|
|
137
170
|
req.logger.debug(() => ({ data: { response } }));
|
|
138
171
|
const verificationResponse = tasks.imgCaptchaManager.getVerificationResponse(response[_prosopo_types.ApiParams.verified], clientRecord, req.i18n.t, response[_prosopo_types.ApiParams.score], response[_prosopo_types.ApiParams.commitmentId], response[_prosopo_types.ApiParams.status], response[_prosopo_types.ApiParams.sessionId]);
|
|
@@ -183,7 +216,9 @@ function prosopoVerifyRouter(env) {
|
|
|
183
216
|
}
|
|
184
217
|
try {
|
|
185
218
|
const { token, dappSignature, ip, email, clientSessionId } = parsed;
|
|
186
|
-
const
|
|
219
|
+
const decoded = decodeTokenOr400(token, req.i18n, req.logger, next);
|
|
220
|
+
if (decoded === null) return;
|
|
221
|
+
const { dapp, user, timestamp, challenge, providerUrl } = decoded;
|
|
187
222
|
const testVerdict = require_testSiteKey.resolveTestSiteKeyVerdict(dapp, req.logger);
|
|
188
223
|
if (testVerdict !== null) {
|
|
189
224
|
const verificationResponse = {
|
|
@@ -223,7 +258,8 @@ function prosopoVerifyRouter(env) {
|
|
|
223
258
|
return res.json(unverifiedResponse);
|
|
224
259
|
}
|
|
225
260
|
const dappPair = env.keyring.addFromAddress(dapp);
|
|
226
|
-
|
|
261
|
+
const signatureError = dappSignatureError(dappSignature, timestamp.toString(), dappPair, req);
|
|
262
|
+
if (signatureError) return next(signatureError);
|
|
227
263
|
const { verified, score, reason, sessionId } = await tasks.powCaptchaManager.serverVerifyPowCaptchaSolution(dapp, challenge, clientRecord.settings.verifiedTimeout, env, ip, userAccessRulesStorage, email, clientRecord.settings.spamEmailDomainCheckEnabled, clientRecord.settings.spamFilter, clientRecord.settings.trafficFilter, clientRecord.settings.storeMetadata, clientSessionId);
|
|
228
264
|
const verificationResponse = tasks.powCaptchaManager.getVerificationResponse(verified, clientRecord, req.i18n.t, score, reason, sessionId);
|
|
229
265
|
return res.json(verificationResponse);
|
|
@@ -272,7 +308,9 @@ function prosopoVerifyRouter(env) {
|
|
|
272
308
|
}
|
|
273
309
|
try {
|
|
274
310
|
const { token, dappSignature, ip, email, clientSessionId } = parsed;
|
|
275
|
-
const
|
|
311
|
+
const decoded = decodeTokenOr400(token, req.i18n, req.logger, next);
|
|
312
|
+
if (decoded === null) return;
|
|
313
|
+
const { dapp, user, timestamp, challenge, providerUrl } = decoded;
|
|
276
314
|
const testVerdict = require_testSiteKey.resolveTestSiteKeyVerdict(dapp, req.logger);
|
|
277
315
|
if (testVerdict !== null) {
|
|
278
316
|
const verificationResponse = {
|
|
@@ -312,7 +350,8 @@ function prosopoVerifyRouter(env) {
|
|
|
312
350
|
return res.json(unverifiedResponse);
|
|
313
351
|
}
|
|
314
352
|
const dappPair = env.keyring.addFromAddress(dapp);
|
|
315
|
-
|
|
353
|
+
const signatureError = dappSignatureError(dappSignature, timestamp.toString(), dappPair, req);
|
|
354
|
+
if (signatureError) return next(signatureError);
|
|
316
355
|
const { verified, score, sessionId } = await tasks.puzzleCaptchaManager.serverVerifyPuzzleCaptchaSolution(dapp, challenge, clientRecord.settings.verifiedTimeout, env, ip, userAccessRulesStorage, email, clientRecord.settings.spamEmailDomainCheckEnabled, clientRecord.settings.spamFilter, clientRecord.settings.trafficFilter, clientRecord.settings.storeMetadata, clientSessionId);
|
|
317
356
|
const verificationResponse = tasks.puzzleCaptchaManager.getVerificationResponse(verified, clientRecord, req.i18n.t, score, void 0, sessionId);
|
|
318
357
|
return res.json(verificationResponse);
|
|
@@ -361,7 +400,9 @@ function prosopoVerifyRouter(env) {
|
|
|
361
400
|
}
|
|
362
401
|
const { dappSignature, token, ip, clientSessionId } = parsed;
|
|
363
402
|
try {
|
|
364
|
-
const
|
|
403
|
+
const decoded = decodeTokenOr400(token, req.i18n, req.logger, next);
|
|
404
|
+
if (decoded === null) return;
|
|
405
|
+
const { user, dapp, timestamp, commitmentId: sessionId, providerUrl } = decoded;
|
|
365
406
|
const testVerdict = require_testSiteKey.resolveTestSiteKeyVerdict(dapp, req.logger);
|
|
366
407
|
if (testVerdict !== null) return res.json({
|
|
367
408
|
status: "ok",
|
|
@@ -391,7 +432,8 @@ function prosopoVerifyRouter(env) {
|
|
|
391
432
|
logger: req.logger
|
|
392
433
|
}));
|
|
393
434
|
const keyPair = env.keyring.addFromAddress(dapp);
|
|
394
|
-
|
|
435
|
+
const signatureError = dappSignatureError(dappSignature, timestamp.toString(), keyPair, req);
|
|
436
|
+
if (signatureError) return next(signatureError);
|
|
395
437
|
if (!sessionId) return res.json({
|
|
396
438
|
status: "API.USER_NOT_VERIFIED_NO_SOLUTION",
|
|
397
439
|
verified: false
|
|
@@ -4,6 +4,11 @@ let ip_address = require("ip-address");
|
|
|
4
4
|
//#region src/compositeIpAddress.ts
|
|
5
5
|
var V6_SHIFT = 64n;
|
|
6
6
|
var v6_LOWER_MASK = (1n << V6_SHIFT) - 1n;
|
|
7
|
+
/**
|
|
8
|
+
* The part of a v4 address treated as the host rather than the network, for
|
|
9
|
+
* the purposes of `isSameIpOrigin`. 8 bits — a /24.
|
|
10
|
+
*/
|
|
11
|
+
var V4_HOST_BITS = 8n;
|
|
7
12
|
var getCompositeIpAddress = (ip) => {
|
|
8
13
|
let ipAddress;
|
|
9
14
|
try {
|
|
@@ -60,10 +65,19 @@ var isUnknownIp = (ip) => ip.type === _prosopo_types.IpAddressType.v4 && readPar
|
|
|
60
65
|
/**
|
|
61
66
|
* Do two observations of a client come from the same place on the network?
|
|
62
67
|
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
68
|
+
* Both families are compared on the network part and neither on the host part.
|
|
69
|
+
*
|
|
70
|
+
* v6 is the /64 prefix: the interface identifier in the low 64 bits is designed
|
|
71
|
+
* to rotate (RFC 8981 privacy extensions), often several times a day on one
|
|
72
|
+
* unchanged connection, so comparing it would call an ordinary phone a
|
|
73
|
+
* different host.
|
|
74
|
+
*
|
|
75
|
+
* v4 is the /24. A carrier NAT or mobile pool reassigns the low octet mid
|
|
76
|
+
* session — an EE subscriber was observed moving one address in four seconds —
|
|
77
|
+
* so comparing the whole address called every rotating mobile and CGNAT client
|
|
78
|
+
* a different host, which is the opposite of the leniency this check is for.
|
|
79
|
+
* The /24 still separates an unrelated network, which is the case worth
|
|
80
|
+
* catching: a challenge handed to a second host to farm the work out.
|
|
67
81
|
*
|
|
68
82
|
* A value we do not actually have counts as the same — one that will not
|
|
69
83
|
* parse, or the all-zero v4 `getCompositeIpAddress` returns when handed an
|
|
@@ -73,13 +87,13 @@ var isUnknownIp = (ip) => ip.type === _prosopo_types.IpAddressType.v4 && readPar
|
|
|
73
87
|
var isSameIpOrigin = (a, b) => {
|
|
74
88
|
if (isUnknownIp(a) || isUnknownIp(b)) return true;
|
|
75
89
|
if (a.type !== b.type) return false;
|
|
76
|
-
const
|
|
90
|
+
const sameNetwork = (left, right, hostBits) => {
|
|
77
91
|
const leftPart = readPart(left);
|
|
78
92
|
const rightPart = readPart(right);
|
|
79
93
|
if (leftPart === void 0 || rightPart === void 0) return true;
|
|
80
|
-
return leftPart === rightPart;
|
|
94
|
+
return leftPart >> hostBits === rightPart >> hostBits;
|
|
81
95
|
};
|
|
82
|
-
return a.type === _prosopo_types.IpAddressType.v6 ?
|
|
96
|
+
return a.type === _prosopo_types.IpAddressType.v6 ? sameNetwork(a.upper, b.upper, 0n) : sameNetwork(a.lower, b.lower, V4_HOST_BITS);
|
|
83
97
|
};
|
|
84
98
|
var never = () => {
|
|
85
99
|
throw new Error("Unhandled type");
|
|
@@ -225,6 +225,21 @@ var CaptchaManager = class CaptchaManager {
|
|
|
225
225
|
sessionId
|
|
226
226
|
}
|
|
227
227
|
}));
|
|
228
|
+
if (!(0, _prosopo_types.isCaptchaTypeFeatureEnabled)(requestedCaptchaType, clientSettings.settings?.captchaTypeFeatureFlags)) {
|
|
229
|
+
this.logger.warn(() => ({
|
|
230
|
+
msg: "Captcha type is switched off for this site",
|
|
231
|
+
data: {
|
|
232
|
+
account: clientSettings.account,
|
|
233
|
+
requestedCaptchaType,
|
|
234
|
+
sessionId
|
|
235
|
+
}
|
|
236
|
+
}));
|
|
237
|
+
return {
|
|
238
|
+
valid: false,
|
|
239
|
+
reason: _prosopo_types.ResultReason.INCORRECT_CAPTCHA_TYPE,
|
|
240
|
+
type: requestedCaptchaType
|
|
241
|
+
};
|
|
242
|
+
}
|
|
228
243
|
if (sessionId) {
|
|
229
244
|
const cachedBeforeRemove = this.writeQueue ? await this.writeQueue.getCachedSession(sessionId) : null;
|
|
230
245
|
const cachedHash = typeof cachedBeforeRemove?.userSitekeyIpHash === "string" ? cachedBeforeRemove.userSitekeyIpHash : void 0;
|
|
@@ -18,12 +18,16 @@ let _prosopo_types = require("@prosopo/types");
|
|
|
18
18
|
* Restrict rules, traffic-filter category policies, routing-machine actions
|
|
19
19
|
* and detector-generated rules.
|
|
20
20
|
*
|
|
21
|
+
* Pass `allowedTypes` from `resolveAllowedCaptchaTypes`, so Prosopo's
|
|
22
|
+
* per-site captcha type feature flags are applied alongside the site owner's
|
|
23
|
+
* own `frictionlessTypes`. Absent means every type is allowed.
|
|
24
|
+
*
|
|
21
25
|
* Coercion only ever narrows. A disabled type falls back to the other
|
|
22
26
|
* interactive type if that is permitted, and to PoW otherwise — never the
|
|
23
27
|
* reverse, so this can't hand a user a harder challenge than was asked for.
|
|
24
28
|
*/
|
|
25
|
-
var coerceToEnabledCaptchaType = (requested,
|
|
26
|
-
const types = (0, _prosopo_types.resolveFrictionlessTypes)(
|
|
29
|
+
var coerceToEnabledCaptchaType = (requested, allowedTypes, logger) => {
|
|
30
|
+
const types = (0, _prosopo_types.resolveFrictionlessTypes)(allowedTypes);
|
|
27
31
|
const puzzleAllowed = types.puzzle && require_puzzleRenderer.isPuzzleRenderAvailable();
|
|
28
32
|
const imageAllowed = types.image;
|
|
29
33
|
const resolved = (() => {
|
|
@@ -49,5 +53,22 @@ var coerceToEnabledCaptchaType = (requested, frictionlessTypes, logger) => {
|
|
|
49
53
|
}));
|
|
50
54
|
return resolved;
|
|
51
55
|
};
|
|
56
|
+
/**
|
|
57
|
+
* Swap a puzzle for an image challenge once the user has refreshed it
|
|
58
|
+
* `PUZZLE_REFRESHES_BEFORE_IMAGE` times in a row.
|
|
59
|
+
*
|
|
60
|
+
* A user who keeps refreshing either cannot solve this kind of puzzle or is a
|
|
61
|
+
* solver shopping for an easy instance. The image challenge answers both.
|
|
62
|
+
*
|
|
63
|
+
* The refresh count is client-reported, so this only moves one way: from
|
|
64
|
+
* puzzle to image, never back. Lying about it can only earn a harder
|
|
65
|
+
* challenge. Leaving it out looks like a fresh visit, which is what a page
|
|
66
|
+
* reload already gets.
|
|
67
|
+
*
|
|
68
|
+
* A site that has not enabled image keeps its puzzle. Serving a type the site
|
|
69
|
+
* turned off is exactly what `coerceToEnabledCaptchaType` exists to stop.
|
|
70
|
+
*/
|
|
71
|
+
var switchTypeAfterRefreshes = (resolved, refreshCount, allowedTypes) => _prosopo_types.CaptchaType.puzzle === resolved && (refreshCount ?? 0) >= 3 && (0, _prosopo_types.resolveFrictionlessTypes)(allowedTypes).image ? _prosopo_types.CaptchaType.image : resolved;
|
|
52
72
|
//#endregion
|
|
53
73
|
exports.coerceToEnabledCaptchaType = coerceToEnabledCaptchaType;
|
|
74
|
+
exports.switchTypeAfterRefreshes = switchTypeAfterRefreshes;
|
|
@@ -5,6 +5,29 @@ let _prosopo_database = require("@prosopo/database");
|
|
|
5
5
|
let _prosopo_types = require("@prosopo/types");
|
|
6
6
|
require("node:crypto");
|
|
7
7
|
let _prosopo_util = require("@prosopo/util");
|
|
8
|
+
/**
|
|
9
|
+
* Matches `text` against a pattern where `*` matches any run of characters.
|
|
10
|
+
* Greedy two-pointer match, retrying from the last `*` on mismatch, so time
|
|
11
|
+
* is bounded by pattern length times text length.
|
|
12
|
+
*/
|
|
13
|
+
var matchesGlob = (pattern, text) => {
|
|
14
|
+
let p = 0;
|
|
15
|
+
let t = 0;
|
|
16
|
+
let starP = -1;
|
|
17
|
+
let starT = 0;
|
|
18
|
+
while (t < text.length) if (p < pattern.length && pattern[p] === "*") {
|
|
19
|
+
starP = p++;
|
|
20
|
+
starT = t;
|
|
21
|
+
} else if (p < pattern.length && pattern[p] === text[t]) {
|
|
22
|
+
p++;
|
|
23
|
+
t++;
|
|
24
|
+
} else if (starP !== -1) {
|
|
25
|
+
p = starP + 1;
|
|
26
|
+
t = ++starT;
|
|
27
|
+
} else return false;
|
|
28
|
+
while (p < pattern.length && pattern[p] === "*") p++;
|
|
29
|
+
return p === pattern.length;
|
|
30
|
+
};
|
|
8
31
|
var ClientTaskManager = class {
|
|
9
32
|
constructor(config, logger, db) {
|
|
10
33
|
this.config = config;
|
|
@@ -251,10 +274,7 @@ var ClientTaskManager = class {
|
|
|
251
274
|
const allowed = (0, _prosopo_util.parseUrl)(suffix).hostname.replace(/\.$/, "");
|
|
252
275
|
return referrerHost.endsWith(`.${allowed}`) || referrerHost === allowed;
|
|
253
276
|
}
|
|
254
|
-
if (pattern.includes("*"))
|
|
255
|
-
const escaped = pattern.replace(/[.+?^${}()|\[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
256
|
-
return new RegExp(`^${escaped}$`, "i").test(referrerHost);
|
|
257
|
-
}
|
|
277
|
+
if (pattern.includes("*")) return matchesGlob(pattern, referrerHost.toLowerCase());
|
|
258
278
|
const allowedHost = (0, _prosopo_util.parseUrl)(pattern).hostname.replace(/\.$/, "");
|
|
259
279
|
return referrerHost === allowedHost || referrerHost.endsWith(`.${allowedHost}`);
|
|
260
280
|
} catch (e) {
|
|
@@ -297,3 +317,4 @@ var ClientTaskManager = class {
|
|
|
297
317
|
};
|
|
298
318
|
//#endregion
|
|
299
319
|
exports.ClientTaskManager = ClientTaskManager;
|
|
320
|
+
exports.matchesGlob = matchesGlob;
|
|
@@ -5,7 +5,7 @@ var providerValidateDataset = async (datasetRaw, minSolvedCaptchas, minUnsolvedC
|
|
|
5
5
|
if (datasetRaw.captchas.length < minSolvedCaptchas + minUnsolvedCaptchas) throw new _prosopo_common.ProsopoEnvError("DATASET.CAPTCHAS_COUNT_LESS_THAN_CONFIGURED", { context: { failedFuncName: providerValidateDataset.name } });
|
|
6
6
|
const solutions = datasetRaw.captchas.map((captcha) => captcha.solution ? 1 : 0).reduce((partialSum, b) => partialSum + b, 0);
|
|
7
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.
|
|
8
|
+
if (datasetRaw.captchas.length - solutions < minUnsolvedCaptchas) throw new _prosopo_common.ProsopoEnvError("DATASET.CAPTCHAS_COUNT_LESS_THAN_CONFIGURED", { context: { failedFuncName: providerValidateDataset.name } });
|
|
9
9
|
const dataset = await (0, _prosopo_datasets.buildDataset)(datasetRaw);
|
|
10
10
|
if (!dataset.datasetId || !dataset.datasetContentId) throw new _prosopo_common.ProsopoEnvError("DATASET.DATASET_ID_UNDEFINED", { context: {
|
|
11
11
|
failedFuncName: providerValidateDataset.name,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const require_usageCounters = require("../../util/usageCounters.cjs");
|
|
2
2
|
const require_decisionMachineRunner = require("../decisionMachine/decisionMachineRunner.cjs");
|
|
3
3
|
const require_dnsEventUrl = require("../../api/dnsEventUrl.cjs");
|
|
4
|
+
const require_rawTlsSignalsMiddleware = require("../../api/rawTlsSignalsMiddleware.cjs");
|
|
4
5
|
const require_lang = require("../../rules/lang.cjs");
|
|
5
6
|
const require_clientMetaData = require("../../utils/clientMetaData.cjs");
|
|
6
7
|
const require_captchaManager = require("../captchaManager.cjs");
|
|
@@ -33,6 +34,15 @@ var FrictionlessManager = class extends require_captchaManager.CaptchaManager {
|
|
|
33
34
|
this.routingContext = ctx;
|
|
34
35
|
}
|
|
35
36
|
/**
|
|
37
|
+
* The types this site may be served, from `resolveAllowedCaptchaTypes`.
|
|
38
|
+
* Set before any path can mint a session, including the short-circuits
|
|
39
|
+
* that skip routing, so the site's feature flags hold on every path.
|
|
40
|
+
* Unset means every type is allowed.
|
|
41
|
+
*/
|
|
42
|
+
setAllowedCaptchaTypes(allowed) {
|
|
43
|
+
this.allowedCaptchaTypes = allowed;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
36
46
|
* Evaluate the configured routing machine without going through
|
|
37
47
|
* `sendCaptcha`. Returns the supplied baseline on any failure (no machine,
|
|
38
48
|
* machine throws, counter fetch failure), matching applyRouter's contract.
|
|
@@ -63,15 +73,10 @@ var FrictionlessManager = class extends require_captchaManager.CaptchaManager {
|
|
|
63
73
|
d: params.d,
|
|
64
74
|
tcpToChelloUs: params.tcpToChelloUs,
|
|
65
75
|
chelloToHandshakeUs: params.chelloToHandshakeUs,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
tcpMss: params.tcpMss,
|
|
71
|
-
tcpWscale: params.tcpWscale,
|
|
72
|
-
tcpOptsFlags: params.tcpOptsFlags,
|
|
73
|
-
tcpOptsOrder: params.tcpOptsOrder,
|
|
74
|
-
tcpWindow: params.tcpWindow
|
|
76
|
+
...require_rawTlsSignalsMiddleware.rawTlsSignalsFromRecord(params),
|
|
77
|
+
refreshOf: params.refreshOf,
|
|
78
|
+
refreshCount: params.refreshCount,
|
|
79
|
+
refreshedAfterMs: params.refreshedAfterMs
|
|
75
80
|
};
|
|
76
81
|
}
|
|
77
82
|
/**
|
|
@@ -91,7 +96,7 @@ var FrictionlessManager = class extends require_captchaManager.CaptchaManager {
|
|
|
91
96
|
return require_lang.checkLangRules(this.config, acceptLanguage);
|
|
92
97
|
}
|
|
93
98
|
async createSession(input) {
|
|
94
|
-
const { token, score, threshold, scoreComponents, ipAddress, captchaType, siteKey, mode, solvedImagesCount, powDifficulty, userSitekeyIpHash, reason, blocked, deleted, ipInfo, headers, bundleId, currentUrl, iframeUrl, d, tcpToChelloUs, chelloToHandshakeUs,
|
|
99
|
+
const { token, score, threshold, scoreComponents, ipAddress, captchaType, siteKey, mode, solvedImagesCount, powDifficulty, userSitekeyIpHash, reason, blocked, deleted, ipInfo, headers, bundleId, currentUrl, iframeUrl, d, tcpToChelloUs, chelloToHandshakeUs, simdReadings, puzzleTolerance, puzzle, isEscalation, originSessionId, refreshOf, refreshCount, refreshedAfterMs, isProtect, matchedRule, clientMetaData, webView = false, iFrame = false, decryptedHeadHash = "" } = input;
|
|
95
100
|
const sessionRecord = {
|
|
96
101
|
sessionId: `${getSessionIDPrefix(this.config.host)}-${(0, uuid.v4)()}`,
|
|
97
102
|
createdAt: /* @__PURE__ */ new Date(),
|
|
@@ -111,6 +116,9 @@ var FrictionlessManager = class extends require_captchaManager.CaptchaManager {
|
|
|
111
116
|
iFrame,
|
|
112
117
|
...isEscalation && { isEscalation: true },
|
|
113
118
|
...originSessionId && { originSessionId },
|
|
119
|
+
...refreshOf && { refreshOf },
|
|
120
|
+
...refreshCount !== void 0 && { refreshCount },
|
|
121
|
+
...refreshedAfterMs !== void 0 && { refreshedAfterMs },
|
|
114
122
|
decryptedHeadHash,
|
|
115
123
|
bundleId,
|
|
116
124
|
reason,
|
|
@@ -127,15 +135,7 @@ var FrictionlessManager = class extends require_captchaManager.CaptchaManager {
|
|
|
127
135
|
d,
|
|
128
136
|
tcpToChelloUs,
|
|
129
137
|
chelloToHandshakeUs,
|
|
130
|
-
|
|
131
|
-
synackNs,
|
|
132
|
-
ackNs,
|
|
133
|
-
observedTtl,
|
|
134
|
-
tcpMss,
|
|
135
|
-
tcpWscale,
|
|
136
|
-
tcpOptsFlags,
|
|
137
|
-
tcpOptsOrder,
|
|
138
|
-
tcpWindow,
|
|
138
|
+
...require_rawTlsSignalsMiddleware.rawTlsSignalsFromRecord(input),
|
|
139
139
|
...matchedRule && { matchedRule },
|
|
140
140
|
...clientMetaData && { clientMetaData }
|
|
141
141
|
};
|
|
@@ -247,14 +247,16 @@ var FrictionlessManager = class extends require_captchaManager.CaptchaManager {
|
|
|
247
247
|
powDifficulty: captchaType === _prosopo_types.CaptchaType.pow ? effectiveParams.powDifficulty : void 0
|
|
248
248
|
};
|
|
249
249
|
const routed = this.routingContext ? await require_routingMachine.applyRouter(this.decisionMachineRunner, this.usageCounters, baseline, this.routingContext, this.logger) : baseline;
|
|
250
|
-
const
|
|
250
|
+
const enabledCaptchaType = require_captchaTypeSelection.coerceToEnabledCaptchaType(routed.captchaType, this.allowedCaptchaTypes, this.logger);
|
|
251
|
+
const finalCaptchaType = require_captchaTypeSelection.switchTypeAfterRefreshes(enabledCaptchaType, effectiveParams.refreshCount, this.allowedCaptchaTypes);
|
|
252
|
+
const switchedByRefreshes = finalCaptchaType !== enabledCaptchaType;
|
|
251
253
|
const requestedSolvedImagesCount = routed.solvedImagesCount ?? effectiveParams.solvedImagesCount;
|
|
252
254
|
const imageRoundsBounds = this.routingContext ?? {};
|
|
253
255
|
const finalSolvedImagesCount = finalCaptchaType === _prosopo_types.CaptchaType.image ? requestedSolvedImagesCount !== void 0 ? (0, _prosopo_types.clampImageRounds)(requestedSolvedImagesCount, imageRoundsBounds) : requestedSolvedImagesCount : void 0;
|
|
254
256
|
const finalPowDifficulty = finalCaptchaType === _prosopo_types.CaptchaType.pow ? routed.powDifficulty ?? effectiveParams.powDifficulty : void 0;
|
|
255
|
-
const finalReason = routed.reason ?? effectiveParams.reason;
|
|
257
|
+
const finalReason = switchedByRefreshes ? _prosopo_types.FrictionlessReason.PUZZLE_REFRESH_LIMIT : routed.reason ?? effectiveParams.reason;
|
|
256
258
|
const finalPuzzleOverrides = finalCaptchaType === _prosopo_types.CaptchaType.puzzle ? (() => {
|
|
257
|
-
const maxLevel = this.routingContext?.puzzleMaxDifficulty ?? _prosopo_types.puzzleMaxDifficultyDefault;
|
|
259
|
+
const maxLevel = (0, _prosopo_captcha_severity.resolveMaxEscalationLevel)(this.routingContext?.puzzleMaxDifficulty ?? _prosopo_types.puzzleMaxDifficultyDefault, this.routingContext?.platform.isMobile ?? false);
|
|
258
260
|
const level = finalReason !== void 0 && _prosopo_types.NO_MEASUREMENT_REASONS.has(finalReason) ? 0 : (0, _prosopo_captcha_severity.severityToPuzzleDifficulty)(requestedSolvedImagesCount, this.routingContext?.baseImageRounds ?? this.config.captchas.solved.count, maxLevel);
|
|
259
261
|
const difficulty = level > 0 ? require_puzzleDifficulty.samplePuzzleDifficulty(level, _prosopo_puzzle_assets.DEFAULT_RENDER_SETTINGS.holeDarken) : void 0;
|
|
260
262
|
const tolerance = routed.puzzleTolerance ?? difficulty?.tolerance;
|
|
@@ -264,7 +266,8 @@ var FrictionlessManager = class extends require_captchaManager.CaptchaManager {
|
|
|
264
266
|
} : void 0;
|
|
265
267
|
return {
|
|
266
268
|
...tolerance !== void 0 && { puzzleTolerance: tolerance },
|
|
267
|
-
...puzzle !== void 0 && { puzzle }
|
|
269
|
+
...puzzle !== void 0 && { puzzle },
|
|
270
|
+
...difficulty !== void 0 && { puzzleLevel: difficulty.level }
|
|
268
271
|
};
|
|
269
272
|
})() : {};
|
|
270
273
|
const blocked = finalCaptchaType === _prosopo_types.CaptchaType.image ? effectiveParams.blocked : void 0;
|