@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.js
CHANGED
|
@@ -102,7 +102,7 @@ function getTrustLevel(score) {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
// src/version.ts
|
|
105
|
-
var SDK_VERSION = "
|
|
105
|
+
var SDK_VERSION = "4.1.0";
|
|
106
106
|
|
|
107
107
|
// src/well-known.ts
|
|
108
108
|
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
@@ -128,7 +128,7 @@ var DEFAULT_CONFIG = {
|
|
|
128
128
|
};
|
|
129
129
|
var initCheckPerformed = false;
|
|
130
130
|
var deprecationWarningShown = false;
|
|
131
|
-
async function performInitCheck(apiBaseUrl, debug, strictInit) {
|
|
131
|
+
async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
|
|
132
132
|
initCheckPerformed = true;
|
|
133
133
|
try {
|
|
134
134
|
const probeUrl = `${apiBaseUrl}/agents/verify-access`;
|
|
@@ -145,6 +145,28 @@ async function performInitCheck(apiBaseUrl, debug, strictInit) {
|
|
|
145
145
|
`[VerificationGateway] init check passed for ${apiBaseUrl} (content-type: ${contentType})`
|
|
146
146
|
);
|
|
147
147
|
}
|
|
148
|
+
if (apiKey) {
|
|
149
|
+
const selfCheck = await fetch(`${apiBaseUrl}/agents/verify-access/self-check`, {
|
|
150
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
151
|
+
});
|
|
152
|
+
if (selfCheck.status === 401) {
|
|
153
|
+
const body = await selfCheck.json().catch(() => ({}));
|
|
154
|
+
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}` : "") + `.`;
|
|
155
|
+
if (strictInit) {
|
|
156
|
+
throw new Error(`${message} (strictInit=true)`);
|
|
157
|
+
}
|
|
158
|
+
console.error(message);
|
|
159
|
+
} else if (selfCheck.ok) {
|
|
160
|
+
const body = await selfCheck.json().catch(() => ({}));
|
|
161
|
+
if (typeof body.daysToExpiry === "number" && body.daysToExpiry <= 7) {
|
|
162
|
+
console.warn(
|
|
163
|
+
`[VerificationGateway] the configured AstraSync API key expires in ${body.daysToExpiry} day(s) \u2014 rotate it at Settings \u2192 Developer Tools before integrations start failing.`
|
|
164
|
+
);
|
|
165
|
+
} else if (debug) {
|
|
166
|
+
console.log("[VerificationGateway] API key self-check passed");
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
148
170
|
} catch (err) {
|
|
149
171
|
if (strictInit) {
|
|
150
172
|
throw err;
|
|
@@ -219,11 +241,54 @@ function cacheResult(request, result, configuredTtl, counterpartyId) {
|
|
|
219
241
|
expiresAt: Date.now() + ttlSeconds * 1e3
|
|
220
242
|
});
|
|
221
243
|
}
|
|
244
|
+
function extractCredentials(headers, query) {
|
|
245
|
+
const credentials = {};
|
|
246
|
+
const ASTRA_ID_PATTERN = /^ASTRAE?-[A-Za-z0-9_-]{1,64}$/;
|
|
247
|
+
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"];
|
|
248
|
+
if (astraIdHeader) {
|
|
249
|
+
const raw = Array.isArray(astraIdHeader) ? astraIdHeader[0] : typeof astraIdHeader === "string" ? astraIdHeader : void 0;
|
|
250
|
+
if (typeof raw === "string" && ASTRA_ID_PATTERN.test(raw)) {
|
|
251
|
+
credentials.astraId = raw;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
const apiKeyHeader = headers["x-api-key"] || headers["X-Api-Key"] || headers["X-API-KEY"];
|
|
255
|
+
if (apiKeyHeader) {
|
|
256
|
+
credentials.apiKey = Array.isArray(apiKeyHeader) ? apiKeyHeader[0] : apiKeyHeader;
|
|
257
|
+
}
|
|
258
|
+
const authHeader = headers["authorization"] || headers["Authorization"];
|
|
259
|
+
if (authHeader) {
|
|
260
|
+
const authValue = Array.isArray(authHeader) ? authHeader[0] : authHeader;
|
|
261
|
+
if (typeof authValue === "string") {
|
|
262
|
+
credentials.authorizationHeader = authValue;
|
|
263
|
+
if (authValue.startsWith("Bearer ")) {
|
|
264
|
+
credentials.jwt = authValue.slice(7);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (query) {
|
|
269
|
+
if (query.astraId && !credentials.astraId) {
|
|
270
|
+
credentials.astraId = query.astraId;
|
|
271
|
+
}
|
|
272
|
+
if (query.apiKey && !credentials.apiKey) {
|
|
273
|
+
credentials.apiKey = query.apiKey;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return credentials;
|
|
277
|
+
}
|
|
222
278
|
function createGuidanceResponse(_config, reason, options = {}) {
|
|
223
279
|
const source = options.source ?? "no_credentials";
|
|
224
280
|
const isApiError = source === "api_error";
|
|
281
|
+
const isMisconfig = source === "merchant_misconfig";
|
|
225
282
|
const urls = options.urls;
|
|
226
|
-
const guidance =
|
|
283
|
+
const guidance = isMisconfig ? {
|
|
284
|
+
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.",
|
|
285
|
+
registrationUrl: urls?.registrationUrl ?? "",
|
|
286
|
+
documentationUrl: options.remediation?.docsUrl ?? urls?.documentationUrl ?? "",
|
|
287
|
+
steps: [
|
|
288
|
+
"Report the failure to the site operator \u2014 their AstraSync API key needs rotating",
|
|
289
|
+
"Do not retry: the failure is deterministic until the operator updates their key"
|
|
290
|
+
]
|
|
291
|
+
} : isApiError ? {
|
|
227
292
|
message: "Verification is temporarily unavailable. Retry with exponential backoff; if the issue persists, contact support with the correlationId.",
|
|
228
293
|
registrationUrl: urls?.registrationUrl ?? "",
|
|
229
294
|
documentationUrl: urls?.documentationUrl ?? "",
|
|
@@ -261,13 +326,22 @@ function createGuidanceResponse(_config, reason, options = {}) {
|
|
|
261
326
|
// partners (and their custom onDenied handlers) can branch on
|
|
262
327
|
// dimension. Without this, the synthesised stub was indistinguishable
|
|
263
328
|
// from a real policy deny.
|
|
264
|
-
failures:
|
|
329
|
+
failures: isMisconfig ? [
|
|
330
|
+
{
|
|
331
|
+
dimension: "verify_access.merchant_misconfigured",
|
|
332
|
+
message: reason ?? "The integration API key was rejected by AstraSync",
|
|
333
|
+
guidance: guidance.message
|
|
334
|
+
}
|
|
335
|
+
] : isApiError ? [
|
|
265
336
|
{
|
|
266
337
|
dimension: "verify_access.api_error",
|
|
267
338
|
message: reason ?? "Verification temporarily unavailable",
|
|
268
339
|
guidance: guidance.message
|
|
269
340
|
}
|
|
270
341
|
] : void 0,
|
|
342
|
+
// 4.0.0: typed marker so adapters render the misconfig envelope (503 +
|
|
343
|
+
// MERCHANT_VERIFICATION_MISCONFIGURED) without string-matching.
|
|
344
|
+
...isMisconfig && { misconfigured: true },
|
|
271
345
|
correlationId: options.correlationId,
|
|
272
346
|
attemptId: options.attemptId,
|
|
273
347
|
verifiedAt: /* @__PURE__ */ new Date()
|
|
@@ -322,7 +396,8 @@ async function callVerifyAccessAPI(config, request) {
|
|
|
322
396
|
const response = await fetch(`${config.apiBaseUrl}/agents/verify-access`, {
|
|
323
397
|
method: "POST",
|
|
324
398
|
headers,
|
|
325
|
-
body: JSON.stringify(body)
|
|
399
|
+
body: JSON.stringify(body),
|
|
400
|
+
signal: AbortSignal.timeout(config.verifyTimeoutMs ?? 1e4)
|
|
326
401
|
});
|
|
327
402
|
const data = await response.json();
|
|
328
403
|
if (response.status === 410) {
|
|
@@ -346,6 +421,17 @@ async function callVerifyAccessAPI(config, request) {
|
|
|
346
421
|
};
|
|
347
422
|
}
|
|
348
423
|
if (!response.ok) {
|
|
424
|
+
if (response.status === 401) {
|
|
425
|
+
return {
|
|
426
|
+
success: false,
|
|
427
|
+
error: data.message || data.error || "Integration API key rejected",
|
|
428
|
+
misconfig: true,
|
|
429
|
+
misconfigCode: typeof data?.code === "string" ? data.code : void 0,
|
|
430
|
+
remediation: data?.remediation && typeof data.remediation === "object" ? data.remediation : void 0,
|
|
431
|
+
correlationId: typeof data?.correlationId === "string" ? data.correlationId : void 0,
|
|
432
|
+
attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0
|
|
433
|
+
};
|
|
434
|
+
}
|
|
349
435
|
return {
|
|
350
436
|
success: false,
|
|
351
437
|
error: data.message || data.error || `API returned ${response.status}`,
|
|
@@ -367,9 +453,19 @@ async function verify(config, request, options) {
|
|
|
367
453
|
const urls = mergedConfig.apiBaseUrl ? getCachedWellKnownUrls(mergedConfig.apiBaseUrl) : void 0;
|
|
368
454
|
if (!initCheckPerformed && !mergedConfig.disableInitChecks && mergedConfig.apiBaseUrl) {
|
|
369
455
|
if (mergedConfig.strictInit) {
|
|
370
|
-
await performInitCheck(
|
|
456
|
+
await performInitCheck(
|
|
457
|
+
mergedConfig.apiBaseUrl,
|
|
458
|
+
mergedConfig.debug,
|
|
459
|
+
true,
|
|
460
|
+
mergedConfig.apiKey
|
|
461
|
+
);
|
|
371
462
|
} else {
|
|
372
|
-
void performInitCheck(
|
|
463
|
+
void performInitCheck(
|
|
464
|
+
mergedConfig.apiBaseUrl,
|
|
465
|
+
mergedConfig.debug,
|
|
466
|
+
false,
|
|
467
|
+
mergedConfig.apiKey
|
|
468
|
+
);
|
|
373
469
|
}
|
|
374
470
|
}
|
|
375
471
|
if (!deprecationWarningShown && (config.minTrustScore !== void 0 || config.minTrustScoreForFull !== void 0)) {
|
|
@@ -408,9 +504,10 @@ async function verify(config, request, options) {
|
|
|
408
504
|
const serverDenialReasons = Array.isArray(rawServerDenialReasons) && rawServerDenialReasons.length > 0 ? rawServerDenialReasons : void 0;
|
|
409
505
|
if (!apiResponse.success) {
|
|
410
506
|
return createGuidanceResponse(mergedConfig, apiResponse.error, {
|
|
411
|
-
source: "api_error",
|
|
507
|
+
source: apiResponse.misconfig ? "merchant_misconfig" : "api_error",
|
|
412
508
|
correlationId: apiResponse.correlationId,
|
|
413
509
|
attemptId: apiResponse.attemptId,
|
|
510
|
+
remediation: apiResponse.remediation,
|
|
414
511
|
urls
|
|
415
512
|
});
|
|
416
513
|
}
|
|
@@ -552,6 +649,21 @@ async function fetchRoutes(config, counterpartyId) {
|
|
|
552
649
|
return null;
|
|
553
650
|
}
|
|
554
651
|
}
|
|
652
|
+
async function reportUnregisteredAttempt(config, data) {
|
|
653
|
+
const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl;
|
|
654
|
+
const { counterpartyUrl, ...rest } = data;
|
|
655
|
+
const body = {
|
|
656
|
+
...rest,
|
|
657
|
+
...counterpartyUrl && { counterpartyUrl },
|
|
658
|
+
...config.counterpartyId && { counterpartyId: config.counterpartyId }
|
|
659
|
+
};
|
|
660
|
+
await fetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
|
|
661
|
+
method: "POST",
|
|
662
|
+
headers: { "Content-Type": "application/json" },
|
|
663
|
+
body: JSON.stringify(body)
|
|
664
|
+
}).catch(() => {
|
|
665
|
+
});
|
|
666
|
+
}
|
|
555
667
|
async function reportCounterpartyPreCheckFailure(config, data) {
|
|
556
668
|
const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl;
|
|
557
669
|
await fetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {
|
|
@@ -657,6 +769,7 @@ function performCounterpartyPreCheck(routeConfig, astraCreds, purpose) {
|
|
|
657
769
|
}
|
|
658
770
|
|
|
659
771
|
// src/metadata-capture.ts
|
|
772
|
+
var CAPTURE_SCHEMA_VERSION = 1;
|
|
660
773
|
var SECRET_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
661
774
|
"cookie",
|
|
662
775
|
"set-cookie",
|
|
@@ -793,6 +906,359 @@ function sanitizeHeaders(raw) {
|
|
|
793
906
|
if (Object.keys(platformHeaders).length > 0) result.platformHeaders = platformHeaders;
|
|
794
907
|
return result;
|
|
795
908
|
}
|
|
909
|
+
var CONNECTION_HEADER_SOURCES = [
|
|
910
|
+
["ip", ["cf-connecting-ip", "fastly-client-ip", "true-client-ip", "x-real-ip"]],
|
|
911
|
+
[
|
|
912
|
+
"country",
|
|
913
|
+
["cf-ipcountry", "x-vercel-ip-country", "cloudfront-viewer-country", "fastly-geo-country"]
|
|
914
|
+
],
|
|
915
|
+
["region", ["x-vercel-ip-country-region", "cloudfront-viewer-country-region"]],
|
|
916
|
+
["city", ["x-vercel-ip-city", "cloudfront-viewer-city"]],
|
|
917
|
+
["asn", ["cloudfront-viewer-asn"]],
|
|
918
|
+
["tlsVersion", ["cloudfront-viewer-tls"]],
|
|
919
|
+
["httpVersion", ["cloudfront-viewer-http-version"]],
|
|
920
|
+
// TLS fingerprints can only be computed where TLS terminates. When a CDN
|
|
921
|
+
// terminates TLS in front of the merchant, its fingerprint headers ARE the
|
|
922
|
+
// connection-layer truth — normalising them here is the permanent design.
|
|
923
|
+
["tlsFingerprint", ["cf-ja3-hash"]],
|
|
924
|
+
["ja4", ["cf-ja4"]]
|
|
925
|
+
];
|
|
926
|
+
function deriveConnectionFromHeaders(headers) {
|
|
927
|
+
if (!headers) return void 0;
|
|
928
|
+
const lower = {};
|
|
929
|
+
for (const name of Object.keys(headers)) {
|
|
930
|
+
const value = headers[name];
|
|
931
|
+
if (typeof value === "string" && value.length > 0) lower[name.toLowerCase()] = value;
|
|
932
|
+
}
|
|
933
|
+
const connection = {};
|
|
934
|
+
for (const [key, sources] of CONNECTION_HEADER_SOURCES) {
|
|
935
|
+
for (const source of sources) {
|
|
936
|
+
const value = lower[source]?.trim();
|
|
937
|
+
if (value) {
|
|
938
|
+
connection[key] = value;
|
|
939
|
+
break;
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
if (!connection.ip) {
|
|
944
|
+
const xff = lower["x-forwarded-for"];
|
|
945
|
+
const client = xff?.split(",")[0]?.trim();
|
|
946
|
+
if (client) connection.ip = client;
|
|
947
|
+
}
|
|
948
|
+
return Object.keys(connection).length > 0 ? connection : void 0;
|
|
949
|
+
}
|
|
950
|
+
function buildSdkObservedMetadata(rawHeaders) {
|
|
951
|
+
const { headers, apiKeyFormat, platformHeaders } = sanitizeHeaders(rawHeaders);
|
|
952
|
+
const flat = {};
|
|
953
|
+
if (rawHeaders) {
|
|
954
|
+
for (const name of Object.keys(rawHeaders)) {
|
|
955
|
+
const value = rawHeaders[name];
|
|
956
|
+
if (value === void 0) continue;
|
|
957
|
+
flat[name.toLowerCase()] = Array.isArray(value) ? value.join(", ") : String(value);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
const connection = deriveConnectionFromHeaders(flat);
|
|
961
|
+
return {
|
|
962
|
+
headers,
|
|
963
|
+
...apiKeyFormat && { apiKeyFormat },
|
|
964
|
+
...platformHeaders && { platformHeaders },
|
|
965
|
+
...connection && { connection },
|
|
966
|
+
schemaVersion: CAPTURE_SCHEMA_VERSION
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
// src/edge-config.ts
|
|
971
|
+
var DEFAULT_EDGE_CONFIG = Object.freeze({
|
|
972
|
+
version: 1,
|
|
973
|
+
mode: "observe",
|
|
974
|
+
depth: "classify",
|
|
975
|
+
pathRules: [],
|
|
976
|
+
sampling: Object.freeze({ anonymousBeaconRate: 0.1 }),
|
|
977
|
+
failurePosture: "open"
|
|
978
|
+
});
|
|
979
|
+
var EDGE_MODES = ["observe", "enforce"];
|
|
980
|
+
var EDGE_DEPTHS = ["classify", "authenticate", "authorize"];
|
|
981
|
+
function isEdgeConfig(value) {
|
|
982
|
+
if (typeof value !== "object" || value === null) return false;
|
|
983
|
+
const v = value;
|
|
984
|
+
if (v.version !== 1) return false;
|
|
985
|
+
if (!EDGE_MODES.includes(v.mode)) return false;
|
|
986
|
+
if (!EDGE_DEPTHS.includes(v.depth)) return false;
|
|
987
|
+
if (!Array.isArray(v.pathRules)) return false;
|
|
988
|
+
for (const rule of v.pathRules) {
|
|
989
|
+
if (typeof rule !== "object" || rule === null) return false;
|
|
990
|
+
const r = rule;
|
|
991
|
+
if (typeof r.pattern !== "string" || r.pattern.length === 0) return false;
|
|
992
|
+
if (r.mode !== void 0 && !EDGE_MODES.includes(r.mode)) return false;
|
|
993
|
+
if (r.depth !== void 0 && !EDGE_DEPTHS.includes(r.depth)) return false;
|
|
994
|
+
}
|
|
995
|
+
const sampling = v.sampling;
|
|
996
|
+
if (typeof sampling !== "object" || sampling === null || typeof sampling.anonymousBeaconRate !== "number" || sampling.anonymousBeaconRate < 0 || sampling.anonymousBeaconRate > 1) {
|
|
997
|
+
return false;
|
|
998
|
+
}
|
|
999
|
+
return v.failurePosture === "open";
|
|
1000
|
+
}
|
|
1001
|
+
async function fetchEdgeConfig(config, counterpartyId, opts) {
|
|
1002
|
+
if (!counterpartyId) return null;
|
|
1003
|
+
const headers = { Accept: "application/json" };
|
|
1004
|
+
if (config.apiKey) {
|
|
1005
|
+
headers["Authorization"] = `Bearer ${config.apiKey}`;
|
|
1006
|
+
headers["X-API-Key"] = config.apiKey;
|
|
1007
|
+
}
|
|
1008
|
+
if (opts?.etag) headers["If-None-Match"] = opts.etag;
|
|
1009
|
+
try {
|
|
1010
|
+
const response = await fetch(
|
|
1011
|
+
`${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/edge-config`,
|
|
1012
|
+
{ method: "GET", headers, signal: AbortSignal.timeout(opts?.timeoutMs ?? 5e3) }
|
|
1013
|
+
);
|
|
1014
|
+
if (response.status === 304) return { notModified: true };
|
|
1015
|
+
if (!response.ok) return null;
|
|
1016
|
+
const body = await response.json();
|
|
1017
|
+
const edgeConfig = body.data?.edgeConfig;
|
|
1018
|
+
if (!isEdgeConfig(edgeConfig)) return null;
|
|
1019
|
+
return { edgeConfig, etag: response.headers.get("etag") ?? void 0 };
|
|
1020
|
+
} catch {
|
|
1021
|
+
return null;
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
var DEFAULT_TTL_MS = 60 * 1e3;
|
|
1025
|
+
var DEFAULT_MAX_STALE_MS = 24 * 60 * 60 * 1e3;
|
|
1026
|
+
var cache2 = /* @__PURE__ */ new Map();
|
|
1027
|
+
var inflight = /* @__PURE__ */ new Map();
|
|
1028
|
+
function cacheKey(apiBaseUrl, counterpartyId) {
|
|
1029
|
+
return `${apiBaseUrl}|${counterpartyId}`;
|
|
1030
|
+
}
|
|
1031
|
+
function degradeToObserve(config) {
|
|
1032
|
+
if (config.mode !== "enforce" && !config.pathRules.some((r) => r.mode === "enforce")) {
|
|
1033
|
+
return config;
|
|
1034
|
+
}
|
|
1035
|
+
return {
|
|
1036
|
+
...config,
|
|
1037
|
+
mode: "observe",
|
|
1038
|
+
pathRules: config.pathRules.map((r) => r.mode === "enforce" ? { ...r, mode: "observe" } : r)
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
async function revalidate(config, counterpartyId) {
|
|
1042
|
+
const key = cacheKey(config.apiBaseUrl, counterpartyId);
|
|
1043
|
+
const existing = inflight.get(key);
|
|
1044
|
+
if (existing) return existing;
|
|
1045
|
+
const promise = fetchEdgeConfig(config, counterpartyId, { etag: cache2.get(key)?.etag }).then((result) => {
|
|
1046
|
+
if (result === null) return;
|
|
1047
|
+
const now = Date.now();
|
|
1048
|
+
if ("notModified" in result) {
|
|
1049
|
+
const entry = cache2.get(key);
|
|
1050
|
+
if (entry) entry.lastSuccessAt = now;
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
cache2.set(key, { config: result.edgeConfig, etag: result.etag, lastSuccessAt: now });
|
|
1054
|
+
}).finally(() => {
|
|
1055
|
+
inflight.delete(key);
|
|
1056
|
+
});
|
|
1057
|
+
inflight.set(key, promise);
|
|
1058
|
+
return promise;
|
|
1059
|
+
}
|
|
1060
|
+
async function getEdgeConfig(config, counterpartyId, opts) {
|
|
1061
|
+
const ttlMs = opts?.ttlMs ?? DEFAULT_TTL_MS;
|
|
1062
|
+
const maxStaleMs = opts?.maxStaleMs ?? DEFAULT_MAX_STALE_MS;
|
|
1063
|
+
const key = cacheKey(config.apiBaseUrl, counterpartyId);
|
|
1064
|
+
const entry = cache2.get(key);
|
|
1065
|
+
if (entry) {
|
|
1066
|
+
const age = Date.now() - entry.lastSuccessAt;
|
|
1067
|
+
if (age > ttlMs) {
|
|
1068
|
+
revalidate(config, counterpartyId).catch(() => {
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
return age > maxStaleMs ? degradeToObserve(entry.config) : entry.config;
|
|
1072
|
+
}
|
|
1073
|
+
await revalidate(config, counterpartyId).catch(() => {
|
|
1074
|
+
});
|
|
1075
|
+
return cache2.get(key)?.config ?? DEFAULT_EDGE_CONFIG;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
// src/platform-signatures.ts
|
|
1079
|
+
var PLATFORM_AGENT_SIGNATURES = [
|
|
1080
|
+
{
|
|
1081
|
+
vendor: "claude",
|
|
1082
|
+
displayName: "Anthropic Claude",
|
|
1083
|
+
uaPatterns: [/claude/i, /anthropic/i],
|
|
1084
|
+
agentCardPatterns: [/anthropic\.com/i, /claude\.ai/i],
|
|
1085
|
+
apiKeyFormatPatterns: [/^sk-ant/i],
|
|
1086
|
+
headerPatterns: [/^anthropic-version/i, /^anthropic-beta/i, /^x-anthropic-/i]
|
|
1087
|
+
},
|
|
1088
|
+
{
|
|
1089
|
+
vendor: "chatgpt",
|
|
1090
|
+
displayName: "OpenAI ChatGPT",
|
|
1091
|
+
uaPatterns: [/chatgpt/i, /openai/i, /gpt-?\d/i],
|
|
1092
|
+
agentCardPatterns: [/openai\.com/i, /chatgpt\.com/i],
|
|
1093
|
+
apiKeyFormatPatterns: [/^sk-proj/i, /^sk-svcacct/i],
|
|
1094
|
+
headerPatterns: [/^openai-organization/i, /^openai-project/i, /^openai-version/i]
|
|
1095
|
+
},
|
|
1096
|
+
{
|
|
1097
|
+
vendor: "gemini",
|
|
1098
|
+
displayName: "Google Gemini",
|
|
1099
|
+
uaPatterns: [/gemini/i, /google-gemini/i, /bard/i],
|
|
1100
|
+
agentCardPatterns: [/gemini\.google\.com/i, /ai\.google\.dev/i],
|
|
1101
|
+
apiKeyFormatPatterns: [/^aiza/i],
|
|
1102
|
+
headerPatterns: [/^x-goog-user-project/i, /^x-goog-api-client/i]
|
|
1103
|
+
},
|
|
1104
|
+
{
|
|
1105
|
+
vendor: "cursor",
|
|
1106
|
+
displayName: "Cursor",
|
|
1107
|
+
uaPatterns: [/cursor/i],
|
|
1108
|
+
agentCardPatterns: [/cursor\.sh/i, /cursor\.com/i]
|
|
1109
|
+
},
|
|
1110
|
+
{
|
|
1111
|
+
vendor: "goose",
|
|
1112
|
+
displayName: "Block Goose",
|
|
1113
|
+
uaPatterns: [/goose/i, /block-goose/i],
|
|
1114
|
+
agentCardPatterns: [/block\.xyz\/goose/i, /goose\.tools/i]
|
|
1115
|
+
},
|
|
1116
|
+
{
|
|
1117
|
+
vendor: "perplexity",
|
|
1118
|
+
displayName: "Perplexity",
|
|
1119
|
+
uaPatterns: [/perplexity/i],
|
|
1120
|
+
agentCardPatterns: [/perplexity\.ai/i],
|
|
1121
|
+
apiKeyFormatPatterns: [/^pplx-/i]
|
|
1122
|
+
}
|
|
1123
|
+
];
|
|
1124
|
+
function matchPlatformSignature(signatures, input) {
|
|
1125
|
+
const ua = input.userAgent ?? "";
|
|
1126
|
+
const cardUrl = input.agentCardUrl ?? "";
|
|
1127
|
+
const apiKeyFormat = input.apiKeyFormat ?? "";
|
|
1128
|
+
const headerLines = [];
|
|
1129
|
+
if (input.platformHeaders) {
|
|
1130
|
+
for (const [name, value] of Object.entries(input.platformHeaders)) {
|
|
1131
|
+
headerLines.push(name, `${name}: ${value}`);
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
for (const sig of signatures) {
|
|
1135
|
+
const uaMatch = sig.uaPatterns?.some((p) => p.test(ua));
|
|
1136
|
+
const cardMatch = sig.agentCardPatterns?.some((p) => p.test(cardUrl));
|
|
1137
|
+
const keyMatch = apiKeyFormat.length > 0 && sig.apiKeyFormatPatterns?.some((p) => p.test(apiKeyFormat));
|
|
1138
|
+
const headerMatch = headerLines.length > 0 && sig.headerPatterns?.some((p) => headerLines.some((line) => p.test(line)));
|
|
1139
|
+
if (uaMatch || cardMatch || keyMatch || headerMatch) {
|
|
1140
|
+
const uaBase = ua.split(/[\s/]/)[0]?.toLowerCase() ?? "";
|
|
1141
|
+
const cardHost = (() => {
|
|
1142
|
+
try {
|
|
1143
|
+
return cardUrl ? new URL(cardUrl).host : "";
|
|
1144
|
+
} catch {
|
|
1145
|
+
return "";
|
|
1146
|
+
}
|
|
1147
|
+
})();
|
|
1148
|
+
const keyBasis = uaBase || cardHost || apiKeyFormat || "unknown";
|
|
1149
|
+
return {
|
|
1150
|
+
vendor: sig.vendor,
|
|
1151
|
+
fingerprintKey: `${sig.vendor}:${keyBasis}`,
|
|
1152
|
+
displayName: sig.displayName
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
return void 0;
|
|
1157
|
+
}
|
|
1158
|
+
function detectPlatformFingerprint(input) {
|
|
1159
|
+
return matchPlatformSignature(PLATFORM_AGENT_SIGNATURES, input);
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
// src/edge-core/classify.ts
|
|
1163
|
+
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;
|
|
1164
|
+
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;
|
|
1165
|
+
var BROWSER_UA_PATTERN = /mozilla\/\d/i;
|
|
1166
|
+
var _patterns = { STATIC_EXTENSIONS, BOT_UA_PATTERN, BROWSER_UA_PATTERN };
|
|
1167
|
+
|
|
1168
|
+
// src/edge-core/events.ts
|
|
1169
|
+
function sampled(rate, random = Math.random) {
|
|
1170
|
+
if (rate <= 0) return false;
|
|
1171
|
+
if (rate >= 1) return true;
|
|
1172
|
+
return random() < rate;
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
// src/adapters/anonymous-beacon.ts
|
|
1176
|
+
function readHeader(raw, name) {
|
|
1177
|
+
const value = raw[name];
|
|
1178
|
+
if (typeof value === "string") return value;
|
|
1179
|
+
if (Array.isArray(value)) return value[0];
|
|
1180
|
+
return void 0;
|
|
1181
|
+
}
|
|
1182
|
+
function classifyPassThroughTraffic(path, rawHeaders) {
|
|
1183
|
+
if (_patterns.STATIC_EXTENSIONS.test(path)) return { kind: "never" };
|
|
1184
|
+
const credentials = extractCredentials(rawHeaders, {});
|
|
1185
|
+
if (credentials.astraId || credentials.apiKey || credentials.jwt) {
|
|
1186
|
+
return { kind: "never" };
|
|
1187
|
+
}
|
|
1188
|
+
const userAgent = readHeader(rawHeaders, "user-agent") ?? "";
|
|
1189
|
+
const { apiKeyFormat, platformHeaders } = sanitizeHeaders(rawHeaders);
|
|
1190
|
+
const fingerprint = detectPlatformFingerprint({ userAgent, apiKeyFormat, platformHeaders });
|
|
1191
|
+
if (fingerprint) {
|
|
1192
|
+
return {
|
|
1193
|
+
kind: "platform",
|
|
1194
|
+
protocol: "platform-ua",
|
|
1195
|
+
evidence: { vendor: fingerprint.vendor, userAgent: userAgent || "(none)" }
|
|
1196
|
+
};
|
|
1197
|
+
}
|
|
1198
|
+
if (!userAgent || _patterns.BOT_UA_PATTERN.test(userAgent) || !_patterns.BROWSER_UA_PATTERN.test(userAgent)) {
|
|
1199
|
+
return {
|
|
1200
|
+
kind: "anonymous-bot",
|
|
1201
|
+
protocol: "ua-heuristic",
|
|
1202
|
+
evidence: { userAgent: userAgent || "(none)" }
|
|
1203
|
+
};
|
|
1204
|
+
}
|
|
1205
|
+
if (!readHeader(rawHeaders, "sec-fetch-mode") && !readHeader(rawHeaders, "accept-language")) {
|
|
1206
|
+
return {
|
|
1207
|
+
kind: "anonymous-bot",
|
|
1208
|
+
protocol: "ua-heuristic",
|
|
1209
|
+
evidence: { userAgent, spoofedBrowser: true }
|
|
1210
|
+
};
|
|
1211
|
+
}
|
|
1212
|
+
return { kind: "never" };
|
|
1213
|
+
}
|
|
1214
|
+
function createAnonymousBeacon(config, options) {
|
|
1215
|
+
const counterpartyId = config.counterpartyId;
|
|
1216
|
+
if (!counterpartyId) return () => {
|
|
1217
|
+
};
|
|
1218
|
+
const random = options?.random ?? Math.random;
|
|
1219
|
+
let edgeConfig;
|
|
1220
|
+
let refreshing = false;
|
|
1221
|
+
const refreshConfig = () => {
|
|
1222
|
+
if (refreshing) return;
|
|
1223
|
+
refreshing = true;
|
|
1224
|
+
getEdgeConfig(config, counterpartyId).then((cfg) => {
|
|
1225
|
+
edgeConfig = cfg;
|
|
1226
|
+
}).catch(() => {
|
|
1227
|
+
}).finally(() => {
|
|
1228
|
+
refreshing = false;
|
|
1229
|
+
});
|
|
1230
|
+
};
|
|
1231
|
+
refreshConfig();
|
|
1232
|
+
return (input) => {
|
|
1233
|
+
try {
|
|
1234
|
+
refreshConfig();
|
|
1235
|
+
const cfg = edgeConfig;
|
|
1236
|
+
if (!cfg) return;
|
|
1237
|
+
const traffic = classifyPassThroughTraffic(input.requestPath, input.rawHeaders);
|
|
1238
|
+
if (traffic.kind === "never") return;
|
|
1239
|
+
if (traffic.kind === "anonymous-bot" && !sampled(cfg.sampling.anonymousBeaconRate, random)) {
|
|
1240
|
+
return;
|
|
1241
|
+
}
|
|
1242
|
+
const userAgent = readHeader(input.rawHeaders, "user-agent");
|
|
1243
|
+
const xff = input.rawHeaders["x-forwarded-for"];
|
|
1244
|
+
const xffStr = Array.isArray(xff) ? xff.join(", ") : xff;
|
|
1245
|
+
const sourceIp = input.sourceIp ?? xffStr?.split(",")[0]?.trim();
|
|
1246
|
+
reportUnregisteredAttempt(config, {
|
|
1247
|
+
...input.counterpartyUrl && { counterpartyUrl: input.counterpartyUrl },
|
|
1248
|
+
counterpartyType: options?.counterpartyType ?? config.counterpartyType ?? "api",
|
|
1249
|
+
...sourceIp && { sourceIp },
|
|
1250
|
+
...userAgent && { userAgent },
|
|
1251
|
+
requestPath: input.requestPath,
|
|
1252
|
+
requestMethod: input.requestMethod,
|
|
1253
|
+
...traffic.protocol && { protocol: traffic.protocol },
|
|
1254
|
+
...traffic.evidence && { evidence: traffic.evidence },
|
|
1255
|
+
observedMetadata: buildSdkObservedMetadata(input.rawHeaders)
|
|
1256
|
+
}).catch(() => {
|
|
1257
|
+
});
|
|
1258
|
+
} catch {
|
|
1259
|
+
}
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
796
1262
|
|
|
797
1263
|
// src/adapters/http-pdlss.ts
|
|
798
1264
|
var HTTP_METHOD_ACTION_TABLE = {
|
|
@@ -956,7 +1422,7 @@ function resolveNextPdlss(request, routeConfig) {
|
|
|
956
1422
|
routeAction: routeConfig?.action
|
|
957
1423
|
});
|
|
958
1424
|
}
|
|
959
|
-
function
|
|
1425
|
+
function generateInterstitialHtml(result, options) {
|
|
960
1426
|
const title = escapeHtml(options.commerceShield?.title || "AstraSync Agent Verification");
|
|
961
1427
|
const message = escapeHtml(
|
|
962
1428
|
options.commerceShield?.message || result.guidance?.message || "This site verifies AI agents before granting access. We noticed you're visiting without AstraSync credentials."
|
|
@@ -1112,13 +1578,31 @@ function generateCommerceShieldHtml(result, options) {
|
|
|
1112
1578
|
}
|
|
1113
1579
|
var DEFAULT_ROUTES_REFRESH_MS = 5 * 60 * 1e3;
|
|
1114
1580
|
function createMiddleware(options) {
|
|
1581
|
+
if ("routes" in options) {
|
|
1582
|
+
console.warn(
|
|
1583
|
+
"[VerificationGateway/Next.js] `routes` is dashboard-managed since v2.9.7; this option is ignored \u2014 configure route policy in your AstraSync dashboard."
|
|
1584
|
+
);
|
|
1585
|
+
}
|
|
1115
1586
|
const {
|
|
1116
1587
|
skipPaths = [],
|
|
1117
|
-
|
|
1588
|
+
showInterstitial,
|
|
1589
|
+
showCommerceShield,
|
|
1118
1590
|
enableRuntimeChallenge = true,
|
|
1119
1591
|
routesRefreshMs = DEFAULT_ROUTES_REFRESH_MS,
|
|
1592
|
+
anonymousBeacon = true,
|
|
1120
1593
|
...config
|
|
1121
1594
|
} = options;
|
|
1595
|
+
const interstitialEnabled = showInterstitial ?? showCommerceShield ?? true;
|
|
1596
|
+
const emitAnonymousBeacon = anonymousBeacon ? createAnonymousBeacon(config, { counterpartyType: config.counterpartyType ?? "website" }) : void 0;
|
|
1597
|
+
const beaconPassThrough = emitAnonymousBeacon ? (request, pathname) => {
|
|
1598
|
+
emitAnonymousBeacon({
|
|
1599
|
+
requestPath: pathname,
|
|
1600
|
+
requestMethod: request.method,
|
|
1601
|
+
counterpartyUrl: config.counterpartyUrl || request.nextUrl.origin,
|
|
1602
|
+
sourceIp: request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || void 0,
|
|
1603
|
+
rawHeaders: Object.fromEntries(request.headers.entries())
|
|
1604
|
+
});
|
|
1605
|
+
} : void 0;
|
|
1122
1606
|
let cachedRoutes = [];
|
|
1123
1607
|
let lastFetchAt = 0;
|
|
1124
1608
|
let refreshing = null;
|
|
@@ -1160,9 +1644,11 @@ function createMiddleware(options) {
|
|
|
1160
1644
|
}
|
|
1161
1645
|
const routeConfig = findRouteConfig(cachedRoutes, pathname, request.method);
|
|
1162
1646
|
if (!routeConfig) {
|
|
1647
|
+
beaconPassThrough?.(request, pathname);
|
|
1163
1648
|
return NextResponse.next();
|
|
1164
1649
|
}
|
|
1165
1650
|
if (routeConfig.minAccessLevel === "none") {
|
|
1651
|
+
beaconPassThrough?.(request, pathname);
|
|
1166
1652
|
return NextResponse.next();
|
|
1167
1653
|
}
|
|
1168
1654
|
const credentials = extractCredentialsFromNextRequest(request);
|
|
@@ -1214,8 +1700,8 @@ function createMiddleware(options) {
|
|
|
1214
1700
|
{ status: 403 }
|
|
1215
1701
|
);
|
|
1216
1702
|
}
|
|
1217
|
-
if (
|
|
1218
|
-
return new NextResponse(
|
|
1703
|
+
if (interstitialEnabled) {
|
|
1704
|
+
return new NextResponse(generateInterstitialHtml(preCheckResult, options), {
|
|
1219
1705
|
status: 200,
|
|
1220
1706
|
headers: {
|
|
1221
1707
|
"Content-Type": "text/html",
|
|
@@ -1243,17 +1729,9 @@ function createMiddleware(options) {
|
|
|
1243
1729
|
host: request.headers.get("host") || void 0,
|
|
1244
1730
|
forwardedFor,
|
|
1245
1731
|
agentCardUrl: request.headers.get("x-astrasync-agent-card") || void 0,
|
|
1246
|
-
// Workstream D — full sanitised header capture
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
Object.fromEntries(request.headers.entries())
|
|
1250
|
-
);
|
|
1251
|
-
return {
|
|
1252
|
-
headers,
|
|
1253
|
-
...apiKeyFormat && { apiKeyFormat },
|
|
1254
|
-
...platformHeaders && { platformHeaders }
|
|
1255
|
-
};
|
|
1256
|
-
})()
|
|
1732
|
+
// Workstream D — full sanitised header capture + CDN-derived
|
|
1733
|
+
// connection block (Web Headers → plain map).
|
|
1734
|
+
observedMetadata: buildSdkObservedMetadata(Object.fromEntries(request.headers.entries()))
|
|
1257
1735
|
}
|
|
1258
1736
|
};
|
|
1259
1737
|
let result = await verify(config, verificationRequest);
|
|
@@ -1269,6 +1747,21 @@ function createMiddleware(options) {
|
|
|
1269
1747
|
const approvalRequired = result.identityVerified && result.policyAllowed && requiresHumanApproval(result);
|
|
1270
1748
|
if (approvalRequired) annotateApprovalRequired(result);
|
|
1271
1749
|
if (!result.identityVerified || !result.policyAllowed || approvalRequired) {
|
|
1750
|
+
if (result.misconfigured && pathname.startsWith("/api/")) {
|
|
1751
|
+
return NextResponse.json(
|
|
1752
|
+
{
|
|
1753
|
+
success: false,
|
|
1754
|
+
error: {
|
|
1755
|
+
code: "MERCHANT_VERIFICATION_MISCONFIGURED",
|
|
1756
|
+
message: result.denialReasons?.[0] || "This site's AstraSync integration is misconfigured",
|
|
1757
|
+
guidance: result.guidance,
|
|
1758
|
+
failures: result.failures,
|
|
1759
|
+
correlationId: result.correlationId
|
|
1760
|
+
}
|
|
1761
|
+
},
|
|
1762
|
+
{ status: 503 }
|
|
1763
|
+
);
|
|
1764
|
+
}
|
|
1272
1765
|
if (pathname.startsWith("/api/")) {
|
|
1273
1766
|
return NextResponse.json(
|
|
1274
1767
|
{
|
|
@@ -1293,8 +1786,8 @@ function createMiddleware(options) {
|
|
|
1293
1786
|
}
|
|
1294
1787
|
);
|
|
1295
1788
|
}
|
|
1296
|
-
if (
|
|
1297
|
-
return new NextResponse(
|
|
1789
|
+
if (interstitialEnabled) {
|
|
1790
|
+
return new NextResponse(generateInterstitialHtml(result, options), {
|
|
1298
1791
|
status: 200,
|
|
1299
1792
|
headers: {
|
|
1300
1793
|
"Content-Type": "text/html",
|