@astrasyncai/verification-gateway 5.14.0 → 5.16.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.
Files changed (100) hide show
  1. package/README.md +36 -18
  2. package/dist/adapters/express.d.mts +1 -1
  3. package/dist/adapters/express.d.ts +1 -1
  4. package/dist/adapters/express.js +492 -240
  5. package/dist/adapters/express.js.map +1 -1
  6. package/dist/adapters/express.mjs +492 -240
  7. package/dist/adapters/express.mjs.map +1 -1
  8. package/dist/adapters/mcp.d.mts +1 -1
  9. package/dist/adapters/mcp.d.ts +1 -1
  10. package/dist/adapters/mcp.js +23 -6
  11. package/dist/adapters/mcp.js.map +1 -1
  12. package/dist/adapters/mcp.mjs +23 -6
  13. package/dist/adapters/mcp.mjs.map +1 -1
  14. package/dist/adapters/nextjs.d.mts +1 -1
  15. package/dist/adapters/nextjs.d.ts +1 -1
  16. package/dist/adapters/nextjs.js +475 -239
  17. package/dist/adapters/nextjs.js.map +1 -1
  18. package/dist/adapters/nextjs.mjs +475 -239
  19. package/dist/adapters/nextjs.mjs.map +1 -1
  20. package/dist/adapters/sdk.d.mts +18 -10
  21. package/dist/adapters/sdk.d.ts +18 -10
  22. package/dist/adapters/sdk.js +83 -23
  23. package/dist/adapters/sdk.js.map +1 -1
  24. package/dist/adapters/sdk.mjs +83 -23
  25. package/dist/adapters/sdk.mjs.map +1 -1
  26. package/dist/agent/index.d.mts +69 -54
  27. package/dist/agent/index.d.ts +69 -54
  28. package/dist/agent/index.js +24 -14
  29. package/dist/agent/index.js.map +1 -1
  30. package/dist/agent/index.mjs +24 -14
  31. package/dist/agent/index.mjs.map +1 -1
  32. package/dist/bin/astrasync-claude-hook.js +58 -11
  33. package/dist/bin/astrasync-codex-hook.js +58 -11
  34. package/dist/bin/astrasync-guard.js +177 -11
  35. package/dist/bin/astrasync.js +433 -38
  36. package/dist/browser/background.js +53 -6
  37. package/dist/browser/background.js.map +1 -1
  38. package/dist/browser/background.mjs +63 -6
  39. package/dist/browser/background.mjs.map +1 -1
  40. package/dist/cli/index.js +1 -1
  41. package/dist/cli/index.js.map +1 -1
  42. package/dist/cli/index.mjs +1 -1
  43. package/dist/cli/index.mjs.map +1 -1
  44. package/dist/codex/index.js +58 -11
  45. package/dist/codex/index.js.map +1 -1
  46. package/dist/codex/index.mjs +68 -11
  47. package/dist/codex/index.mjs.map +1 -1
  48. package/dist/cursor/extension.js +58 -11
  49. package/dist/cursor/extension.js.map +1 -1
  50. package/dist/cursor/extension.mjs +68 -11
  51. package/dist/cursor/extension.mjs.map +1 -1
  52. package/dist/edge-config.d.mts +31 -2
  53. package/dist/edge-config.d.ts +31 -2
  54. package/dist/edge-config.js +35 -5
  55. package/dist/edge-config.js.map +1 -1
  56. package/dist/edge-config.mjs +32 -4
  57. package/dist/edge-config.mjs.map +1 -1
  58. package/dist/edge-core/index.d.mts +11 -2
  59. package/dist/edge-core/index.d.ts +11 -2
  60. package/dist/edge-core/index.js +97 -11
  61. package/dist/edge-core/index.js.map +1 -1
  62. package/dist/edge-core/index.mjs +95 -11
  63. package/dist/edge-core/index.mjs.map +1 -1
  64. package/dist/gateway/gateway.js +53 -6
  65. package/dist/gateway/gateway.js.map +1 -1
  66. package/dist/gateway/gateway.mjs +63 -6
  67. package/dist/gateway/gateway.mjs.map +1 -1
  68. package/dist/git-trigger/git-hooks.d.mts +1 -1
  69. package/dist/git-trigger/git-hooks.d.ts +1 -1
  70. package/dist/index.d.mts +1407 -88
  71. package/dist/index.d.ts +1407 -88
  72. package/dist/index.js +5148 -2616
  73. package/dist/index.js.map +1 -1
  74. package/dist/index.mjs +5094 -2618
  75. package/dist/index.mjs.map +1 -1
  76. package/dist/registration/index.d.mts +28 -2
  77. package/dist/registration/index.d.ts +28 -2
  78. package/dist/registration/index.js +18 -3
  79. package/dist/registration/index.js.map +1 -1
  80. package/dist/registration/index.mjs +15 -2
  81. package/dist/registration/index.mjs.map +1 -1
  82. package/dist/transport/index.d.mts +7 -5
  83. package/dist/transport/index.d.ts +7 -5
  84. package/dist/transport/index.js +61 -5
  85. package/dist/transport/index.js.map +1 -1
  86. package/dist/transport/index.mjs +67 -11
  87. package/dist/transport/index.mjs.map +1 -1
  88. package/dist/{types-BCkwBA84.d.mts → types-Blz520bR.d.mts} +116 -12
  89. package/dist/{types-BCkwBA84.d.ts → types-Blz520bR.d.ts} +116 -12
  90. package/dist/{types-C_b4QTyO.d.ts → types-Bm1QvGqI.d.ts} +114 -10
  91. package/dist/{types-DlshIIR-.d.mts → types-DBe4hgfT.d.mts} +114 -10
  92. package/dist/ui/index.d.mts +1 -1
  93. package/dist/ui/index.d.ts +1 -1
  94. package/dist/verify.d.mts +1 -1
  95. package/dist/verify.d.ts +1 -1
  96. package/dist/verify.js +23 -6
  97. package/dist/verify.js.map +1 -1
  98. package/dist/verify.mjs +23 -6
  99. package/dist/verify.mjs.map +1 -1
  100. package/package.json +1 -1
@@ -1,5 +1,430 @@
1
+ // src/metadata-capture.ts
2
+ var CAPTURE_SCHEMA_VERSION = 1;
3
+ var SECRET_HEADER_NAMES = /* @__PURE__ */ new Set([
4
+ "cookie",
5
+ "set-cookie",
6
+ "x-csrf-token",
7
+ "proxy-authorization",
8
+ "x-astrasync-signature",
9
+ "x-astrasync-secret",
10
+ "x-hub-signature",
11
+ "x-hub-signature-256",
12
+ // Short-lived verify dedupe token — must never persist in stored metadata.
13
+ "x-astra-verified-hop"
14
+ ]);
15
+ var SECRET_HEADER_PREFIXES = ["x-astrasync-", "kya-", "kya_"];
16
+ var CREDENTIAL_HEADER_NAMES = /* @__PURE__ */ new Set([
17
+ "authorization",
18
+ "x-api-key",
19
+ "x-goog-api-key",
20
+ "api-key"
21
+ ]);
22
+ function isSignatureHeader(name) {
23
+ return name === "signature" || name.endsWith("-signature") || name.endsWith("-signature-256");
24
+ }
25
+ var PLATFORM_HEADER_NAMES = /* @__PURE__ */ new Set([
26
+ "openai-organization",
27
+ "openai-project",
28
+ "openai-version",
29
+ "openai-processing-ms",
30
+ "anthropic-version",
31
+ "anthropic-beta",
32
+ "x-goog-user-project",
33
+ "x-goog-api-client",
34
+ "user-agent",
35
+ "x-agent-card-url",
36
+ "x-agent-provider"
37
+ ]);
38
+ var PLATFORM_HEADER_PREFIXES = [
39
+ "x-goog-",
40
+ "x-stainless-",
41
+ "x-anthropic-",
42
+ "x-openai-",
43
+ "sec-ch-"
44
+ ];
45
+ var KEY_FORMAT_PREFIXES = [
46
+ "sk-ant-api03",
47
+ "sk-ant-api",
48
+ "sk-ant",
49
+ "sk-proj",
50
+ "sk-svcacct",
51
+ "sk-or-v1",
52
+ "sk-or",
53
+ "sk-lf",
54
+ "sk-",
55
+ "gsk_",
56
+ "aiza",
57
+ "ya29",
58
+ "ghp_",
59
+ "gho_",
60
+ "github_pat_",
61
+ "xai-",
62
+ "pplx-",
63
+ "r8_",
64
+ "hf_",
65
+ "astra-",
66
+ "astrae-"
67
+ ];
68
+ var MAX_HEADERS = 64;
69
+ var MAX_HEADER_VALUE_BYTES = 1024;
70
+ var MAX_TOTAL_BYTES = 16 * 1024;
71
+ function byteLength(s) {
72
+ let bytes = 0;
73
+ for (let i = 0; i < s.length; i++) {
74
+ const code = s.charCodeAt(i);
75
+ if (code < 128) bytes += 1;
76
+ else if (code < 2048) bytes += 2;
77
+ else if (code >= 55296 && code <= 56319) {
78
+ bytes += 4;
79
+ i++;
80
+ } else bytes += 3;
81
+ }
82
+ return bytes;
83
+ }
84
+ function extractApiKeyFormat(rawValue) {
85
+ if (!rawValue) return void 0;
86
+ let value = rawValue.trim();
87
+ const spaceIdx = value.indexOf(" ");
88
+ if (spaceIdx > 0) {
89
+ const scheme = value.slice(0, spaceIdx).toLowerCase();
90
+ if (scheme === "bearer" || scheme === "basic" || scheme === "token") {
91
+ value = value.slice(spaceIdx + 1).trim();
92
+ }
93
+ }
94
+ const lower = value.toLowerCase();
95
+ for (const prefix of KEY_FORMAT_PREFIXES) {
96
+ if (lower.startsWith(prefix)) {
97
+ return prefix;
98
+ }
99
+ }
100
+ return void 0;
101
+ }
102
+ function isSecretHeader(name) {
103
+ if (SECRET_HEADER_NAMES.has(name)) return true;
104
+ if (SECRET_HEADER_PREFIXES.some((p) => name.startsWith(p))) return true;
105
+ if (isSignatureHeader(name)) return true;
106
+ return false;
107
+ }
108
+ function isPlatformHeader(name) {
109
+ if (PLATFORM_HEADER_NAMES.has(name)) return true;
110
+ return PLATFORM_HEADER_PREFIXES.some((p) => name.startsWith(p));
111
+ }
112
+ function sanitizeHeaders(raw) {
113
+ const headers = {};
114
+ const platformHeaders = {};
115
+ let apiKeyFormat;
116
+ let cookieCount;
117
+ let totalBytes = 0;
118
+ let count = 0;
119
+ if (!raw) return { headers };
120
+ for (const rawName of Object.keys(raw)) {
121
+ if (count >= MAX_HEADERS) break;
122
+ const name = rawName.toLowerCase();
123
+ const rawValue = raw[rawName];
124
+ if (rawValue === void 0) continue;
125
+ const joined = Array.isArray(rawValue) ? rawValue.join(", ") : String(rawValue);
126
+ if (CREDENTIAL_HEADER_NAMES.has(name)) {
127
+ const fmt = extractApiKeyFormat(joined);
128
+ if (fmt && !apiKeyFormat) apiKeyFormat = fmt;
129
+ continue;
130
+ }
131
+ if (name === "cookie") {
132
+ cookieCount = joined.split(";").filter((s) => s.trim().length > 0).length;
133
+ }
134
+ if (isSecretHeader(name)) continue;
135
+ let value = joined;
136
+ if (byteLength(value) > MAX_HEADER_VALUE_BYTES) {
137
+ value = value.slice(0, MAX_HEADER_VALUE_BYTES);
138
+ }
139
+ const entryBytes = byteLength(name) + byteLength(value);
140
+ if (totalBytes + entryBytes > MAX_TOTAL_BYTES) continue;
141
+ totalBytes += entryBytes;
142
+ count++;
143
+ headers[name] = value;
144
+ if (isPlatformHeader(name)) platformHeaders[name] = value;
145
+ }
146
+ const result = { headers };
147
+ if (apiKeyFormat) result.apiKeyFormat = apiKeyFormat;
148
+ if (Object.keys(platformHeaders).length > 0) result.platformHeaders = platformHeaders;
149
+ if (cookieCount !== void 0) result.cookieCount = cookieCount;
150
+ return result;
151
+ }
152
+ var CONNECTION_HEADER_SOURCES = [
153
+ ["ip", ["cf-connecting-ip", "fastly-client-ip", "true-client-ip", "x-real-ip"]],
154
+ [
155
+ "country",
156
+ ["cf-ipcountry", "x-vercel-ip-country", "cloudfront-viewer-country", "fastly-geo-country"]
157
+ ],
158
+ ["region", ["x-vercel-ip-country-region", "cloudfront-viewer-country-region"]],
159
+ ["countryRegionName", ["cloudfront-viewer-country-region-name"]],
160
+ ["city", ["x-vercel-ip-city", "cloudfront-viewer-city"]],
161
+ ["latitude", ["x-vercel-ip-latitude", "cloudfront-viewer-latitude"]],
162
+ ["longitude", ["x-vercel-ip-longitude", "cloudfront-viewer-longitude"]],
163
+ ["postalCode", ["x-vercel-ip-postal-code", "cloudfront-viewer-postal-code"]],
164
+ ["asn", ["cloudfront-viewer-asn"]],
165
+ ["tlsVersion", ["cloudfront-viewer-tls"]],
166
+ ["httpVersion", ["cloudfront-viewer-http-version"]],
167
+ ["timeZone", ["x-vercel-ip-timezone", "cloudfront-viewer-time-zone"]],
168
+ ["metroCode", ["cloudfront-viewer-metro-code"]],
169
+ // TLS fingerprints can only be computed where TLS terminates. When a CDN
170
+ // terminates TLS in front of the merchant, its fingerprint headers ARE the
171
+ // connection-layer truth — normalising them here is the permanent design.
172
+ // CloudFront delivers JA3/JA4 only via an origin request policy and only
173
+ // for HTTPS viewer connections.
174
+ ["tlsFingerprint", ["cf-ja3-hash", "cloudfront-viewer-ja3-fingerprint"]],
175
+ ["ja4", ["cf-ja4", "cloudfront-viewer-ja4-fingerprint"]],
176
+ // Header-structure fingerprint: browser/SDK-distinctive header ordering.
177
+ ["headerOrder", ["cloudfront-viewer-header-order"]],
178
+ ["headerCount", ["cloudfront-viewer-header-count"]]
179
+ ];
180
+ var MAX_CONNECTION_VALUE_CHARS = 256;
181
+ function deriveConnectionFromHeaders(headers) {
182
+ if (!headers) return void 0;
183
+ const lower = {};
184
+ for (const name of Object.keys(headers)) {
185
+ const value = headers[name];
186
+ if (typeof value === "string" && value.length > 0) lower[name.toLowerCase()] = value;
187
+ }
188
+ const connection = {};
189
+ for (const [key, sources] of CONNECTION_HEADER_SOURCES) {
190
+ for (const source of sources) {
191
+ const value = lower[source]?.trim();
192
+ if (value) {
193
+ connection[key] = value.slice(0, MAX_CONNECTION_VALUE_CHARS);
194
+ break;
195
+ }
196
+ }
197
+ }
198
+ {
199
+ const addr = lower["cloudfront-viewer-address"]?.trim();
200
+ const portMatch = addr?.match(/:(\d+)$/);
201
+ if (portMatch) connection.sourcePort = portMatch[1];
202
+ if (!connection.ip && addr) connection.ip = addr.replace(/:\d+$/, "");
203
+ }
204
+ if (!connection.ip) {
205
+ const xff = lower["x-forwarded-for"];
206
+ const client = xff?.split(",")[0]?.trim();
207
+ if (client) connection.ip = client;
208
+ }
209
+ if (!connection.tlsCipher) {
210
+ const cipher = connection.tlsVersion?.split(":")[1];
211
+ if (cipher) connection.tlsCipher = cipher;
212
+ }
213
+ return Object.keys(connection).length > 0 ? connection : void 0;
214
+ }
215
+ function buildSdkObservedMetadata(rawHeaders) {
216
+ const { headers, apiKeyFormat, platformHeaders, cookieCount } = sanitizeHeaders(rawHeaders);
217
+ const flat = {};
218
+ if (rawHeaders) {
219
+ for (const name of Object.keys(rawHeaders)) {
220
+ const value = rawHeaders[name];
221
+ if (value === void 0) continue;
222
+ flat[name.toLowerCase()] = Array.isArray(value) ? value.join(", ") : String(value);
223
+ }
224
+ }
225
+ const connection = deriveConnectionFromHeaders(flat);
226
+ return {
227
+ headers,
228
+ ...apiKeyFormat && { apiKeyFormat },
229
+ ...platformHeaders && { platformHeaders },
230
+ ...connection && { connection },
231
+ ...cookieCount !== void 0 && { cookieCount },
232
+ schemaVersion: CAPTURE_SCHEMA_VERSION
233
+ };
234
+ }
235
+
236
+ // src/edge-core/request.ts
237
+ function headerValue(req, name) {
238
+ return req.headers[name.toLowerCase()];
239
+ }
240
+ function requestUrl(req, host) {
241
+ const qs = req.querystring ? `?${req.querystring}` : "";
242
+ return `https://${host}${req.uri}${qs}`;
243
+ }
244
+
245
+ // src/edge-core/detect.ts
246
+ var SD_JWT_PATTERN = /^eyJ[\w-]+\.[\w-]*\.[\w-]+~/;
247
+ function decodeBase64Url(seg) {
248
+ const b64 = seg.replace(/-/g, "+").replace(/_/g, "/");
249
+ const padded = b64 + "=".repeat((4 - b64.length % 4) % 4);
250
+ const binary = atob(padded);
251
+ const bytes = new Uint8Array(binary.length);
252
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
253
+ return new TextDecoder().decode(bytes);
254
+ }
255
+ function sniffJwtHeader(compact) {
256
+ try {
257
+ const seg = compact.split(".")[0];
258
+ return JSON.parse(decodeBase64Url(seg));
259
+ } catch {
260
+ return void 0;
261
+ }
262
+ }
263
+ function parseJsonBody(bodyText) {
264
+ if (!bodyText) return void 0;
265
+ try {
266
+ const parsed = JSON.parse(bodyText);
267
+ return typeof parsed === "object" && parsed !== null ? parsed : void 0;
268
+ } catch {
269
+ return void 0;
270
+ }
271
+ }
272
+ function detectCommerce(req, host) {
273
+ const url = requestUrl(req, host);
274
+ const method = req.method;
275
+ const headers = req.headers;
276
+ const jsonBody = parseJsonBody(req.bodyText);
277
+ const truncated = req.bodyTruncated ? { bodyTruncated: true } : {};
278
+ const authHeader = headerValue(req, "authorization");
279
+ const bearer = authHeader?.replace(/^Bearer\s+/i, "");
280
+ const mandateHeader = headerValue(req, "x-payment-mandate") ?? headerValue(req, "payment-mandate");
281
+ const sdJwtCandidate = [mandateHeader, bearer].find((v) => v && SD_JWT_PATTERN.test(v));
282
+ if (sdJwtCandidate) {
283
+ const typ = sniffJwtHeader(sdJwtCandidate)?.typ;
284
+ return {
285
+ protocol: "vi-sd-jwt",
286
+ artifacts: { viSdJwt: sdJwtCandidate },
287
+ evidence: { typ, ...truncated }
288
+ };
289
+ }
290
+ const signature = headerValue(req, "signature");
291
+ const signatureInput = headerValue(req, "signature-input");
292
+ if (signature && signatureInput) {
293
+ const signatureAgent = headerValue(req, "signature-agent");
294
+ const registryHint = headerValue(req, "x-agent-registry");
295
+ const lowerInput = signatureInput.toLowerCase();
296
+ const protocol = signatureAgent ? "rfc9421-web-bot-auth" : registryHint?.toLowerCase().includes("visa") || lowerInput.includes("visa") ? "rfc9421-tap" : registryHint?.toLowerCase().includes("mastercard") || lowerInput.includes("mastercard") ? "rfc9421-agent-pay" : "rfc9421";
297
+ return {
298
+ protocol,
299
+ artifacts: {
300
+ rfc9421: { headers, method, url, ...req.bodyText && { body: req.bodyText } }
301
+ },
302
+ evidence: {
303
+ ...signatureAgent && { signatureAgent },
304
+ ...registryHint && { registryHint },
305
+ keyid: signatureInput.match(/keyid="([^"]+)"/)?.[1],
306
+ ...truncated
307
+ }
308
+ };
309
+ }
310
+ const acpTimestamp = headerValue(req, "timestamp");
311
+ if (signature && !signatureInput && (acpTimestamp || headerValue(req, "idempotency-key"))) {
312
+ return {
313
+ protocol: "acp",
314
+ artifacts: {
315
+ acpRequest: {
316
+ method,
317
+ url,
318
+ headers,
319
+ ...req.bodyText && { rawBody: req.bodyText },
320
+ ...jsonBody && { body: jsonBody }
321
+ }
322
+ },
323
+ evidence: { hasTimestamp: Boolean(acpTimestamp), ...truncated }
324
+ };
325
+ }
326
+ if (/\/checkout[-_]sessions(\/|$)/.test(req.uri) && headerValue(req, "ucp-agent")) {
327
+ return {
328
+ protocol: "ucp",
329
+ artifacts: { ucpRequest: { method, url, ...jsonBody && { body: jsonBody } } },
330
+ evidence: { path: req.uri, ...truncated }
331
+ };
332
+ }
333
+ if (jsonBody) {
334
+ const intent = jsonBody.intent_mandate ?? jsonBody.intentMandate;
335
+ const cart = jsonBody.cart_mandate ?? jsonBody.cartMandate;
336
+ const payment = jsonBody.payment_mandate ?? jsonBody.paymentMandate;
337
+ if (typeof intent === "string" || typeof cart === "string" || typeof payment === "string") {
338
+ return {
339
+ protocol: "ap2",
340
+ artifacts: {
341
+ ap2Mandates: {
342
+ ...typeof intent === "string" && { intent },
343
+ ...typeof cart === "string" && { cart },
344
+ ...typeof payment === "string" && { payment }
345
+ }
346
+ },
347
+ evidence: {
348
+ mandatesPresent: [
349
+ typeof intent === "string" && "intent",
350
+ typeof cart === "string" && "cart",
351
+ typeof payment === "string" && "payment"
352
+ ].filter(Boolean),
353
+ ...truncated
354
+ }
355
+ };
356
+ }
357
+ }
358
+ if (authHeader && /^Payment\s+/i.test(authHeader) || headerValue(req, "payment-authorization")) {
359
+ return {
360
+ protocol: "mpp",
361
+ artifacts: {
362
+ mppRequest: {
363
+ method,
364
+ url,
365
+ headers,
366
+ ...req.bodyText && { rawBody: req.bodyText },
367
+ ...jsonBody && { body: jsonBody }
368
+ }
369
+ },
370
+ evidence: truncated
371
+ };
372
+ }
373
+ if (headerValue(req, "payment-signature") || headerValue(req, "x-payment")) {
374
+ return {
375
+ protocol: "x402",
376
+ artifacts: {
377
+ x402Request: { method, url, headers, ...jsonBody && { body: jsonBody } }
378
+ },
379
+ evidence: truncated
380
+ };
381
+ }
382
+ return void 0;
383
+ }
384
+
385
+ // src/adapters/commerce-detect.ts
386
+ var MAX_BODY_BYTES = 64 * 1024;
387
+ function detect(req, host) {
388
+ try {
389
+ return detectCommerce(req, host)?.artifacts;
390
+ } catch {
391
+ return void 0;
392
+ }
393
+ }
394
+ async function detectFetchCommerce(request) {
395
+ const url = new URL(request.url);
396
+ const headers = {};
397
+ request.headers.forEach((v, k) => headers[k.toLowerCase()] = v);
398
+ let bodyText;
399
+ const length = Number(headers["content-length"] ?? 0);
400
+ if (request.method !== "GET" && request.method !== "HEAD" && length <= MAX_BODY_BYTES) {
401
+ bodyText = await request.clone().text().catch(() => void 0);
402
+ }
403
+ return detect(
404
+ {
405
+ method: request.method,
406
+ uri: url.pathname,
407
+ querystring: url.search.replace(/^\?/, ""),
408
+ headers,
409
+ ...bodyText ? { bodyText } : {}
410
+ },
411
+ headers["x-forwarded-host"] ?? url.host
412
+ );
413
+ }
414
+
415
+ // src/types.ts
416
+ function routePolicyOf(route) {
417
+ if (!route) return void 0;
418
+ const owners = route.allowedOwners?.filter(Boolean) ?? [];
419
+ const policy = {
420
+ ...owners.length > 0 && { allowedOwners: owners },
421
+ ...route.requiresRuntimeChallenge === true && { requiresRuntimeChallenge: true }
422
+ };
423
+ return Object.keys(policy).length > 0 ? policy : void 0;
424
+ }
425
+
1
426
  // src/version.ts
2
- var SDK_VERSION = "5.14.0";
427
+ var SDK_VERSION = "5.16.0";
3
428
 
4
429
  // src/http.ts
5
430
  var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
@@ -247,6 +672,8 @@ function getCacheKey(request, counterpartyId) {
247
672
  request.durationRequired ?? "",
248
673
  request.invocationProtocol || "",
249
674
  request.enableRuntimeChallenge ? "1" : "0",
675
+ // 5.16.0: route policy changes the verdict (owners / challenge).
676
+ request.routePolicy ? JSON.stringify(request.routePolicy) : "",
250
677
  // callerMetadata fields contribute to risk model; include the ones
251
678
  // backend reads. sourceIp/userAgent/forwardedFor change per-request
252
679
  // so their inclusion effectively forces a re-check for any varying
@@ -453,6 +880,7 @@ async function callVerifyAccessAPI(config, request) {
453
880
  if (requestData.subAgentDepth !== void 0) body.subAgentDepth = requestData.subAgentDepth;
454
881
  if (requestData.enableRuntimeChallenge)
455
882
  body.enableRuntimeChallenge = requestData.enableRuntimeChallenge;
883
+ if (requestData.routePolicy) body.routePolicy = requestData.routePolicy;
456
884
  if (requestData.createSession) body.createSession = requestData.createSession;
457
885
  if (requestData.durationRequired) body.durationRequired = requestData.durationRequired;
458
886
  if (requestData.counterpartyType) body.counterpartyType = requestData.counterpartyType;
@@ -463,6 +891,7 @@ async function callVerifyAccessAPI(config, request) {
463
891
  if (requestData.invocationProtocol) body.invocationProtocol = requestData.invocationProtocol;
464
892
  if (requestData.commercePhase) body.commercePhase = requestData.commercePhase;
465
893
  if (requestData.checkoutSessionId) body.checkoutSessionId = requestData.checkoutSessionId;
894
+ if (requestData.checkoutJwt) body.checkoutJwt = requestData.checkoutJwt;
466
895
  if (requestData.checkoutItems) body.checkoutItems = requestData.checkoutItems;
467
896
  if (requestData.buyerEmail) body.buyerEmail = requestData.buyerEmail;
468
897
  if (requestData.commerceArtifacts) body.commerceArtifacts = requestData.commerceArtifacts;
@@ -613,7 +1042,8 @@ async function verify(config, request, options) {
613
1042
  "[VerificationGateway] minTrustScore / minTrustScoreForFull are deprecated in v2.3.0 and have no effect. Server is now the single source of truth for access decisions (verified ID + runtime challenge + PDLSS + trust score). To gate access to an endpoint, configure the endpoint's trust_score_requirement server-side."
614
1043
  );
615
1044
  }
616
- if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache) {
1045
+ const uncacheable = Boolean(request.commerceArtifacts);
1046
+ if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache && !uncacheable) {
617
1047
  const cached = getCachedResult(request, mergedConfig.counterpartyId);
618
1048
  if (cached) {
619
1049
  if (mergedConfig.debug) {
@@ -694,7 +1124,10 @@ async function verify(config, request, options) {
694
1124
  // MERCHANT caller only (bridge callers strip it before the agent plane).
695
1125
  settlementToken: apiResponse.settlementToken,
696
1126
  // 5.6.0: fulfilment contact — merchant-only lane, same strip rule.
697
- fulfilment: apiResponse.fulfilment
1127
+ fulfilment: apiResponse.fulfilment,
1128
+ mandates: apiResponse.mandates,
1129
+ kyaToken: apiResponse.kyaToken,
1130
+ aep: apiResponse.aep
698
1131
  };
699
1132
  return result2;
700
1133
  }
@@ -704,7 +1137,10 @@ async function verify(config, request, options) {
704
1137
  trustScore: apiResponse.agent.trustScore,
705
1138
  trustLevel: getTrustLevel(apiResponse.agent.trustScore),
706
1139
  blockchainAnchored: apiResponse.agent.blockchainStatus === "anchored",
707
- status: apiResponse.agent.agentStatus
1140
+ status: apiResponse.agent.agentStatus,
1141
+ ...apiResponse.agent.protocolIdentities && {
1142
+ protocolIdentities: apiResponse.agent.protocolIdentities
1143
+ }
708
1144
  } : void 0;
709
1145
  const developer = apiResponse.developer ? {
710
1146
  astradId: apiResponse.developer.kyaOwnerId,
@@ -755,7 +1191,11 @@ async function verify(config, request, options) {
755
1191
  settlementOutcome: apiResponse.settlementOutcome,
756
1192
  settlementToken: apiResponse.settlementToken,
757
1193
  // 5.6.0: fulfilment contact — merchant-only lane, same strip rule.
758
- fulfilment: apiResponse.fulfilment
1194
+ fulfilment: apiResponse.fulfilment,
1195
+ // 5.15.0: merchant-lane AP2 mandates + KYAPay identity token.
1196
+ mandates: apiResponse.mandates,
1197
+ kyaToken: apiResponse.kyaToken,
1198
+ aep: apiResponse.aep
759
1199
  };
760
1200
  if (result.recommendation === "deny") {
761
1201
  result.policyAllowed = false;
@@ -775,7 +1215,9 @@ async function verify(config, request, options) {
775
1215
  }
776
1216
  const escalates = result.requiresStepUp === true || result.requiresApproval === true || result.recommendation === "step_up_required";
777
1217
  if (mergedConfig.cacheTtl !== 0 && result.recommendation !== "deny" && !escalates) {
778
- cacheResult(request, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);
1218
+ if (!uncacheable) {
1219
+ cacheResult(request, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);
1220
+ }
779
1221
  }
780
1222
  return result;
781
1223
  }
@@ -929,239 +1371,30 @@ function performCounterpartyPreCheck(routeConfig, astraCreds, purpose) {
929
1371
  return failures;
930
1372
  }
931
1373
 
932
- // src/metadata-capture.ts
933
- var CAPTURE_SCHEMA_VERSION = 1;
934
- var SECRET_HEADER_NAMES = /* @__PURE__ */ new Set([
935
- "cookie",
936
- "set-cookie",
937
- "x-csrf-token",
938
- "proxy-authorization",
939
- "x-astrasync-signature",
940
- "x-astrasync-secret",
941
- "x-hub-signature",
942
- "x-hub-signature-256",
943
- // Short-lived verify dedupe token — must never persist in stored metadata.
944
- "x-astra-verified-hop"
945
- ]);
946
- var SECRET_HEADER_PREFIXES = ["x-astrasync-", "kya-", "kya_"];
947
- var CREDENTIAL_HEADER_NAMES = /* @__PURE__ */ new Set([
948
- "authorization",
949
- "x-api-key",
950
- "x-goog-api-key",
951
- "api-key"
952
- ]);
953
- function isSignatureHeader(name) {
954
- return name === "signature" || name.endsWith("-signature") || name.endsWith("-signature-256");
955
- }
956
- var PLATFORM_HEADER_NAMES = /* @__PURE__ */ new Set([
957
- "openai-organization",
958
- "openai-project",
959
- "openai-version",
960
- "openai-processing-ms",
961
- "anthropic-version",
962
- "anthropic-beta",
963
- "x-goog-user-project",
964
- "x-goog-api-client",
965
- "user-agent",
966
- "x-agent-card-url",
967
- "x-agent-provider"
968
- ]);
969
- var PLATFORM_HEADER_PREFIXES = [
970
- "x-goog-",
971
- "x-stainless-",
972
- "x-anthropic-",
973
- "x-openai-",
974
- "sec-ch-"
975
- ];
976
- var KEY_FORMAT_PREFIXES = [
977
- "sk-ant-api03",
978
- "sk-ant-api",
979
- "sk-ant",
980
- "sk-proj",
981
- "sk-svcacct",
982
- "sk-or-v1",
983
- "sk-or",
984
- "sk-lf",
985
- "sk-",
986
- "gsk_",
987
- "aiza",
988
- "ya29",
989
- "ghp_",
990
- "gho_",
991
- "github_pat_",
992
- "xai-",
993
- "pplx-",
994
- "r8_",
995
- "hf_",
996
- "astra-",
997
- "astrae-"
998
- ];
999
- var MAX_HEADERS = 64;
1000
- var MAX_HEADER_VALUE_BYTES = 1024;
1001
- var MAX_TOTAL_BYTES = 16 * 1024;
1002
- function byteLength(s) {
1003
- let bytes = 0;
1004
- for (let i = 0; i < s.length; i++) {
1005
- const code = s.charCodeAt(i);
1006
- if (code < 128) bytes += 1;
1007
- else if (code < 2048) bytes += 2;
1008
- else if (code >= 55296 && code <= 56319) {
1009
- bytes += 4;
1010
- i++;
1011
- } else bytes += 3;
1012
- }
1013
- return bytes;
1014
- }
1015
- function extractApiKeyFormat(rawValue) {
1016
- if (!rawValue) return void 0;
1017
- let value = rawValue.trim();
1018
- const spaceIdx = value.indexOf(" ");
1019
- if (spaceIdx > 0) {
1020
- const scheme = value.slice(0, spaceIdx).toLowerCase();
1021
- if (scheme === "bearer" || scheme === "basic" || scheme === "token") {
1022
- value = value.slice(spaceIdx + 1).trim();
1023
- }
1024
- }
1025
- const lower = value.toLowerCase();
1026
- for (const prefix of KEY_FORMAT_PREFIXES) {
1027
- if (lower.startsWith(prefix)) {
1028
- return prefix;
1029
- }
1030
- }
1031
- return void 0;
1032
- }
1033
- function isSecretHeader(name) {
1034
- if (SECRET_HEADER_NAMES.has(name)) return true;
1035
- if (SECRET_HEADER_PREFIXES.some((p) => name.startsWith(p))) return true;
1036
- if (isSignatureHeader(name)) return true;
1037
- return false;
1038
- }
1039
- function isPlatformHeader(name) {
1040
- if (PLATFORM_HEADER_NAMES.has(name)) return true;
1041
- return PLATFORM_HEADER_PREFIXES.some((p) => name.startsWith(p));
1042
- }
1043
- function sanitizeHeaders(raw) {
1044
- const headers = {};
1045
- const platformHeaders = {};
1046
- let apiKeyFormat;
1047
- let cookieCount;
1048
- let totalBytes = 0;
1049
- let count = 0;
1050
- if (!raw) return { headers };
1051
- for (const rawName of Object.keys(raw)) {
1052
- if (count >= MAX_HEADERS) break;
1053
- const name = rawName.toLowerCase();
1054
- const rawValue = raw[rawName];
1055
- if (rawValue === void 0) continue;
1056
- const joined = Array.isArray(rawValue) ? rawValue.join(", ") : String(rawValue);
1057
- if (CREDENTIAL_HEADER_NAMES.has(name)) {
1058
- const fmt = extractApiKeyFormat(joined);
1059
- if (fmt && !apiKeyFormat) apiKeyFormat = fmt;
1060
- continue;
1061
- }
1062
- if (name === "cookie") {
1063
- cookieCount = joined.split(";").filter((s) => s.trim().length > 0).length;
1064
- }
1065
- if (isSecretHeader(name)) continue;
1066
- let value = joined;
1067
- if (byteLength(value) > MAX_HEADER_VALUE_BYTES) {
1068
- value = value.slice(0, MAX_HEADER_VALUE_BYTES);
1069
- }
1070
- const entryBytes = byteLength(name) + byteLength(value);
1071
- if (totalBytes + entryBytes > MAX_TOTAL_BYTES) continue;
1072
- totalBytes += entryBytes;
1073
- count++;
1074
- headers[name] = value;
1075
- if (isPlatformHeader(name)) platformHeaders[name] = value;
1076
- }
1077
- const result = { headers };
1078
- if (apiKeyFormat) result.apiKeyFormat = apiKeyFormat;
1079
- if (Object.keys(platformHeaders).length > 0) result.platformHeaders = platformHeaders;
1080
- if (cookieCount !== void 0) result.cookieCount = cookieCount;
1081
- return result;
1082
- }
1083
- var CONNECTION_HEADER_SOURCES = [
1084
- ["ip", ["cf-connecting-ip", "fastly-client-ip", "true-client-ip", "x-real-ip"]],
1085
- [
1086
- "country",
1087
- ["cf-ipcountry", "x-vercel-ip-country", "cloudfront-viewer-country", "fastly-geo-country"]
1088
- ],
1089
- ["region", ["x-vercel-ip-country-region", "cloudfront-viewer-country-region"]],
1090
- ["countryRegionName", ["cloudfront-viewer-country-region-name"]],
1091
- ["city", ["x-vercel-ip-city", "cloudfront-viewer-city"]],
1092
- ["latitude", ["x-vercel-ip-latitude", "cloudfront-viewer-latitude"]],
1093
- ["longitude", ["x-vercel-ip-longitude", "cloudfront-viewer-longitude"]],
1094
- ["postalCode", ["x-vercel-ip-postal-code", "cloudfront-viewer-postal-code"]],
1095
- ["asn", ["cloudfront-viewer-asn"]],
1096
- ["tlsVersion", ["cloudfront-viewer-tls"]],
1097
- ["httpVersion", ["cloudfront-viewer-http-version"]],
1098
- ["timeZone", ["x-vercel-ip-timezone", "cloudfront-viewer-time-zone"]],
1099
- ["metroCode", ["cloudfront-viewer-metro-code"]],
1100
- // TLS fingerprints can only be computed where TLS terminates. When a CDN
1101
- // terminates TLS in front of the merchant, its fingerprint headers ARE the
1102
- // connection-layer truth — normalising them here is the permanent design.
1103
- // CloudFront delivers JA3/JA4 only via an origin request policy and only
1104
- // for HTTPS viewer connections.
1105
- ["tlsFingerprint", ["cf-ja3-hash", "cloudfront-viewer-ja3-fingerprint"]],
1106
- ["ja4", ["cf-ja4", "cloudfront-viewer-ja4-fingerprint"]],
1107
- // Header-structure fingerprint: browser/SDK-distinctive header ordering.
1108
- ["headerOrder", ["cloudfront-viewer-header-order"]],
1109
- ["headerCount", ["cloudfront-viewer-header-count"]]
1110
- ];
1111
- var MAX_CONNECTION_VALUE_CHARS = 256;
1112
- function deriveConnectionFromHeaders(headers) {
1113
- if (!headers) return void 0;
1114
- const lower = {};
1115
- for (const name of Object.keys(headers)) {
1116
- const value = headers[name];
1117
- if (typeof value === "string" && value.length > 0) lower[name.toLowerCase()] = value;
1118
- }
1119
- const connection = {};
1120
- for (const [key, sources] of CONNECTION_HEADER_SOURCES) {
1121
- for (const source of sources) {
1122
- const value = lower[source]?.trim();
1123
- if (value) {
1124
- connection[key] = value.slice(0, MAX_CONNECTION_VALUE_CHARS);
1125
- break;
1126
- }
1374
+ // src/route-pattern.ts
1375
+ function routePatternToRegExp(pattern, opts = {}) {
1376
+ let source = "";
1377
+ for (let i = 0; i < pattern.length; i++) {
1378
+ const ch = pattern[i];
1379
+ if (ch === "*") {
1380
+ source += ".*";
1381
+ } else if (ch === ":" && /[A-Za-z_]/.test(pattern[i + 1] ?? "") && (i === 0 || pattern[i - 1] === "/")) {
1382
+ let j = i + 1;
1383
+ while (j < pattern.length && /[A-Za-z0-9_]/.test(pattern[j])) j++;
1384
+ source += "[^/]+";
1385
+ i = j - 1;
1386
+ } else {
1387
+ source += ch.replace(/[.+?^${}()|[\]\\/]/g, "\\$&");
1127
1388
  }
1128
1389
  }
1129
- {
1130
- const addr = lower["cloudfront-viewer-address"]?.trim();
1131
- const portMatch = addr?.match(/:(\d+)$/);
1132
- if (portMatch) connection.sourcePort = portMatch[1];
1133
- if (!connection.ip && addr) connection.ip = addr.replace(/:\d+$/, "");
1134
- }
1135
- if (!connection.ip) {
1136
- const xff = lower["x-forwarded-for"];
1137
- const client = xff?.split(",")[0]?.trim();
1138
- if (client) connection.ip = client;
1139
- }
1140
- if (!connection.tlsCipher) {
1141
- const cipher = connection.tlsVersion?.split(":")[1];
1142
- if (cipher) connection.tlsCipher = cipher;
1390
+ try {
1391
+ return new RegExp(`^${source}$`, opts.caseInsensitive ? "i" : "");
1392
+ } catch {
1393
+ return null;
1143
1394
  }
1144
- return Object.keys(connection).length > 0 ? connection : void 0;
1145
1395
  }
1146
- function buildSdkObservedMetadata(rawHeaders) {
1147
- const { headers, apiKeyFormat, platformHeaders, cookieCount } = sanitizeHeaders(rawHeaders);
1148
- const flat = {};
1149
- if (rawHeaders) {
1150
- for (const name of Object.keys(rawHeaders)) {
1151
- const value = rawHeaders[name];
1152
- if (value === void 0) continue;
1153
- flat[name.toLowerCase()] = Array.isArray(value) ? value.join(", ") : String(value);
1154
- }
1155
- }
1156
- const connection = deriveConnectionFromHeaders(flat);
1157
- return {
1158
- headers,
1159
- ...apiKeyFormat && { apiKeyFormat },
1160
- ...platformHeaders && { platformHeaders },
1161
- ...connection && { connection },
1162
- ...cookieCount !== void 0 && { cookieCount },
1163
- schemaVersion: CAPTURE_SCHEMA_VERSION
1164
- };
1396
+ function matchRoutePattern(pattern, path, opts = {}) {
1397
+ return routePatternToRegExp(pattern, opts)?.test(path) ?? false;
1165
1398
  }
1166
1399
 
1167
1400
  // src/edge-config.ts
@@ -1196,6 +1429,7 @@ function isEdgeConfig(value) {
1196
1429
  if (r.sampling !== void 0 && !isSamplingShape(r.sampling)) return false;
1197
1430
  }
1198
1431
  if (!isSamplingShape(v.sampling)) return false;
1432
+ if (v.minTls !== void 0 && v.minTls !== "1.2" && v.minTls !== "1.3") return false;
1199
1433
  if (v.queryValueAllowlist !== void 0) {
1200
1434
  if (!Array.isArray(v.queryValueAllowlist) || v.queryValueAllowlist.length > 16 || !v.queryValueAllowlist.every((e) => typeof e === "string" && e.length > 0 && e.length <= 64)) {
1201
1435
  return false;
@@ -1911,9 +2145,7 @@ function extractCredentialsFromNextRequest(request) {
1911
2145
  return credentials;
1912
2146
  }
1913
2147
  function matchRoute(pattern, path) {
1914
- const regexPattern = pattern.replace(/\*/g, ".*").replace(/\//g, "\\/");
1915
- const regex = new RegExp(`^${regexPattern}$`);
1916
- return regex.test(path);
2148
+ return matchRoutePattern(pattern, path);
1917
2149
  }
1918
2150
  function findRouteConfig(routes, path, method) {
1919
2151
  return routes.find((route) => {
@@ -2104,7 +2336,8 @@ function createMiddleware(options) {
2104
2336
  skipPaths = [],
2105
2337
  showInterstitial,
2106
2338
  showCommerceShield,
2107
- enableRuntimeChallenge = true,
2339
+ // Round 27.8: opt-in only (the merchant turns it on); was default-on.
2340
+ enableRuntimeChallenge = false,
2108
2341
  routesRefreshMs = DEFAULT_ROUTES_REFRESH_MS,
2109
2342
  anonymousBeacon = true,
2110
2343
  trustVerifiedHop = false,
@@ -2241,14 +2474,17 @@ function createMiddleware(options) {
2241
2474
  const originalClientIp = forwardedFor?.split(",")[0]?.trim();
2242
2475
  const attemptHeader = request.headers.get("x-astra-attempt-id");
2243
2476
  const inboundAttemptId = attemptHeader && /^att_[0-9a-f]{32}$/.test(attemptHeader) ? attemptHeader : void 0;
2477
+ const commerceArtifacts = await detectFetchCommerce(request);
2244
2478
  const verificationRequest = {
2245
2479
  credentials,
2480
+ ...commerceArtifacts && { commerceArtifacts },
2246
2481
  purpose,
2247
2482
  action: pdlssPair.action,
2248
2483
  resource: pathname,
2249
2484
  counterpartyUrl,
2250
2485
  counterpartyType: config.counterpartyType || "website",
2251
2486
  enableRuntimeChallenge,
2487
+ ...routePolicyOf(routeConfig) && { routePolicy: routePolicyOf(routeConfig) },
2252
2488
  durationRequired: astraCreds?.pdlss?.duration?.maxSessionDuration,
2253
2489
  ...inboundAttemptId && { attemptId: inboundAttemptId },
2254
2490
  callerMetadata: {