@astrasyncai/verification-gateway 4.2.1 → 4.4.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/README.md +45 -21
- package/dist/adapter-interface/interface.d.mts +2 -2
- package/dist/adapter-interface/interface.d.ts +2 -2
- package/dist/adapters/express.d.mts +2 -2
- package/dist/adapters/express.d.ts +2 -2
- package/dist/adapters/express.js +145 -21
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +145 -21
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/mcp.d.mts +38 -30
- package/dist/adapters/mcp.d.ts +38 -30
- package/dist/adapters/mcp.js +72 -16
- package/dist/adapters/mcp.js.map +1 -1
- package/dist/adapters/mcp.mjs +72 -16
- package/dist/adapters/mcp.mjs.map +1 -1
- package/dist/adapters/nextjs.d.mts +2 -2
- package/dist/adapters/nextjs.d.ts +2 -2
- package/dist/adapters/nextjs.js +141 -19
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +141 -19
- package/dist/adapters/nextjs.mjs.map +1 -1
- package/dist/adapters/sdk.d.mts +2 -2
- package/dist/adapters/sdk.d.ts +2 -2
- package/dist/adapters/sdk.js +24 -6
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +24 -6
- package/dist/adapters/sdk.mjs.map +1 -1
- package/dist/agent/index.d.mts +2 -2
- package/dist/agent/index.d.ts +2 -2
- package/dist/agent/index.js +24 -3
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/index.mjs +24 -3
- package/dist/agent/index.mjs.map +1 -1
- package/dist/bin/astrasync-claude-hook.js +22 -4
- package/dist/bin/astrasync-codex-hook.js +22 -4
- package/dist/bin/astrasync-guard.js +26 -8
- package/dist/bin/astrasync.js +76 -23
- package/dist/browser/background.js +22 -4
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +22 -4
- package/dist/browser/background.mjs.map +1 -1
- package/dist/browser/browser-adapter.d.mts +2 -2
- package/dist/browser/browser-adapter.d.ts +2 -2
- package/dist/claude-code/claude-code-adapter.d.mts +2 -2
- package/dist/claude-code/claude-code-adapter.d.ts +2 -2
- package/dist/cli/index.d.mts +2 -2
- package/dist/cli/index.d.ts +2 -2
- package/dist/cli/index.js +23 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +23 -2
- package/dist/cli/index.mjs.map +1 -1
- package/dist/codex/index.d.mts +2 -2
- package/dist/codex/index.d.ts +2 -2
- package/dist/codex/index.js +22 -4
- package/dist/codex/index.js.map +1 -1
- package/dist/codex/index.mjs +22 -4
- package/dist/codex/index.mjs.map +1 -1
- package/dist/cursor/cursor-adapter.d.mts +2 -2
- package/dist/cursor/cursor-adapter.d.ts +2 -2
- package/dist/cursor/extension.d.mts +2 -2
- package/dist/cursor/extension.d.ts +2 -2
- package/dist/cursor/extension.js +22 -4
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +22 -4
- package/dist/cursor/extension.mjs.map +1 -1
- package/dist/edge-config.d.mts +1 -1
- package/dist/edge-config.d.ts +1 -1
- package/dist/edge-config.js +24 -1
- package/dist/edge-config.js.map +1 -1
- package/dist/edge-config.mjs +22 -1
- package/dist/edge-config.mjs.map +1 -1
- package/dist/edge-core/index.d.mts +1 -1
- package/dist/edge-core/index.d.ts +1 -1
- package/dist/edge-core/index.js +121 -16
- package/dist/edge-core/index.js.map +1 -1
- package/dist/edge-core/index.mjs +121 -16
- package/dist/edge-core/index.mjs.map +1 -1
- package/dist/{express-D_4hTn5Z.d.mts → express-CR6F3njD.d.mts} +1 -1
- package/dist/{express-BVd1_3FE.d.ts → express-DaqL11tx.d.ts} +1 -1
- package/dist/gateway/gateway.d.mts +2 -2
- package/dist/gateway/gateway.d.ts +2 -2
- package/dist/gateway/gateway.js +22 -4
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +22 -4
- package/dist/gateway/gateway.mjs.map +1 -1
- package/dist/git-trigger/git-hooks.d.mts +2 -2
- package/dist/git-trigger/git-hooks.d.ts +2 -2
- package/dist/{index-BqHKlLOU.d.ts → index-B5Ts1WtM.d.ts} +1 -1
- package/dist/{index-T1aBoUcc.d.ts → index-BfIxCYIZ.d.ts} +1 -1
- package/dist/{index-BTeQ9fgV.d.mts → index-Cub7lK-T.d.mts} +1 -1
- package/dist/{index-DQb5-_1X.d.mts → index-CwAh7Tln.d.mts} +1 -1
- package/dist/index.d.mts +58 -9
- package/dist/index.d.ts +58 -9
- package/dist/index.js +218 -54
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +214 -54
- package/dist/index.mjs.map +1 -1
- package/dist/local-evaluator/evaluator.d.mts +2 -2
- package/dist/local-evaluator/evaluator.d.ts +2 -2
- package/dist/metadata-capture.d.mts +35 -5
- package/dist/metadata-capture.d.ts +35 -5
- package/dist/metadata-capture.js +44 -6
- package/dist/metadata-capture.js.map +1 -1
- package/dist/metadata-capture.mjs +43 -6
- package/dist/metadata-capture.mjs.map +1 -1
- package/dist/{nextjs-w7RTbP1P.d.ts → nextjs-DL6uE1ye.d.ts} +1 -1
- package/dist/{nextjs-WyeVr2Kp.d.mts → nextjs-DlX6xwbU.d.mts} +1 -1
- package/dist/platform-signatures.d.mts +18 -1
- package/dist/platform-signatures.d.ts +18 -1
- package/dist/platform-signatures.js +35 -2
- package/dist/platform-signatures.js.map +1 -1
- package/dist/platform-signatures.mjs +35 -2
- package/dist/platform-signatures.mjs.map +1 -1
- package/dist/registration/index.js +25 -4
- package/dist/registration/index.js.map +1 -1
- package/dist/registration/index.mjs +25 -4
- package/dist/registration/index.mjs.map +1 -1
- package/dist/{sdk-CbTNIkAa.d.mts → sdk-Bbh9B_dw.d.mts} +1 -1
- package/dist/{sdk-QeX0Z8Ho.d.ts → sdk-C6SASmsh.d.ts} +1 -1
- package/dist/transport/index.d.mts +2 -2
- package/dist/transport/index.d.ts +2 -2
- package/dist/transport/index.js +22 -1
- package/dist/transport/index.js.map +1 -1
- package/dist/transport/index.mjs +22 -1
- package/dist/transport/index.mjs.map +1 -1
- package/dist/{types-r850cOt0.d.mts → types-C0zfSXMB.d.mts} +22 -0
- package/dist/{types-DqfPU5Bl.d.mts → types-C7Rb-w0d.d.mts} +1 -1
- package/dist/{types-sMxBT-nM.d.ts → types-DwSfO7Sb.d.ts} +22 -0
- package/dist/{types-BLUx92FJ.d.ts → types-nIh3HtuA.d.ts} +1 -1
- package/dist/ui/index.d.mts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/dist/verify.d.mts +1 -1
- package/dist/verify.d.ts +1 -1
- package/dist/verify.js +29 -11
- package/dist/verify.js.map +1 -1
- package/dist/verify.mjs +29 -11
- package/dist/verify.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
// src/version.ts
|
|
2
|
+
var SDK_VERSION = "4.4.0";
|
|
3
|
+
|
|
4
|
+
// src/http.ts
|
|
5
|
+
var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
|
|
6
|
+
function withUserAgent(initHeaders) {
|
|
7
|
+
if (!initHeaders) return { "user-agent": SDK_USER_AGENT };
|
|
8
|
+
if (initHeaders instanceof Headers) {
|
|
9
|
+
const copy = new Headers(initHeaders);
|
|
10
|
+
if (!copy.has("user-agent")) copy.set("user-agent", SDK_USER_AGENT);
|
|
11
|
+
return copy;
|
|
12
|
+
}
|
|
13
|
+
if (Array.isArray(initHeaders)) {
|
|
14
|
+
const has2 = initHeaders.some(([name]) => name.toLowerCase() === "user-agent");
|
|
15
|
+
return has2 ? initHeaders : [...initHeaders, ["user-agent", SDK_USER_AGENT]];
|
|
16
|
+
}
|
|
17
|
+
const has = Object.keys(initHeaders).some((name) => name.toLowerCase() === "user-agent");
|
|
18
|
+
return has ? initHeaders : { "user-agent": SDK_USER_AGENT, ...initHeaders };
|
|
19
|
+
}
|
|
20
|
+
var sdkFetch = (input, init) => fetch(input, { ...init, headers: withUserAgent(init?.headers) });
|
|
21
|
+
|
|
1
22
|
// src/adapters/stepup-poll.ts
|
|
2
23
|
var DEFAULT_POLL_INTERVAL_MS = 3e3;
|
|
3
24
|
var MIN_POLL_INTERVAL_MS = 250;
|
|
@@ -12,7 +33,7 @@ function resolvePollUrl(config, pollUrl) {
|
|
|
12
33
|
async function pollStepUpStatus(config, approval) {
|
|
13
34
|
if (!approval.pollUrl) return null;
|
|
14
35
|
try {
|
|
15
|
-
const res = await
|
|
36
|
+
const res = await sdkFetch(resolvePollUrl(config, approval.pollUrl), {
|
|
16
37
|
method: "GET",
|
|
17
38
|
signal: AbortSignal.timeout(POLL_REQUEST_TIMEOUT_MS)
|
|
18
39
|
});
|
|
@@ -64,9 +85,6 @@ function getTrustLevel(score) {
|
|
|
64
85
|
return "BRONZE";
|
|
65
86
|
}
|
|
66
87
|
|
|
67
|
-
// src/version.ts
|
|
68
|
-
var SDK_VERSION = "4.1.0";
|
|
69
|
-
|
|
70
88
|
// src/well-known.ts
|
|
71
89
|
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
72
90
|
var cache = /* @__PURE__ */ new Map();
|
|
@@ -77,7 +95,7 @@ function wellKnownUrl(apiBaseUrl) {
|
|
|
77
95
|
}
|
|
78
96
|
async function fetchWellKnown(apiBaseUrl) {
|
|
79
97
|
const url = wellKnownUrl(apiBaseUrl);
|
|
80
|
-
const response = await
|
|
98
|
+
const response = await sdkFetch(url, {
|
|
81
99
|
method: "GET",
|
|
82
100
|
headers: { Accept: "application/json" },
|
|
83
101
|
signal: AbortSignal.timeout(5e3)
|
|
@@ -147,7 +165,7 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
|
|
|
147
165
|
initCheckPerformed = true;
|
|
148
166
|
try {
|
|
149
167
|
const probeUrl = `${apiBaseUrl}/agents/verify-access`;
|
|
150
|
-
const response = await
|
|
168
|
+
const response = await sdkFetch(probeUrl, { method: "HEAD" });
|
|
151
169
|
const contentType = response.headers.get("content-type") ?? "";
|
|
152
170
|
if (contentType.startsWith("text/html")) {
|
|
153
171
|
const message = `[VerificationGateway] apiBaseUrl '${apiBaseUrl}' returned HTML (content-type: ${contentType}). This usually means apiBaseUrl is pointing at a marketing site instead of the API. Expected: 'https://astrasync.ai/api' (prod) or 'https://staging.astrasync.ai/api' (staging).`;
|
|
@@ -161,7 +179,7 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
|
|
|
161
179
|
);
|
|
162
180
|
}
|
|
163
181
|
if (apiKey) {
|
|
164
|
-
const selfCheck = await
|
|
182
|
+
const selfCheck = await sdkFetch(`${apiBaseUrl}/agents/verify-access/self-check`, {
|
|
165
183
|
headers: { Authorization: `Bearer ${apiKey}` }
|
|
166
184
|
});
|
|
167
185
|
if (selfCheck.status === 401) {
|
|
@@ -408,7 +426,7 @@ async function callVerifyAccessAPI(config, request) {
|
|
|
408
426
|
headers["X-API-Key"] = config.apiKey;
|
|
409
427
|
}
|
|
410
428
|
try {
|
|
411
|
-
const response = await
|
|
429
|
+
const response = await sdkFetch(`${config.apiBaseUrl}/agents/verify-access`, {
|
|
412
430
|
method: "POST",
|
|
413
431
|
headers,
|
|
414
432
|
body: JSON.stringify(body),
|
|
@@ -651,7 +669,7 @@ async function recordDecision(config, sessionId, decision, reason) {
|
|
|
651
669
|
headers["Authorization"] = `Bearer ${config.apiKey}`;
|
|
652
670
|
headers["X-API-Key"] = config.apiKey;
|
|
653
671
|
}
|
|
654
|
-
await
|
|
672
|
+
await sdkFetch(`${config.apiBaseUrl}/agents/verify-access/${sessionId}/decision`, {
|
|
655
673
|
method: "POST",
|
|
656
674
|
headers,
|
|
657
675
|
body: JSON.stringify({ decision, reason })
|
|
@@ -666,7 +684,7 @@ async function fetchRoutes(config, counterpartyId) {
|
|
|
666
684
|
headers["X-API-Key"] = config.apiKey;
|
|
667
685
|
}
|
|
668
686
|
try {
|
|
669
|
-
const response = await
|
|
687
|
+
const response = await sdkFetch(
|
|
670
688
|
`${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/routes`,
|
|
671
689
|
{ method: "GET", headers }
|
|
672
690
|
);
|
|
@@ -685,7 +703,7 @@ async function reportUnregisteredAttempt(config, data) {
|
|
|
685
703
|
...counterpartyUrl && { counterpartyUrl },
|
|
686
704
|
...config.counterpartyId && { counterpartyId: config.counterpartyId }
|
|
687
705
|
};
|
|
688
|
-
await
|
|
706
|
+
await sdkFetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
|
|
689
707
|
method: "POST",
|
|
690
708
|
headers: { "Content-Type": "application/json" },
|
|
691
709
|
body: JSON.stringify(body)
|
|
@@ -694,7 +712,7 @@ async function reportUnregisteredAttempt(config, data) {
|
|
|
694
712
|
}
|
|
695
713
|
async function reportCounterpartyPreCheckFailure(config, data) {
|
|
696
714
|
const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl;
|
|
697
|
-
await
|
|
715
|
+
await sdkFetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {
|
|
698
716
|
method: "POST",
|
|
699
717
|
headers: { "Content-Type": "application/json" },
|
|
700
718
|
body: JSON.stringify(data)
|
|
@@ -748,6 +766,31 @@ function extractHttpCredentials(headers) {
|
|
|
748
766
|
return credentials;
|
|
749
767
|
}
|
|
750
768
|
|
|
769
|
+
// src/verified-hop.ts
|
|
770
|
+
var MCP_VERIFIED_HOP_HEADER = "X-Astra-Verified-Hop";
|
|
771
|
+
var MCP_VERIFIED_HOP_MAX_AGE_MS = 6e4;
|
|
772
|
+
function parseVerifiedHop(value) {
|
|
773
|
+
if (!value) return null;
|
|
774
|
+
const parts = value.split(";");
|
|
775
|
+
if (parts.length !== 3) return null;
|
|
776
|
+
const [astraId, sessionId, checkedAtRaw] = parts;
|
|
777
|
+
if (!astraId) return null;
|
|
778
|
+
const checkedAt = Number(checkedAtRaw);
|
|
779
|
+
if (!Number.isFinite(checkedAt) || checkedAt <= 0) return null;
|
|
780
|
+
return {
|
|
781
|
+
astraId,
|
|
782
|
+
...sessionId ? { sessionId } : {},
|
|
783
|
+
checkedAt
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
function isVerifiedHopValidFor(marker, expectedAstraId, opts = {}) {
|
|
787
|
+
if (!marker) return false;
|
|
788
|
+
if (marker.astraId !== expectedAstraId) return false;
|
|
789
|
+
const maxAge = opts.maxAgeMs ?? MCP_VERIFIED_HOP_MAX_AGE_MS;
|
|
790
|
+
const now = opts.now ?? Date.now();
|
|
791
|
+
return now - marker.checkedAt <= maxAge && now >= marker.checkedAt;
|
|
792
|
+
}
|
|
793
|
+
|
|
751
794
|
// src/adapters/http-pdlss.ts
|
|
752
795
|
var HTTP_METHOD_ACTION_TABLE = {
|
|
753
796
|
GET: "data.read",
|
|
@@ -898,7 +941,9 @@ var SECRET_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
|
898
941
|
"x-astrasync-signature",
|
|
899
942
|
"x-astrasync-secret",
|
|
900
943
|
"x-hub-signature",
|
|
901
|
-
"x-hub-signature-256"
|
|
944
|
+
"x-hub-signature-256",
|
|
945
|
+
// Short-lived verify dedupe token — must never persist in stored metadata.
|
|
946
|
+
"x-astra-verified-hop"
|
|
902
947
|
]);
|
|
903
948
|
var SECRET_HEADER_PREFIXES = ["x-astrasync-", "kya-", "kya_"];
|
|
904
949
|
var CREDENTIAL_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
@@ -923,7 +968,13 @@ var PLATFORM_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
|
923
968
|
"x-agent-card-url",
|
|
924
969
|
"x-agent-provider"
|
|
925
970
|
]);
|
|
926
|
-
var PLATFORM_HEADER_PREFIXES = [
|
|
971
|
+
var PLATFORM_HEADER_PREFIXES = [
|
|
972
|
+
"x-goog-",
|
|
973
|
+
"x-stainless-",
|
|
974
|
+
"x-anthropic-",
|
|
975
|
+
"x-openai-",
|
|
976
|
+
"sec-ch-"
|
|
977
|
+
];
|
|
927
978
|
var KEY_FORMAT_PREFIXES = [
|
|
928
979
|
"sk-ant-api03",
|
|
929
980
|
"sk-ant-api",
|
|
@@ -995,6 +1046,7 @@ function sanitizeHeaders(raw) {
|
|
|
995
1046
|
const headers = {};
|
|
996
1047
|
const platformHeaders = {};
|
|
997
1048
|
let apiKeyFormat;
|
|
1049
|
+
let cookieCount;
|
|
998
1050
|
let totalBytes = 0;
|
|
999
1051
|
let count = 0;
|
|
1000
1052
|
if (!raw) return { headers };
|
|
@@ -1009,6 +1061,9 @@ function sanitizeHeaders(raw) {
|
|
|
1009
1061
|
if (fmt && !apiKeyFormat) apiKeyFormat = fmt;
|
|
1010
1062
|
continue;
|
|
1011
1063
|
}
|
|
1064
|
+
if (name === "cookie") {
|
|
1065
|
+
cookieCount = joined.split(";").filter((s) => s.trim().length > 0).length;
|
|
1066
|
+
}
|
|
1012
1067
|
if (isSecretHeader(name)) continue;
|
|
1013
1068
|
let value = joined;
|
|
1014
1069
|
if (byteLength(value) > MAX_HEADER_VALUE_BYTES) {
|
|
@@ -1024,6 +1079,7 @@ function sanitizeHeaders(raw) {
|
|
|
1024
1079
|
const result = { headers };
|
|
1025
1080
|
if (apiKeyFormat) result.apiKeyFormat = apiKeyFormat;
|
|
1026
1081
|
if (Object.keys(platformHeaders).length > 0) result.platformHeaders = platformHeaders;
|
|
1082
|
+
if (cookieCount !== void 0) result.cookieCount = cookieCount;
|
|
1027
1083
|
return result;
|
|
1028
1084
|
}
|
|
1029
1085
|
var CONNECTION_HEADER_SOURCES = [
|
|
@@ -1033,16 +1089,28 @@ var CONNECTION_HEADER_SOURCES = [
|
|
|
1033
1089
|
["cf-ipcountry", "x-vercel-ip-country", "cloudfront-viewer-country", "fastly-geo-country"]
|
|
1034
1090
|
],
|
|
1035
1091
|
["region", ["x-vercel-ip-country-region", "cloudfront-viewer-country-region"]],
|
|
1092
|
+
["countryRegionName", ["cloudfront-viewer-country-region-name"]],
|
|
1036
1093
|
["city", ["x-vercel-ip-city", "cloudfront-viewer-city"]],
|
|
1094
|
+
["latitude", ["x-vercel-ip-latitude", "cloudfront-viewer-latitude"]],
|
|
1095
|
+
["longitude", ["x-vercel-ip-longitude", "cloudfront-viewer-longitude"]],
|
|
1096
|
+
["postalCode", ["x-vercel-ip-postal-code", "cloudfront-viewer-postal-code"]],
|
|
1037
1097
|
["asn", ["cloudfront-viewer-asn"]],
|
|
1038
1098
|
["tlsVersion", ["cloudfront-viewer-tls"]],
|
|
1039
1099
|
["httpVersion", ["cloudfront-viewer-http-version"]],
|
|
1100
|
+
["timeZone", ["x-vercel-ip-timezone", "cloudfront-viewer-time-zone"]],
|
|
1101
|
+
["metroCode", ["cloudfront-viewer-metro-code"]],
|
|
1040
1102
|
// TLS fingerprints can only be computed where TLS terminates. When a CDN
|
|
1041
1103
|
// terminates TLS in front of the merchant, its fingerprint headers ARE the
|
|
1042
1104
|
// connection-layer truth — normalising them here is the permanent design.
|
|
1043
|
-
|
|
1044
|
-
|
|
1105
|
+
// CloudFront delivers JA3/JA4 only via an origin request policy and only
|
|
1106
|
+
// for HTTPS viewer connections.
|
|
1107
|
+
["tlsFingerprint", ["cf-ja3-hash", "cloudfront-viewer-ja3-fingerprint"]],
|
|
1108
|
+
["ja4", ["cf-ja4", "cloudfront-viewer-ja4-fingerprint"]],
|
|
1109
|
+
// Header-structure fingerprint: browser/SDK-distinctive header ordering.
|
|
1110
|
+
["headerOrder", ["cloudfront-viewer-header-order"]],
|
|
1111
|
+
["headerCount", ["cloudfront-viewer-header-count"]]
|
|
1045
1112
|
];
|
|
1113
|
+
var MAX_CONNECTION_VALUE_CHARS = 256;
|
|
1046
1114
|
function deriveConnectionFromHeaders(headers) {
|
|
1047
1115
|
if (!headers) return void 0;
|
|
1048
1116
|
const lower = {};
|
|
@@ -1055,20 +1123,30 @@ function deriveConnectionFromHeaders(headers) {
|
|
|
1055
1123
|
for (const source of sources) {
|
|
1056
1124
|
const value = lower[source]?.trim();
|
|
1057
1125
|
if (value) {
|
|
1058
|
-
connection[key] = value;
|
|
1126
|
+
connection[key] = value.slice(0, MAX_CONNECTION_VALUE_CHARS);
|
|
1059
1127
|
break;
|
|
1060
1128
|
}
|
|
1061
1129
|
}
|
|
1062
1130
|
}
|
|
1131
|
+
{
|
|
1132
|
+
const addr = lower["cloudfront-viewer-address"]?.trim();
|
|
1133
|
+
const portMatch = addr?.match(/:(\d+)$/);
|
|
1134
|
+
if (portMatch) connection.sourcePort = portMatch[1];
|
|
1135
|
+
if (!connection.ip && addr) connection.ip = addr.replace(/:\d+$/, "");
|
|
1136
|
+
}
|
|
1063
1137
|
if (!connection.ip) {
|
|
1064
1138
|
const xff = lower["x-forwarded-for"];
|
|
1065
1139
|
const client = xff?.split(",")[0]?.trim();
|
|
1066
1140
|
if (client) connection.ip = client;
|
|
1067
1141
|
}
|
|
1142
|
+
if (!connection.tlsCipher) {
|
|
1143
|
+
const cipher = connection.tlsVersion?.split(":")[1];
|
|
1144
|
+
if (cipher) connection.tlsCipher = cipher;
|
|
1145
|
+
}
|
|
1068
1146
|
return Object.keys(connection).length > 0 ? connection : void 0;
|
|
1069
1147
|
}
|
|
1070
1148
|
function buildSdkObservedMetadata(rawHeaders) {
|
|
1071
|
-
const { headers, apiKeyFormat, platformHeaders } = sanitizeHeaders(rawHeaders);
|
|
1149
|
+
const { headers, apiKeyFormat, platformHeaders, cookieCount } = sanitizeHeaders(rawHeaders);
|
|
1072
1150
|
const flat = {};
|
|
1073
1151
|
if (rawHeaders) {
|
|
1074
1152
|
for (const name of Object.keys(rawHeaders)) {
|
|
@@ -1083,6 +1161,7 @@ function buildSdkObservedMetadata(rawHeaders) {
|
|
|
1083
1161
|
...apiKeyFormat && { apiKeyFormat },
|
|
1084
1162
|
...platformHeaders && { platformHeaders },
|
|
1085
1163
|
...connection && { connection },
|
|
1164
|
+
...cookieCount !== void 0 && { cookieCount },
|
|
1086
1165
|
schemaVersion: CAPTURE_SCHEMA_VERSION
|
|
1087
1166
|
};
|
|
1088
1167
|
}
|
|
@@ -1127,7 +1206,7 @@ async function fetchEdgeConfig(config, counterpartyId, opts) {
|
|
|
1127
1206
|
}
|
|
1128
1207
|
if (opts?.etag) headers["If-None-Match"] = opts.etag;
|
|
1129
1208
|
try {
|
|
1130
|
-
const response = await
|
|
1209
|
+
const response = await sdkFetch(
|
|
1131
1210
|
`${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/edge-config`,
|
|
1132
1211
|
{ method: "GET", headers, signal: AbortSignal.timeout(opts?.timeoutMs ?? 5e3) }
|
|
1133
1212
|
);
|
|
@@ -1239,12 +1318,43 @@ var PLATFORM_AGENT_SIGNATURES = [
|
|
|
1239
1318
|
uaPatterns: [/perplexity/i],
|
|
1240
1319
|
agentCardPatterns: [/perplexity\.ai/i],
|
|
1241
1320
|
apiKeyFormatPatterns: [/^pplx-/i]
|
|
1321
|
+
},
|
|
1322
|
+
// Agentic browsers — versioned-token patterns ONLY (a stock Chrome UA must
|
|
1323
|
+
// never match). Distinct vendor keys (not 'chatgpt'/'perplexity'): the
|
|
1324
|
+
// browser product is a different surface than the assistant service, and
|
|
1325
|
+
// the DB registry mirrors one row per vendor. These sit after the primary
|
|
1326
|
+
// vendor entries so a UA carrying an explicit vendor token keeps its
|
|
1327
|
+
// primary attribution; a browser-shaped UA with the agentic token is
|
|
1328
|
+
// caught here BEFORE classify's human tier, which is what makes these
|
|
1329
|
+
// visits observable at all. Token drift is real (some builds mimic Chrome
|
|
1330
|
+
// outright) — re-verify against captured events when a new browser ships,
|
|
1331
|
+
// and check `sec-ch-ua` platformHeaders for the real brand.
|
|
1332
|
+
{
|
|
1333
|
+
vendor: "chatgpt-atlas",
|
|
1334
|
+
displayName: "ChatGPT Atlas browser",
|
|
1335
|
+
uaPatterns: [/\batlas\/\d/i],
|
|
1336
|
+
headerPatterns: [/^sec-ch-ua: .*atlas/i]
|
|
1337
|
+
},
|
|
1338
|
+
{
|
|
1339
|
+
vendor: "perplexity-comet",
|
|
1340
|
+
displayName: "Perplexity Comet browser",
|
|
1341
|
+
uaPatterns: [/\bcomet\/\d/i],
|
|
1342
|
+
headerPatterns: [/^sec-ch-ua: .*comet/i]
|
|
1343
|
+
},
|
|
1344
|
+
// Our own SDK self-identifies (sdkFetch stamps this UA) — first-party
|
|
1345
|
+
// traffic must never pollute the unknown/anonymous pool it exists to audit.
|
|
1346
|
+
{
|
|
1347
|
+
vendor: "astrasync-sdk",
|
|
1348
|
+
displayName: "AstraSync SDK",
|
|
1349
|
+
uaPatterns: [/^astrasync-sdk\//i]
|
|
1242
1350
|
}
|
|
1243
1351
|
];
|
|
1244
1352
|
function matchPlatformSignature(signatures, input) {
|
|
1245
1353
|
const ua = input.userAgent ?? "";
|
|
1246
1354
|
const cardUrl = input.agentCardUrl ?? "";
|
|
1247
1355
|
const apiKeyFormat = input.apiKeyFormat ?? "";
|
|
1356
|
+
const ja4 = input.ja4 ?? "";
|
|
1357
|
+
const asn = input.asn ?? "";
|
|
1248
1358
|
const headerLines = [];
|
|
1249
1359
|
if (input.platformHeaders) {
|
|
1250
1360
|
for (const [name, value] of Object.entries(input.platformHeaders)) {
|
|
@@ -1256,7 +1366,9 @@ function matchPlatformSignature(signatures, input) {
|
|
|
1256
1366
|
const cardMatch = sig.agentCardPatterns?.some((p) => p.test(cardUrl));
|
|
1257
1367
|
const keyMatch = apiKeyFormat.length > 0 && sig.apiKeyFormatPatterns?.some((p) => p.test(apiKeyFormat));
|
|
1258
1368
|
const headerMatch = headerLines.length > 0 && sig.headerPatterns?.some((p) => headerLines.some((line) => p.test(line)));
|
|
1259
|
-
|
|
1369
|
+
const ja4Match = ja4.length > 0 && sig.ja4Patterns?.some((p) => p.test(ja4));
|
|
1370
|
+
const asnMatch = asn.length > 0 && sig.asnList?.includes(asn);
|
|
1371
|
+
if (uaMatch || cardMatch || keyMatch || headerMatch || ja4Match || asnMatch) {
|
|
1260
1372
|
const uaBase = ua.split(/[\s/]/)[0]?.toLowerCase() ?? "";
|
|
1261
1373
|
const cardHost = (() => {
|
|
1262
1374
|
try {
|
|
@@ -1265,7 +1377,7 @@ function matchPlatformSignature(signatures, input) {
|
|
|
1265
1377
|
return "";
|
|
1266
1378
|
}
|
|
1267
1379
|
})();
|
|
1268
|
-
const keyBasis = uaBase || cardHost || apiKeyFormat || "unknown";
|
|
1380
|
+
const keyBasis = uaBase || cardHost || apiKeyFormat || ja4 || asn && `asn${asn}` || "unknown";
|
|
1269
1381
|
return {
|
|
1270
1382
|
vendor: sig.vendor,
|
|
1271
1383
|
fingerprintKey: `${sig.vendor}:${keyBasis}`,
|
|
@@ -1500,6 +1612,8 @@ function createMiddleware(options) {
|
|
|
1500
1612
|
failOnError = "open",
|
|
1501
1613
|
caseInsensitiveRouteMatch = false,
|
|
1502
1614
|
anonymousBeacon = true,
|
|
1615
|
+
trustVerifiedHop = false,
|
|
1616
|
+
verifiedHopMaxAgeMs,
|
|
1503
1617
|
...config
|
|
1504
1618
|
} = options;
|
|
1505
1619
|
if (config.apiBaseUrl) {
|
|
@@ -1592,6 +1706,16 @@ function createMiddleware(options) {
|
|
|
1592
1706
|
beaconPassThrough?.(req);
|
|
1593
1707
|
return next();
|
|
1594
1708
|
}
|
|
1709
|
+
if (trustVerifiedHop && credentials.astraId) {
|
|
1710
|
+
const hopRaw = req.headers[MCP_VERIFIED_HOP_HEADER.toLowerCase()];
|
|
1711
|
+
const hopValue = typeof hopRaw === "string" ? hopRaw : Array.isArray(hopRaw) ? hopRaw[0] : void 0;
|
|
1712
|
+
const marker = parseVerifiedHop(hopValue);
|
|
1713
|
+
if (isVerifiedHopValidFor(marker, credentials.astraId, {
|
|
1714
|
+
...verifiedHopMaxAgeMs !== void 0 ? { maxAgeMs: verifiedHopMaxAgeMs } : {}
|
|
1715
|
+
})) {
|
|
1716
|
+
return next();
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1595
1719
|
const pdlssPair = resolveRequestPdlss(
|
|
1596
1720
|
req,
|
|
1597
1721
|
routeConfig,
|