@astrasyncai/verification-gateway 3.12.0 → 4.1.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 +482 -22
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +482 -22
- 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 +309 -10
- package/dist/adapters/mcp.js.map +1 -1
- package/dist/adapters/mcp.mjs +309 -10
- 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 +518 -25
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +518 -25
- 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 +72 -9
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +72 -9
- 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 +83 -20
- package/dist/bin/astrasync-codex-hook.js +83 -20
- package/dist/bin/astrasync-guard.js +83 -20
- package/dist/bin/astrasync.js +150 -33
- package/dist/browser/background.js +83 -20
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +83 -20
- 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 +83 -20
- package/dist/codex/index.js.map +1 -1
- package/dist/codex/index.mjs +83 -20
- 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 +83 -20
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +83 -20
- 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-B39o89gf.d.mts → express-BVd1_3FE.d.ts} +9 -7
- package/dist/{express-BuxKNO_q.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 +83 -20
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +83 -20
- 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-CLHdqs2M.d.ts → index-B0YHu_SP.d.ts} +30 -27
- package/dist/{index-DME3w3lf.d.mts → index-BPEBlOsE.d.mts} +30 -27
- package/dist/{index-CqydB4ks.d.mts → index-DQb5-_1X.d.mts} +1 -1
- package/dist/{index-BG62SXso.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 +595 -272
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +592 -272
- 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-D74W7wt-.d.mts → nextjs-WyeVr2Kp.d.mts} +3 -3
- package/dist/{nextjs-Ddlw_J1J.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-CT7gdkfR.d.ts → sdk-CbTNIkAa.d.mts} +4 -4
- package/dist/{sdk-Bn6bj7kt.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-DHu7m9HI.d.ts → types-BLUx92FJ.d.ts} +1 -1
- package/dist/{types-Djw4GLtz.d.mts → types-DqfPU5Bl.d.mts} +1 -1
- package/dist/{types-BntuzEEn.d.mts → types-r850cOt0.d.mts} +121 -88
- package/dist/{types-SDu4Llbe.d.ts → types-sMxBT-nM.d.ts} +121 -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 +11 -8
- package/dist/verify.d.ts +11 -8
- package/dist/verify.js +76 -8
- package/dist/verify.js.map +1 -1
- package/dist/verify.mjs +75 -8
- 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 = "
|
|
68
|
+
var SDK_VERSION = "4.1.0";
|
|
69
69
|
|
|
70
70
|
// src/well-known.ts
|
|
71
71
|
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
@@ -91,7 +91,7 @@ var DEFAULT_CONFIG = {
|
|
|
91
91
|
};
|
|
92
92
|
var initCheckPerformed = false;
|
|
93
93
|
var deprecationWarningShown = false;
|
|
94
|
-
async function performInitCheck(apiBaseUrl, debug, strictInit) {
|
|
94
|
+
async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
|
|
95
95
|
initCheckPerformed = true;
|
|
96
96
|
try {
|
|
97
97
|
const probeUrl = `${apiBaseUrl}/agents/verify-access`;
|
|
@@ -108,6 +108,28 @@ async function performInitCheck(apiBaseUrl, debug, strictInit) {
|
|
|
108
108
|
`[VerificationGateway] init check passed for ${apiBaseUrl} (content-type: ${contentType})`
|
|
109
109
|
);
|
|
110
110
|
}
|
|
111
|
+
if (apiKey) {
|
|
112
|
+
const selfCheck = await fetch(`${apiBaseUrl}/agents/verify-access/self-check`, {
|
|
113
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
114
|
+
});
|
|
115
|
+
if (selfCheck.status === 401) {
|
|
116
|
+
const body = await selfCheck.json().catch(() => ({}));
|
|
117
|
+
const message = `[VerificationGateway] the configured AstraSync API key was REJECTED at startup (${body.code ?? "api_key_invalid"}: ${body.message ?? "invalid key"}). Every inbound agent will be turned away until the key is rotated` + (body.remediation?.manageUrl ? ` at ${body.remediation.manageUrl}` : "") + `.`;
|
|
118
|
+
if (strictInit) {
|
|
119
|
+
throw new Error(`${message} (strictInit=true)`);
|
|
120
|
+
}
|
|
121
|
+
console.error(message);
|
|
122
|
+
} else if (selfCheck.ok) {
|
|
123
|
+
const body = await selfCheck.json().catch(() => ({}));
|
|
124
|
+
if (typeof body.daysToExpiry === "number" && body.daysToExpiry <= 7) {
|
|
125
|
+
console.warn(
|
|
126
|
+
`[VerificationGateway] the configured AstraSync API key expires in ${body.daysToExpiry} day(s) \u2014 rotate it at Settings \u2192 Developer Tools before integrations start failing.`
|
|
127
|
+
);
|
|
128
|
+
} else if (debug) {
|
|
129
|
+
console.log("[VerificationGateway] API key self-check passed");
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
111
133
|
} catch (err) {
|
|
112
134
|
if (strictInit) {
|
|
113
135
|
throw err;
|
|
@@ -182,11 +204,54 @@ function cacheResult(request, result, configuredTtl, counterpartyId) {
|
|
|
182
204
|
expiresAt: Date.now() + ttlSeconds * 1e3
|
|
183
205
|
});
|
|
184
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
|
+
}
|
|
185
241
|
function createGuidanceResponse(_config, reason, options = {}) {
|
|
186
242
|
const source = options.source ?? "no_credentials";
|
|
187
243
|
const isApiError = source === "api_error";
|
|
244
|
+
const isMisconfig = source === "merchant_misconfig";
|
|
188
245
|
const urls = options.urls;
|
|
189
|
-
const guidance =
|
|
246
|
+
const guidance = isMisconfig ? {
|
|
247
|
+
message: "This site's AstraSync integration is misconfigured (its API key was revoked, expired, or rotated). Your identity and the AstraSync platform are fine \u2014 contact the site operator. Retrying will not help.",
|
|
248
|
+
registrationUrl: urls?.registrationUrl ?? "",
|
|
249
|
+
documentationUrl: options.remediation?.docsUrl ?? urls?.documentationUrl ?? "",
|
|
250
|
+
steps: [
|
|
251
|
+
"Report the failure to the site operator \u2014 their AstraSync API key needs rotating",
|
|
252
|
+
"Do not retry: the failure is deterministic until the operator updates their key"
|
|
253
|
+
]
|
|
254
|
+
} : isApiError ? {
|
|
190
255
|
message: "Verification is temporarily unavailable. Retry with exponential backoff; if the issue persists, contact support with the correlationId.",
|
|
191
256
|
registrationUrl: urls?.registrationUrl ?? "",
|
|
192
257
|
documentationUrl: urls?.documentationUrl ?? "",
|
|
@@ -224,13 +289,22 @@ function createGuidanceResponse(_config, reason, options = {}) {
|
|
|
224
289
|
// partners (and their custom onDenied handlers) can branch on
|
|
225
290
|
// dimension. Without this, the synthesised stub was indistinguishable
|
|
226
291
|
// from a real policy deny.
|
|
227
|
-
failures:
|
|
292
|
+
failures: isMisconfig ? [
|
|
293
|
+
{
|
|
294
|
+
dimension: "verify_access.merchant_misconfigured",
|
|
295
|
+
message: reason ?? "The integration API key was rejected by AstraSync",
|
|
296
|
+
guidance: guidance.message
|
|
297
|
+
}
|
|
298
|
+
] : isApiError ? [
|
|
228
299
|
{
|
|
229
300
|
dimension: "verify_access.api_error",
|
|
230
301
|
message: reason ?? "Verification temporarily unavailable",
|
|
231
302
|
guidance: guidance.message
|
|
232
303
|
}
|
|
233
304
|
] : void 0,
|
|
305
|
+
// 4.0.0: typed marker so adapters render the misconfig envelope (503 +
|
|
306
|
+
// MERCHANT_VERIFICATION_MISCONFIGURED) without string-matching.
|
|
307
|
+
...isMisconfig && { misconfigured: true },
|
|
234
308
|
correlationId: options.correlationId,
|
|
235
309
|
attemptId: options.attemptId,
|
|
236
310
|
verifiedAt: /* @__PURE__ */ new Date()
|
|
@@ -285,7 +359,8 @@ async function callVerifyAccessAPI(config, request) {
|
|
|
285
359
|
const response = await fetch(`${config.apiBaseUrl}/agents/verify-access`, {
|
|
286
360
|
method: "POST",
|
|
287
361
|
headers,
|
|
288
|
-
body: JSON.stringify(body)
|
|
362
|
+
body: JSON.stringify(body),
|
|
363
|
+
signal: AbortSignal.timeout(config.verifyTimeoutMs ?? 1e4)
|
|
289
364
|
});
|
|
290
365
|
const data = await response.json();
|
|
291
366
|
if (response.status === 410) {
|
|
@@ -309,6 +384,17 @@ async function callVerifyAccessAPI(config, request) {
|
|
|
309
384
|
};
|
|
310
385
|
}
|
|
311
386
|
if (!response.ok) {
|
|
387
|
+
if (response.status === 401) {
|
|
388
|
+
return {
|
|
389
|
+
success: false,
|
|
390
|
+
error: data.message || data.error || "Integration API key rejected",
|
|
391
|
+
misconfig: true,
|
|
392
|
+
misconfigCode: typeof data?.code === "string" ? data.code : void 0,
|
|
393
|
+
remediation: data?.remediation && typeof data.remediation === "object" ? data.remediation : void 0,
|
|
394
|
+
correlationId: typeof data?.correlationId === "string" ? data.correlationId : void 0,
|
|
395
|
+
attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0
|
|
396
|
+
};
|
|
397
|
+
}
|
|
312
398
|
return {
|
|
313
399
|
success: false,
|
|
314
400
|
error: data.message || data.error || `API returned ${response.status}`,
|
|
@@ -330,9 +416,19 @@ async function verify(config, request, options) {
|
|
|
330
416
|
const urls = mergedConfig.apiBaseUrl ? getCachedWellKnownUrls(mergedConfig.apiBaseUrl) : void 0;
|
|
331
417
|
if (!initCheckPerformed && !mergedConfig.disableInitChecks && mergedConfig.apiBaseUrl) {
|
|
332
418
|
if (mergedConfig.strictInit) {
|
|
333
|
-
await performInitCheck(
|
|
419
|
+
await performInitCheck(
|
|
420
|
+
mergedConfig.apiBaseUrl,
|
|
421
|
+
mergedConfig.debug,
|
|
422
|
+
true,
|
|
423
|
+
mergedConfig.apiKey
|
|
424
|
+
);
|
|
334
425
|
} else {
|
|
335
|
-
void performInitCheck(
|
|
426
|
+
void performInitCheck(
|
|
427
|
+
mergedConfig.apiBaseUrl,
|
|
428
|
+
mergedConfig.debug,
|
|
429
|
+
false,
|
|
430
|
+
mergedConfig.apiKey
|
|
431
|
+
);
|
|
336
432
|
}
|
|
337
433
|
}
|
|
338
434
|
if (!deprecationWarningShown && (config.minTrustScore !== void 0 || config.minTrustScoreForFull !== void 0)) {
|
|
@@ -371,9 +467,10 @@ async function verify(config, request, options) {
|
|
|
371
467
|
const serverDenialReasons = Array.isArray(rawServerDenialReasons) && rawServerDenialReasons.length > 0 ? rawServerDenialReasons : void 0;
|
|
372
468
|
if (!apiResponse.success) {
|
|
373
469
|
return createGuidanceResponse(mergedConfig, apiResponse.error, {
|
|
374
|
-
source: "api_error",
|
|
470
|
+
source: apiResponse.misconfig ? "merchant_misconfig" : "api_error",
|
|
375
471
|
correlationId: apiResponse.correlationId,
|
|
376
472
|
attemptId: apiResponse.attemptId,
|
|
473
|
+
remediation: apiResponse.remediation,
|
|
377
474
|
urls
|
|
378
475
|
});
|
|
379
476
|
}
|
|
@@ -515,6 +612,21 @@ async function fetchRoutes(config, counterpartyId) {
|
|
|
515
612
|
return null;
|
|
516
613
|
}
|
|
517
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
|
+
}
|
|
518
630
|
async function reportCounterpartyPreCheckFailure(config, data) {
|
|
519
631
|
const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl;
|
|
520
632
|
await fetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {
|
|
@@ -620,6 +732,7 @@ function performCounterpartyPreCheck(routeConfig, astraCreds, purpose) {
|
|
|
620
732
|
}
|
|
621
733
|
|
|
622
734
|
// src/metadata-capture.ts
|
|
735
|
+
var CAPTURE_SCHEMA_VERSION = 1;
|
|
623
736
|
var SECRET_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
624
737
|
"cookie",
|
|
625
738
|
"set-cookie",
|
|
@@ -756,6 +869,359 @@ function sanitizeHeaders(raw) {
|
|
|
756
869
|
if (Object.keys(platformHeaders).length > 0) result.platformHeaders = platformHeaders;
|
|
757
870
|
return result;
|
|
758
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
|
+
}
|
|
759
1225
|
|
|
760
1226
|
// src/adapters/http-pdlss.ts
|
|
761
1227
|
var HTTP_METHOD_ACTION_TABLE = {
|
|
@@ -919,7 +1385,7 @@ function resolveNextPdlss(request, routeConfig) {
|
|
|
919
1385
|
routeAction: routeConfig?.action
|
|
920
1386
|
});
|
|
921
1387
|
}
|
|
922
|
-
function
|
|
1388
|
+
function generateInterstitialHtml(result, options) {
|
|
923
1389
|
const title = escapeHtml(options.commerceShield?.title || "AstraSync Agent Verification");
|
|
924
1390
|
const message = escapeHtml(
|
|
925
1391
|
options.commerceShield?.message || result.guidance?.message || "This site verifies AI agents before granting access. We noticed you're visiting without AstraSync credentials."
|
|
@@ -1075,13 +1541,31 @@ function generateCommerceShieldHtml(result, options) {
|
|
|
1075
1541
|
}
|
|
1076
1542
|
var DEFAULT_ROUTES_REFRESH_MS = 5 * 60 * 1e3;
|
|
1077
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
|
+
}
|
|
1078
1549
|
const {
|
|
1079
1550
|
skipPaths = [],
|
|
1080
|
-
|
|
1551
|
+
showInterstitial,
|
|
1552
|
+
showCommerceShield,
|
|
1081
1553
|
enableRuntimeChallenge = true,
|
|
1082
1554
|
routesRefreshMs = DEFAULT_ROUTES_REFRESH_MS,
|
|
1555
|
+
anonymousBeacon = true,
|
|
1083
1556
|
...config
|
|
1084
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;
|
|
1085
1569
|
let cachedRoutes = [];
|
|
1086
1570
|
let lastFetchAt = 0;
|
|
1087
1571
|
let refreshing = null;
|
|
@@ -1123,9 +1607,11 @@ function createMiddleware(options) {
|
|
|
1123
1607
|
}
|
|
1124
1608
|
const routeConfig = findRouteConfig(cachedRoutes, pathname, request.method);
|
|
1125
1609
|
if (!routeConfig) {
|
|
1610
|
+
beaconPassThrough?.(request, pathname);
|
|
1126
1611
|
return NextResponse.next();
|
|
1127
1612
|
}
|
|
1128
1613
|
if (routeConfig.minAccessLevel === "none") {
|
|
1614
|
+
beaconPassThrough?.(request, pathname);
|
|
1129
1615
|
return NextResponse.next();
|
|
1130
1616
|
}
|
|
1131
1617
|
const credentials = extractCredentialsFromNextRequest(request);
|
|
@@ -1177,8 +1663,8 @@ function createMiddleware(options) {
|
|
|
1177
1663
|
{ status: 403 }
|
|
1178
1664
|
);
|
|
1179
1665
|
}
|
|
1180
|
-
if (
|
|
1181
|
-
return new NextResponse(
|
|
1666
|
+
if (interstitialEnabled) {
|
|
1667
|
+
return new NextResponse(generateInterstitialHtml(preCheckResult, options), {
|
|
1182
1668
|
status: 200,
|
|
1183
1669
|
headers: {
|
|
1184
1670
|
"Content-Type": "text/html",
|
|
@@ -1206,17 +1692,9 @@ function createMiddleware(options) {
|
|
|
1206
1692
|
host: request.headers.get("host") || void 0,
|
|
1207
1693
|
forwardedFor,
|
|
1208
1694
|
agentCardUrl: request.headers.get("x-astrasync-agent-card") || void 0,
|
|
1209
|
-
// Workstream D — full sanitised header capture
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
Object.fromEntries(request.headers.entries())
|
|
1213
|
-
);
|
|
1214
|
-
return {
|
|
1215
|
-
headers,
|
|
1216
|
-
...apiKeyFormat && { apiKeyFormat },
|
|
1217
|
-
...platformHeaders && { platformHeaders }
|
|
1218
|
-
};
|
|
1219
|
-
})()
|
|
1695
|
+
// Workstream D — full sanitised header capture + CDN-derived
|
|
1696
|
+
// connection block (Web Headers → plain map).
|
|
1697
|
+
observedMetadata: buildSdkObservedMetadata(Object.fromEntries(request.headers.entries()))
|
|
1220
1698
|
}
|
|
1221
1699
|
};
|
|
1222
1700
|
let result = await verify(config, verificationRequest);
|
|
@@ -1232,6 +1710,21 @@ function createMiddleware(options) {
|
|
|
1232
1710
|
const approvalRequired = result.identityVerified && result.policyAllowed && requiresHumanApproval(result);
|
|
1233
1711
|
if (approvalRequired) annotateApprovalRequired(result);
|
|
1234
1712
|
if (!result.identityVerified || !result.policyAllowed || approvalRequired) {
|
|
1713
|
+
if (result.misconfigured && pathname.startsWith("/api/")) {
|
|
1714
|
+
return NextResponse.json(
|
|
1715
|
+
{
|
|
1716
|
+
success: false,
|
|
1717
|
+
error: {
|
|
1718
|
+
code: "MERCHANT_VERIFICATION_MISCONFIGURED",
|
|
1719
|
+
message: result.denialReasons?.[0] || "This site's AstraSync integration is misconfigured",
|
|
1720
|
+
guidance: result.guidance,
|
|
1721
|
+
failures: result.failures,
|
|
1722
|
+
correlationId: result.correlationId
|
|
1723
|
+
}
|
|
1724
|
+
},
|
|
1725
|
+
{ status: 503 }
|
|
1726
|
+
);
|
|
1727
|
+
}
|
|
1235
1728
|
if (pathname.startsWith("/api/")) {
|
|
1236
1729
|
return NextResponse.json(
|
|
1237
1730
|
{
|
|
@@ -1256,8 +1749,8 @@ function createMiddleware(options) {
|
|
|
1256
1749
|
}
|
|
1257
1750
|
);
|
|
1258
1751
|
}
|
|
1259
|
-
if (
|
|
1260
|
-
return new NextResponse(
|
|
1752
|
+
if (interstitialEnabled) {
|
|
1753
|
+
return new NextResponse(generateInterstitialHtml(result, options), {
|
|
1261
1754
|
status: 200,
|
|
1262
1755
|
headers: {
|
|
1263
1756
|
"Content-Type": "text/html",
|