@astrasyncai/verification-gateway 4.0.0 → 4.2.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 +106 -29
- 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 +398 -15
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +398 -15
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/http-pdlss.d.mts +3 -3
- package/dist/adapters/http-pdlss.d.ts +3 -3
- package/dist/adapters/http-pdlss.js.map +1 -1
- package/dist/adapters/http-pdlss.mjs.map +1 -1
- package/dist/adapters/mcp.d.mts +56 -36
- package/dist/adapters/mcp.d.ts +56 -36
- package/dist/adapters/mcp.js +220 -3
- package/dist/adapters/mcp.js.map +1 -1
- package/dist/adapters/mcp.mjs +220 -3
- 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 +433 -18
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +433 -18
- 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 +2 -2
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +2 -2
- 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.map +1 -1
- package/dist/agent/index.mjs.map +1 -1
- package/dist/bin/astrasync-claude-hook.js +13 -13
- package/dist/bin/astrasync-codex-hook.js +13 -13
- package/dist/bin/astrasync-guard.js +100 -13
- package/dist/bin/astrasync.js +168 -27
- package/dist/browser/background.js +13 -13
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +13 -13
- 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/codex/index.d.mts +3 -3
- package/dist/codex/index.d.ts +3 -3
- package/dist/codex/index.js +24 -13
- package/dist/codex/index.js.map +1 -1
- package/dist/codex/index.mjs +24 -13
- 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 +13 -13
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +13 -13
- package/dist/cursor/extension.mjs.map +1 -1
- package/dist/edge-config.d.mts +22 -7
- package/dist/edge-config.d.ts +22 -7
- package/dist/edge-config.js.map +1 -1
- package/dist/edge-config.mjs.map +1 -1
- package/dist/edge-core/index.d.mts +425 -0
- package/dist/edge-core/index.d.ts +425 -0
- package/dist/edge-core/index.js +1482 -0
- package/dist/edge-core/index.js.map +1 -0
- package/dist/edge-core/index.mjs +1437 -0
- package/dist/edge-core/index.mjs.map +1 -0
- package/dist/{express-BZajRs4i.d.mts → express-BVd1_3FE.d.ts} +9 -7
- package/dist/{express-CjCUB1k7.d.ts → express-D_4hTn5Z.d.mts} +9 -7
- package/dist/gateway/gateway.d.mts +2 -2
- package/dist/gateway/gateway.d.ts +2 -2
- package/dist/gateway/gateway.js +13 -13
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +13 -13
- 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-adgKhujR.d.ts → index-B0YHu_SP.d.ts} +30 -27
- package/dist/{index-B0cyMZDL.d.mts → index-BPEBlOsE.d.mts} +30 -27
- package/dist/{index-BRYteOqO.d.mts → index-DQb5-_1X.d.mts} +1 -1
- package/dist/{index-BZQdVqNw.d.ts → index-T1aBoUcc.d.ts} +1 -1
- package/dist/index.d.mts +11 -11
- package/dist/index.d.ts +11 -11
- package/dist/index.js +477 -265
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +474 -265
- 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 +63 -4
- package/dist/metadata-capture.d.ts +63 -4
- package/dist/metadata-capture.js +67 -0
- package/dist/metadata-capture.js.map +1 -1
- package/dist/metadata-capture.mjs +64 -0
- package/dist/metadata-capture.mjs.map +1 -1
- package/dist/{nextjs-DeUTnOBd.d.mts → nextjs-WyeVr2Kp.d.mts} +3 -3
- package/dist/{nextjs-D_iFf5k-.d.ts → nextjs-w7RTbP1P.d.ts} +3 -3
- package/dist/platform-signatures.d.mts +5 -6
- package/dist/platform-signatures.d.ts +5 -6
- package/dist/platform-signatures.js.map +1 -1
- package/dist/platform-signatures.mjs.map +1 -1
- package/dist/registration/index.d.mts +10 -11
- package/dist/registration/index.d.ts +10 -11
- package/dist/registration/index.js.map +1 -1
- package/dist/registration/index.mjs.map +1 -1
- package/dist/{sdk-BM3mwEAq.d.ts → sdk-CbTNIkAa.d.mts} +4 -4
- package/dist/{sdk-_il1Q41f.d.mts → sdk-QeX0Z8Ho.d.ts} +4 -4
- package/dist/transport/index.d.mts +2 -2
- package/dist/transport/index.d.ts +2 -2
- package/dist/transport/index.js.map +1 -1
- package/dist/transport/index.mjs.map +1 -1
- package/dist/{types-CHKCgDFC.d.ts → types-BLUx92FJ.d.ts} +1 -1
- package/dist/{types-DfnP7JiM.d.mts → types-DqfPU5Bl.d.mts} +1 -1
- package/dist/{types-BcDf9Kk0.d.mts → types-r850cOt0.d.mts} +106 -88
- package/dist/{types-fTQD448H.d.ts → types-sMxBT-nM.d.ts} +106 -88
- package/dist/ui/index.d.mts +11 -7
- package/dist/ui/index.d.ts +11 -7
- package/dist/ui/index.js +13 -9
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/index.mjs +10 -8
- package/dist/ui/index.mjs.map +1 -1
- package/dist/verify.d.mts +8 -7
- package/dist/verify.d.ts +8 -7
- package/dist/verify.js +1 -1
- package/dist/verify.js.map +1 -1
- package/dist/verify.mjs +1 -1
- package/dist/verify.mjs.map +1 -1
- package/package.json +8 -2
package/dist/adapters/nextjs.mjs
CHANGED
|
@@ -65,7 +65,7 @@ function getTrustLevel(score) {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
// src/version.ts
|
|
68
|
-
var SDK_VERSION = "4.
|
|
68
|
+
var SDK_VERSION = "4.1.0";
|
|
69
69
|
|
|
70
70
|
// src/well-known.ts
|
|
71
71
|
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
@@ -204,6 +204,40 @@ function cacheResult(request, result, configuredTtl, counterpartyId) {
|
|
|
204
204
|
expiresAt: Date.now() + ttlSeconds * 1e3
|
|
205
205
|
});
|
|
206
206
|
}
|
|
207
|
+
function extractCredentials(headers, query) {
|
|
208
|
+
const credentials = {};
|
|
209
|
+
const ASTRA_ID_PATTERN = /^ASTRAE?-[A-Za-z0-9_-]{1,64}$/;
|
|
210
|
+
const astraIdHeader = headers["x-astra-id"] || headers["X-Astra-Id"] || headers["X-ASTRA-ID"] || headers["x-astra-agentid"] || headers["X-Astra-AgentId"] || headers["x-astra-agent-id"] || headers["X-Astra-Agent-Id"] || headers["X-ASTRA-AGENT-ID"];
|
|
211
|
+
if (astraIdHeader) {
|
|
212
|
+
const raw = Array.isArray(astraIdHeader) ? astraIdHeader[0] : typeof astraIdHeader === "string" ? astraIdHeader : void 0;
|
|
213
|
+
if (typeof raw === "string" && ASTRA_ID_PATTERN.test(raw)) {
|
|
214
|
+
credentials.astraId = raw;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const apiKeyHeader = headers["x-api-key"] || headers["X-Api-Key"] || headers["X-API-KEY"];
|
|
218
|
+
if (apiKeyHeader) {
|
|
219
|
+
credentials.apiKey = Array.isArray(apiKeyHeader) ? apiKeyHeader[0] : apiKeyHeader;
|
|
220
|
+
}
|
|
221
|
+
const authHeader = headers["authorization"] || headers["Authorization"];
|
|
222
|
+
if (authHeader) {
|
|
223
|
+
const authValue = Array.isArray(authHeader) ? authHeader[0] : authHeader;
|
|
224
|
+
if (typeof authValue === "string") {
|
|
225
|
+
credentials.authorizationHeader = authValue;
|
|
226
|
+
if (authValue.startsWith("Bearer ")) {
|
|
227
|
+
credentials.jwt = authValue.slice(7);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (query) {
|
|
232
|
+
if (query.astraId && !credentials.astraId) {
|
|
233
|
+
credentials.astraId = query.astraId;
|
|
234
|
+
}
|
|
235
|
+
if (query.apiKey && !credentials.apiKey) {
|
|
236
|
+
credentials.apiKey = query.apiKey;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return credentials;
|
|
240
|
+
}
|
|
207
241
|
function createGuidanceResponse(_config, reason, options = {}) {
|
|
208
242
|
const source = options.source ?? "no_credentials";
|
|
209
243
|
const isApiError = source === "api_error";
|
|
@@ -578,6 +612,21 @@ async function fetchRoutes(config, counterpartyId) {
|
|
|
578
612
|
return null;
|
|
579
613
|
}
|
|
580
614
|
}
|
|
615
|
+
async function reportUnregisteredAttempt(config, data) {
|
|
616
|
+
const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl;
|
|
617
|
+
const { counterpartyUrl, ...rest } = data;
|
|
618
|
+
const body = {
|
|
619
|
+
...rest,
|
|
620
|
+
...counterpartyUrl && { counterpartyUrl },
|
|
621
|
+
...config.counterpartyId && { counterpartyId: config.counterpartyId }
|
|
622
|
+
};
|
|
623
|
+
await fetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
|
|
624
|
+
method: "POST",
|
|
625
|
+
headers: { "Content-Type": "application/json" },
|
|
626
|
+
body: JSON.stringify(body)
|
|
627
|
+
}).catch(() => {
|
|
628
|
+
});
|
|
629
|
+
}
|
|
581
630
|
async function reportCounterpartyPreCheckFailure(config, data) {
|
|
582
631
|
const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl;
|
|
583
632
|
await fetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {
|
|
@@ -683,6 +732,7 @@ function performCounterpartyPreCheck(routeConfig, astraCreds, purpose) {
|
|
|
683
732
|
}
|
|
684
733
|
|
|
685
734
|
// src/metadata-capture.ts
|
|
735
|
+
var CAPTURE_SCHEMA_VERSION = 1;
|
|
686
736
|
var SECRET_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
687
737
|
"cookie",
|
|
688
738
|
"set-cookie",
|
|
@@ -819,6 +869,359 @@ function sanitizeHeaders(raw) {
|
|
|
819
869
|
if (Object.keys(platformHeaders).length > 0) result.platformHeaders = platformHeaders;
|
|
820
870
|
return result;
|
|
821
871
|
}
|
|
872
|
+
var CONNECTION_HEADER_SOURCES = [
|
|
873
|
+
["ip", ["cf-connecting-ip", "fastly-client-ip", "true-client-ip", "x-real-ip"]],
|
|
874
|
+
[
|
|
875
|
+
"country",
|
|
876
|
+
["cf-ipcountry", "x-vercel-ip-country", "cloudfront-viewer-country", "fastly-geo-country"]
|
|
877
|
+
],
|
|
878
|
+
["region", ["x-vercel-ip-country-region", "cloudfront-viewer-country-region"]],
|
|
879
|
+
["city", ["x-vercel-ip-city", "cloudfront-viewer-city"]],
|
|
880
|
+
["asn", ["cloudfront-viewer-asn"]],
|
|
881
|
+
["tlsVersion", ["cloudfront-viewer-tls"]],
|
|
882
|
+
["httpVersion", ["cloudfront-viewer-http-version"]],
|
|
883
|
+
// TLS fingerprints can only be computed where TLS terminates. When a CDN
|
|
884
|
+
// terminates TLS in front of the merchant, its fingerprint headers ARE the
|
|
885
|
+
// connection-layer truth — normalising them here is the permanent design.
|
|
886
|
+
["tlsFingerprint", ["cf-ja3-hash"]],
|
|
887
|
+
["ja4", ["cf-ja4"]]
|
|
888
|
+
];
|
|
889
|
+
function deriveConnectionFromHeaders(headers) {
|
|
890
|
+
if (!headers) return void 0;
|
|
891
|
+
const lower = {};
|
|
892
|
+
for (const name of Object.keys(headers)) {
|
|
893
|
+
const value = headers[name];
|
|
894
|
+
if (typeof value === "string" && value.length > 0) lower[name.toLowerCase()] = value;
|
|
895
|
+
}
|
|
896
|
+
const connection = {};
|
|
897
|
+
for (const [key, sources] of CONNECTION_HEADER_SOURCES) {
|
|
898
|
+
for (const source of sources) {
|
|
899
|
+
const value = lower[source]?.trim();
|
|
900
|
+
if (value) {
|
|
901
|
+
connection[key] = value;
|
|
902
|
+
break;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
if (!connection.ip) {
|
|
907
|
+
const xff = lower["x-forwarded-for"];
|
|
908
|
+
const client = xff?.split(",")[0]?.trim();
|
|
909
|
+
if (client) connection.ip = client;
|
|
910
|
+
}
|
|
911
|
+
return Object.keys(connection).length > 0 ? connection : void 0;
|
|
912
|
+
}
|
|
913
|
+
function buildSdkObservedMetadata(rawHeaders) {
|
|
914
|
+
const { headers, apiKeyFormat, platformHeaders } = sanitizeHeaders(rawHeaders);
|
|
915
|
+
const flat = {};
|
|
916
|
+
if (rawHeaders) {
|
|
917
|
+
for (const name of Object.keys(rawHeaders)) {
|
|
918
|
+
const value = rawHeaders[name];
|
|
919
|
+
if (value === void 0) continue;
|
|
920
|
+
flat[name.toLowerCase()] = Array.isArray(value) ? value.join(", ") : String(value);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
const connection = deriveConnectionFromHeaders(flat);
|
|
924
|
+
return {
|
|
925
|
+
headers,
|
|
926
|
+
...apiKeyFormat && { apiKeyFormat },
|
|
927
|
+
...platformHeaders && { platformHeaders },
|
|
928
|
+
...connection && { connection },
|
|
929
|
+
schemaVersion: CAPTURE_SCHEMA_VERSION
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
// src/edge-config.ts
|
|
934
|
+
var DEFAULT_EDGE_CONFIG = Object.freeze({
|
|
935
|
+
version: 1,
|
|
936
|
+
mode: "observe",
|
|
937
|
+
depth: "classify",
|
|
938
|
+
pathRules: [],
|
|
939
|
+
sampling: Object.freeze({ anonymousBeaconRate: 0.1 }),
|
|
940
|
+
failurePosture: "open"
|
|
941
|
+
});
|
|
942
|
+
var EDGE_MODES = ["observe", "enforce"];
|
|
943
|
+
var EDGE_DEPTHS = ["classify", "authenticate", "authorize"];
|
|
944
|
+
function isEdgeConfig(value) {
|
|
945
|
+
if (typeof value !== "object" || value === null) return false;
|
|
946
|
+
const v = value;
|
|
947
|
+
if (v.version !== 1) return false;
|
|
948
|
+
if (!EDGE_MODES.includes(v.mode)) return false;
|
|
949
|
+
if (!EDGE_DEPTHS.includes(v.depth)) return false;
|
|
950
|
+
if (!Array.isArray(v.pathRules)) return false;
|
|
951
|
+
for (const rule of v.pathRules) {
|
|
952
|
+
if (typeof rule !== "object" || rule === null) return false;
|
|
953
|
+
const r = rule;
|
|
954
|
+
if (typeof r.pattern !== "string" || r.pattern.length === 0) return false;
|
|
955
|
+
if (r.mode !== void 0 && !EDGE_MODES.includes(r.mode)) return false;
|
|
956
|
+
if (r.depth !== void 0 && !EDGE_DEPTHS.includes(r.depth)) return false;
|
|
957
|
+
}
|
|
958
|
+
const sampling = v.sampling;
|
|
959
|
+
if (typeof sampling !== "object" || sampling === null || typeof sampling.anonymousBeaconRate !== "number" || sampling.anonymousBeaconRate < 0 || sampling.anonymousBeaconRate > 1) {
|
|
960
|
+
return false;
|
|
961
|
+
}
|
|
962
|
+
return v.failurePosture === "open";
|
|
963
|
+
}
|
|
964
|
+
async function fetchEdgeConfig(config, counterpartyId, opts) {
|
|
965
|
+
if (!counterpartyId) return null;
|
|
966
|
+
const headers = { Accept: "application/json" };
|
|
967
|
+
if (config.apiKey) {
|
|
968
|
+
headers["Authorization"] = `Bearer ${config.apiKey}`;
|
|
969
|
+
headers["X-API-Key"] = config.apiKey;
|
|
970
|
+
}
|
|
971
|
+
if (opts?.etag) headers["If-None-Match"] = opts.etag;
|
|
972
|
+
try {
|
|
973
|
+
const response = await fetch(
|
|
974
|
+
`${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/edge-config`,
|
|
975
|
+
{ method: "GET", headers, signal: AbortSignal.timeout(opts?.timeoutMs ?? 5e3) }
|
|
976
|
+
);
|
|
977
|
+
if (response.status === 304) return { notModified: true };
|
|
978
|
+
if (!response.ok) return null;
|
|
979
|
+
const body = await response.json();
|
|
980
|
+
const edgeConfig = body.data?.edgeConfig;
|
|
981
|
+
if (!isEdgeConfig(edgeConfig)) return null;
|
|
982
|
+
return { edgeConfig, etag: response.headers.get("etag") ?? void 0 };
|
|
983
|
+
} catch {
|
|
984
|
+
return null;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
var DEFAULT_TTL_MS = 60 * 1e3;
|
|
988
|
+
var DEFAULT_MAX_STALE_MS = 24 * 60 * 60 * 1e3;
|
|
989
|
+
var cache2 = /* @__PURE__ */ new Map();
|
|
990
|
+
var inflight = /* @__PURE__ */ new Map();
|
|
991
|
+
function cacheKey(apiBaseUrl, counterpartyId) {
|
|
992
|
+
return `${apiBaseUrl}|${counterpartyId}`;
|
|
993
|
+
}
|
|
994
|
+
function degradeToObserve(config) {
|
|
995
|
+
if (config.mode !== "enforce" && !config.pathRules.some((r) => r.mode === "enforce")) {
|
|
996
|
+
return config;
|
|
997
|
+
}
|
|
998
|
+
return {
|
|
999
|
+
...config,
|
|
1000
|
+
mode: "observe",
|
|
1001
|
+
pathRules: config.pathRules.map((r) => r.mode === "enforce" ? { ...r, mode: "observe" } : r)
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
async function revalidate(config, counterpartyId) {
|
|
1005
|
+
const key = cacheKey(config.apiBaseUrl, counterpartyId);
|
|
1006
|
+
const existing = inflight.get(key);
|
|
1007
|
+
if (existing) return existing;
|
|
1008
|
+
const promise = fetchEdgeConfig(config, counterpartyId, { etag: cache2.get(key)?.etag }).then((result) => {
|
|
1009
|
+
if (result === null) return;
|
|
1010
|
+
const now = Date.now();
|
|
1011
|
+
if ("notModified" in result) {
|
|
1012
|
+
const entry = cache2.get(key);
|
|
1013
|
+
if (entry) entry.lastSuccessAt = now;
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
cache2.set(key, { config: result.edgeConfig, etag: result.etag, lastSuccessAt: now });
|
|
1017
|
+
}).finally(() => {
|
|
1018
|
+
inflight.delete(key);
|
|
1019
|
+
});
|
|
1020
|
+
inflight.set(key, promise);
|
|
1021
|
+
return promise;
|
|
1022
|
+
}
|
|
1023
|
+
async function getEdgeConfig(config, counterpartyId, opts) {
|
|
1024
|
+
const ttlMs = opts?.ttlMs ?? DEFAULT_TTL_MS;
|
|
1025
|
+
const maxStaleMs = opts?.maxStaleMs ?? DEFAULT_MAX_STALE_MS;
|
|
1026
|
+
const key = cacheKey(config.apiBaseUrl, counterpartyId);
|
|
1027
|
+
const entry = cache2.get(key);
|
|
1028
|
+
if (entry) {
|
|
1029
|
+
const age = Date.now() - entry.lastSuccessAt;
|
|
1030
|
+
if (age > ttlMs) {
|
|
1031
|
+
revalidate(config, counterpartyId).catch(() => {
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
return age > maxStaleMs ? degradeToObserve(entry.config) : entry.config;
|
|
1035
|
+
}
|
|
1036
|
+
await revalidate(config, counterpartyId).catch(() => {
|
|
1037
|
+
});
|
|
1038
|
+
return cache2.get(key)?.config ?? DEFAULT_EDGE_CONFIG;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// src/platform-signatures.ts
|
|
1042
|
+
var PLATFORM_AGENT_SIGNATURES = [
|
|
1043
|
+
{
|
|
1044
|
+
vendor: "claude",
|
|
1045
|
+
displayName: "Anthropic Claude",
|
|
1046
|
+
uaPatterns: [/claude/i, /anthropic/i],
|
|
1047
|
+
agentCardPatterns: [/anthropic\.com/i, /claude\.ai/i],
|
|
1048
|
+
apiKeyFormatPatterns: [/^sk-ant/i],
|
|
1049
|
+
headerPatterns: [/^anthropic-version/i, /^anthropic-beta/i, /^x-anthropic-/i]
|
|
1050
|
+
},
|
|
1051
|
+
{
|
|
1052
|
+
vendor: "chatgpt",
|
|
1053
|
+
displayName: "OpenAI ChatGPT",
|
|
1054
|
+
uaPatterns: [/chatgpt/i, /openai/i, /gpt-?\d/i],
|
|
1055
|
+
agentCardPatterns: [/openai\.com/i, /chatgpt\.com/i],
|
|
1056
|
+
apiKeyFormatPatterns: [/^sk-proj/i, /^sk-svcacct/i],
|
|
1057
|
+
headerPatterns: [/^openai-organization/i, /^openai-project/i, /^openai-version/i]
|
|
1058
|
+
},
|
|
1059
|
+
{
|
|
1060
|
+
vendor: "gemini",
|
|
1061
|
+
displayName: "Google Gemini",
|
|
1062
|
+
uaPatterns: [/gemini/i, /google-gemini/i, /bard/i],
|
|
1063
|
+
agentCardPatterns: [/gemini\.google\.com/i, /ai\.google\.dev/i],
|
|
1064
|
+
apiKeyFormatPatterns: [/^aiza/i],
|
|
1065
|
+
headerPatterns: [/^x-goog-user-project/i, /^x-goog-api-client/i]
|
|
1066
|
+
},
|
|
1067
|
+
{
|
|
1068
|
+
vendor: "cursor",
|
|
1069
|
+
displayName: "Cursor",
|
|
1070
|
+
uaPatterns: [/cursor/i],
|
|
1071
|
+
agentCardPatterns: [/cursor\.sh/i, /cursor\.com/i]
|
|
1072
|
+
},
|
|
1073
|
+
{
|
|
1074
|
+
vendor: "goose",
|
|
1075
|
+
displayName: "Block Goose",
|
|
1076
|
+
uaPatterns: [/goose/i, /block-goose/i],
|
|
1077
|
+
agentCardPatterns: [/block\.xyz\/goose/i, /goose\.tools/i]
|
|
1078
|
+
},
|
|
1079
|
+
{
|
|
1080
|
+
vendor: "perplexity",
|
|
1081
|
+
displayName: "Perplexity",
|
|
1082
|
+
uaPatterns: [/perplexity/i],
|
|
1083
|
+
agentCardPatterns: [/perplexity\.ai/i],
|
|
1084
|
+
apiKeyFormatPatterns: [/^pplx-/i]
|
|
1085
|
+
}
|
|
1086
|
+
];
|
|
1087
|
+
function matchPlatformSignature(signatures, input) {
|
|
1088
|
+
const ua = input.userAgent ?? "";
|
|
1089
|
+
const cardUrl = input.agentCardUrl ?? "";
|
|
1090
|
+
const apiKeyFormat = input.apiKeyFormat ?? "";
|
|
1091
|
+
const headerLines = [];
|
|
1092
|
+
if (input.platformHeaders) {
|
|
1093
|
+
for (const [name, value] of Object.entries(input.platformHeaders)) {
|
|
1094
|
+
headerLines.push(name, `${name}: ${value}`);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
for (const sig of signatures) {
|
|
1098
|
+
const uaMatch = sig.uaPatterns?.some((p) => p.test(ua));
|
|
1099
|
+
const cardMatch = sig.agentCardPatterns?.some((p) => p.test(cardUrl));
|
|
1100
|
+
const keyMatch = apiKeyFormat.length > 0 && sig.apiKeyFormatPatterns?.some((p) => p.test(apiKeyFormat));
|
|
1101
|
+
const headerMatch = headerLines.length > 0 && sig.headerPatterns?.some((p) => headerLines.some((line) => p.test(line)));
|
|
1102
|
+
if (uaMatch || cardMatch || keyMatch || headerMatch) {
|
|
1103
|
+
const uaBase = ua.split(/[\s/]/)[0]?.toLowerCase() ?? "";
|
|
1104
|
+
const cardHost = (() => {
|
|
1105
|
+
try {
|
|
1106
|
+
return cardUrl ? new URL(cardUrl).host : "";
|
|
1107
|
+
} catch {
|
|
1108
|
+
return "";
|
|
1109
|
+
}
|
|
1110
|
+
})();
|
|
1111
|
+
const keyBasis = uaBase || cardHost || apiKeyFormat || "unknown";
|
|
1112
|
+
return {
|
|
1113
|
+
vendor: sig.vendor,
|
|
1114
|
+
fingerprintKey: `${sig.vendor}:${keyBasis}`,
|
|
1115
|
+
displayName: sig.displayName
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
return void 0;
|
|
1120
|
+
}
|
|
1121
|
+
function detectPlatformFingerprint(input) {
|
|
1122
|
+
return matchPlatformSignature(PLATFORM_AGENT_SIGNATURES, input);
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
// src/edge-core/classify.ts
|
|
1126
|
+
var STATIC_EXTENSIONS = /\.(css|js|mjs|map|png|jpe?g|gif|webp|avif|svg|ico|woff2?|ttf|otf|eot|mp4|webm|pdf|txt|xml)$/i;
|
|
1127
|
+
var BOT_UA_PATTERN = /curl|wget|python-requests|python-urllib|node-fetch|axios|got|okhttp|go-http-client|libwww|httpie|java\/|ruby|php|scrapy|headless|phantomjs|selenium|playwright|puppeteer|bot|crawler|spider|slurp/i;
|
|
1128
|
+
var BROWSER_UA_PATTERN = /mozilla\/\d/i;
|
|
1129
|
+
var _patterns = { STATIC_EXTENSIONS, BOT_UA_PATTERN, BROWSER_UA_PATTERN };
|
|
1130
|
+
|
|
1131
|
+
// src/edge-core/events.ts
|
|
1132
|
+
function sampled(rate, random = Math.random) {
|
|
1133
|
+
if (rate <= 0) return false;
|
|
1134
|
+
if (rate >= 1) return true;
|
|
1135
|
+
return random() < rate;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
// src/adapters/anonymous-beacon.ts
|
|
1139
|
+
function readHeader(raw, name) {
|
|
1140
|
+
const value = raw[name];
|
|
1141
|
+
if (typeof value === "string") return value;
|
|
1142
|
+
if (Array.isArray(value)) return value[0];
|
|
1143
|
+
return void 0;
|
|
1144
|
+
}
|
|
1145
|
+
function classifyPassThroughTraffic(path, rawHeaders) {
|
|
1146
|
+
if (_patterns.STATIC_EXTENSIONS.test(path)) return { kind: "never" };
|
|
1147
|
+
const credentials = extractCredentials(rawHeaders, {});
|
|
1148
|
+
if (credentials.astraId || credentials.apiKey || credentials.jwt) {
|
|
1149
|
+
return { kind: "never" };
|
|
1150
|
+
}
|
|
1151
|
+
const userAgent = readHeader(rawHeaders, "user-agent") ?? "";
|
|
1152
|
+
const { apiKeyFormat, platformHeaders } = sanitizeHeaders(rawHeaders);
|
|
1153
|
+
const fingerprint = detectPlatformFingerprint({ userAgent, apiKeyFormat, platformHeaders });
|
|
1154
|
+
if (fingerprint) {
|
|
1155
|
+
return {
|
|
1156
|
+
kind: "platform",
|
|
1157
|
+
protocol: "platform-ua",
|
|
1158
|
+
evidence: { vendor: fingerprint.vendor, userAgent: userAgent || "(none)" }
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
if (!userAgent || _patterns.BOT_UA_PATTERN.test(userAgent) || !_patterns.BROWSER_UA_PATTERN.test(userAgent)) {
|
|
1162
|
+
return {
|
|
1163
|
+
kind: "anonymous-bot",
|
|
1164
|
+
protocol: "ua-heuristic",
|
|
1165
|
+
evidence: { userAgent: userAgent || "(none)" }
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
if (!readHeader(rawHeaders, "sec-fetch-mode") && !readHeader(rawHeaders, "accept-language")) {
|
|
1169
|
+
return {
|
|
1170
|
+
kind: "anonymous-bot",
|
|
1171
|
+
protocol: "ua-heuristic",
|
|
1172
|
+
evidence: { userAgent, spoofedBrowser: true }
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
return { kind: "never" };
|
|
1176
|
+
}
|
|
1177
|
+
function createAnonymousBeacon(config, options) {
|
|
1178
|
+
const counterpartyId = config.counterpartyId;
|
|
1179
|
+
if (!counterpartyId) return () => {
|
|
1180
|
+
};
|
|
1181
|
+
const random = options?.random ?? Math.random;
|
|
1182
|
+
let edgeConfig;
|
|
1183
|
+
let refreshing = false;
|
|
1184
|
+
const refreshConfig = () => {
|
|
1185
|
+
if (refreshing) return;
|
|
1186
|
+
refreshing = true;
|
|
1187
|
+
getEdgeConfig(config, counterpartyId).then((cfg) => {
|
|
1188
|
+
edgeConfig = cfg;
|
|
1189
|
+
}).catch(() => {
|
|
1190
|
+
}).finally(() => {
|
|
1191
|
+
refreshing = false;
|
|
1192
|
+
});
|
|
1193
|
+
};
|
|
1194
|
+
refreshConfig();
|
|
1195
|
+
return (input) => {
|
|
1196
|
+
try {
|
|
1197
|
+
refreshConfig();
|
|
1198
|
+
const cfg = edgeConfig;
|
|
1199
|
+
if (!cfg) return;
|
|
1200
|
+
const traffic = classifyPassThroughTraffic(input.requestPath, input.rawHeaders);
|
|
1201
|
+
if (traffic.kind === "never") return;
|
|
1202
|
+
if (traffic.kind === "anonymous-bot" && !sampled(cfg.sampling.anonymousBeaconRate, random)) {
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1205
|
+
const userAgent = readHeader(input.rawHeaders, "user-agent");
|
|
1206
|
+
const xff = input.rawHeaders["x-forwarded-for"];
|
|
1207
|
+
const xffStr = Array.isArray(xff) ? xff.join(", ") : xff;
|
|
1208
|
+
const sourceIp = input.sourceIp ?? xffStr?.split(",")[0]?.trim();
|
|
1209
|
+
reportUnregisteredAttempt(config, {
|
|
1210
|
+
...input.counterpartyUrl && { counterpartyUrl: input.counterpartyUrl },
|
|
1211
|
+
counterpartyType: options?.counterpartyType ?? config.counterpartyType ?? "api",
|
|
1212
|
+
...sourceIp && { sourceIp },
|
|
1213
|
+
...userAgent && { userAgent },
|
|
1214
|
+
requestPath: input.requestPath,
|
|
1215
|
+
requestMethod: input.requestMethod,
|
|
1216
|
+
...traffic.protocol && { protocol: traffic.protocol },
|
|
1217
|
+
...traffic.evidence && { evidence: traffic.evidence },
|
|
1218
|
+
observedMetadata: buildSdkObservedMetadata(input.rawHeaders)
|
|
1219
|
+
}).catch(() => {
|
|
1220
|
+
});
|
|
1221
|
+
} catch {
|
|
1222
|
+
}
|
|
1223
|
+
};
|
|
1224
|
+
}
|
|
822
1225
|
|
|
823
1226
|
// src/adapters/http-pdlss.ts
|
|
824
1227
|
var HTTP_METHOD_ACTION_TABLE = {
|
|
@@ -982,7 +1385,7 @@ function resolveNextPdlss(request, routeConfig) {
|
|
|
982
1385
|
routeAction: routeConfig?.action
|
|
983
1386
|
});
|
|
984
1387
|
}
|
|
985
|
-
function
|
|
1388
|
+
function generateInterstitialHtml(result, options) {
|
|
986
1389
|
const title = escapeHtml(options.commerceShield?.title || "AstraSync Agent Verification");
|
|
987
1390
|
const message = escapeHtml(
|
|
988
1391
|
options.commerceShield?.message || result.guidance?.message || "This site verifies AI agents before granting access. We noticed you're visiting without AstraSync credentials."
|
|
@@ -1138,13 +1541,31 @@ function generateCommerceShieldHtml(result, options) {
|
|
|
1138
1541
|
}
|
|
1139
1542
|
var DEFAULT_ROUTES_REFRESH_MS = 5 * 60 * 1e3;
|
|
1140
1543
|
function createMiddleware(options) {
|
|
1544
|
+
if ("routes" in options) {
|
|
1545
|
+
console.warn(
|
|
1546
|
+
"[VerificationGateway/Next.js] `routes` is dashboard-managed since v2.9.7; this option is ignored \u2014 configure route policy in your AstraSync dashboard."
|
|
1547
|
+
);
|
|
1548
|
+
}
|
|
1141
1549
|
const {
|
|
1142
1550
|
skipPaths = [],
|
|
1143
|
-
|
|
1551
|
+
showInterstitial,
|
|
1552
|
+
showCommerceShield,
|
|
1144
1553
|
enableRuntimeChallenge = true,
|
|
1145
1554
|
routesRefreshMs = DEFAULT_ROUTES_REFRESH_MS,
|
|
1555
|
+
anonymousBeacon = true,
|
|
1146
1556
|
...config
|
|
1147
1557
|
} = options;
|
|
1558
|
+
const interstitialEnabled = showInterstitial ?? showCommerceShield ?? true;
|
|
1559
|
+
const emitAnonymousBeacon = anonymousBeacon ? createAnonymousBeacon(config, { counterpartyType: config.counterpartyType ?? "website" }) : void 0;
|
|
1560
|
+
const beaconPassThrough = emitAnonymousBeacon ? (request, pathname) => {
|
|
1561
|
+
emitAnonymousBeacon({
|
|
1562
|
+
requestPath: pathname,
|
|
1563
|
+
requestMethod: request.method,
|
|
1564
|
+
counterpartyUrl: config.counterpartyUrl || request.nextUrl.origin,
|
|
1565
|
+
sourceIp: request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || void 0,
|
|
1566
|
+
rawHeaders: Object.fromEntries(request.headers.entries())
|
|
1567
|
+
});
|
|
1568
|
+
} : void 0;
|
|
1148
1569
|
let cachedRoutes = [];
|
|
1149
1570
|
let lastFetchAt = 0;
|
|
1150
1571
|
let refreshing = null;
|
|
@@ -1186,9 +1607,11 @@ function createMiddleware(options) {
|
|
|
1186
1607
|
}
|
|
1187
1608
|
const routeConfig = findRouteConfig(cachedRoutes, pathname, request.method);
|
|
1188
1609
|
if (!routeConfig) {
|
|
1610
|
+
beaconPassThrough?.(request, pathname);
|
|
1189
1611
|
return NextResponse.next();
|
|
1190
1612
|
}
|
|
1191
1613
|
if (routeConfig.minAccessLevel === "none") {
|
|
1614
|
+
beaconPassThrough?.(request, pathname);
|
|
1192
1615
|
return NextResponse.next();
|
|
1193
1616
|
}
|
|
1194
1617
|
const credentials = extractCredentialsFromNextRequest(request);
|
|
@@ -1240,8 +1663,8 @@ function createMiddleware(options) {
|
|
|
1240
1663
|
{ status: 403 }
|
|
1241
1664
|
);
|
|
1242
1665
|
}
|
|
1243
|
-
if (
|
|
1244
|
-
return new NextResponse(
|
|
1666
|
+
if (interstitialEnabled) {
|
|
1667
|
+
return new NextResponse(generateInterstitialHtml(preCheckResult, options), {
|
|
1245
1668
|
status: 200,
|
|
1246
1669
|
headers: {
|
|
1247
1670
|
"Content-Type": "text/html",
|
|
@@ -1269,17 +1692,9 @@ function createMiddleware(options) {
|
|
|
1269
1692
|
host: request.headers.get("host") || void 0,
|
|
1270
1693
|
forwardedFor,
|
|
1271
1694
|
agentCardUrl: request.headers.get("x-astrasync-agent-card") || void 0,
|
|
1272
|
-
// Workstream D — full sanitised header capture
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
Object.fromEntries(request.headers.entries())
|
|
1276
|
-
);
|
|
1277
|
-
return {
|
|
1278
|
-
headers,
|
|
1279
|
-
...apiKeyFormat && { apiKeyFormat },
|
|
1280
|
-
...platformHeaders && { platformHeaders }
|
|
1281
|
-
};
|
|
1282
|
-
})()
|
|
1695
|
+
// Workstream D — full sanitised header capture + CDN-derived
|
|
1696
|
+
// connection block (Web Headers → plain map).
|
|
1697
|
+
observedMetadata: buildSdkObservedMetadata(Object.fromEntries(request.headers.entries()))
|
|
1283
1698
|
}
|
|
1284
1699
|
};
|
|
1285
1700
|
let result = await verify(config, verificationRequest);
|
|
@@ -1334,8 +1749,8 @@ function createMiddleware(options) {
|
|
|
1334
1749
|
}
|
|
1335
1750
|
);
|
|
1336
1751
|
}
|
|
1337
|
-
if (
|
|
1338
|
-
return new NextResponse(
|
|
1752
|
+
if (interstitialEnabled) {
|
|
1753
|
+
return new NextResponse(generateInterstitialHtml(result, options), {
|
|
1339
1754
|
status: 200,
|
|
1340
1755
|
headers: {
|
|
1341
1756
|
"Content-Type": "text/html",
|