@astrasyncai/verification-gateway 5.13.0 → 5.15.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 +27 -17
- package/dist/adapters/express.d.mts +1 -1
- package/dist/adapters/express.d.ts +1 -1
- package/dist/adapters/express.js +504 -249
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +504 -249
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/mcp.d.mts +1 -1
- package/dist/adapters/mcp.d.ts +1 -1
- package/dist/adapters/mcp.js +20 -6
- package/dist/adapters/mcp.js.map +1 -1
- package/dist/adapters/mcp.mjs +20 -6
- package/dist/adapters/mcp.mjs.map +1 -1
- package/dist/adapters/nextjs.d.mts +1 -1
- package/dist/adapters/nextjs.d.ts +1 -1
- package/dist/adapters/nextjs.js +487 -248
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +487 -248
- package/dist/adapters/nextjs.mjs.map +1 -1
- package/dist/adapters/sdk.d.mts +18 -10
- package/dist/adapters/sdk.d.ts +18 -10
- package/dist/adapters/sdk.js +80 -23
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +80 -23
- package/dist/adapters/sdk.mjs.map +1 -1
- package/dist/agent/index.js +1 -1
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/index.mjs +1 -1
- package/dist/agent/index.mjs.map +1 -1
- package/dist/bin/astrasync-claude-hook.js +82 -20
- package/dist/bin/astrasync-codex-hook.js +82 -20
- package/dist/bin/astrasync-guard.js +82 -20
- package/dist/bin/astrasync.js +316 -43
- package/dist/browser/background.js +77 -15
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +87 -15
- package/dist/browser/background.mjs.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +1 -1
- package/dist/cli/index.mjs.map +1 -1
- package/dist/codex/index.js +82 -20
- package/dist/codex/index.js.map +1 -1
- package/dist/codex/index.mjs +92 -20
- package/dist/codex/index.mjs.map +1 -1
- package/dist/cursor/extension.js +82 -20
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +92 -20
- package/dist/cursor/extension.mjs.map +1 -1
- package/dist/edge-config.d.mts +31 -2
- package/dist/edge-config.d.ts +31 -2
- package/dist/edge-config.js +35 -5
- package/dist/edge-config.js.map +1 -1
- package/dist/edge-config.mjs +32 -4
- package/dist/edge-config.mjs.map +1 -1
- package/dist/edge-core/index.d.mts +43 -6
- package/dist/edge-core/index.d.ts +43 -6
- package/dist/edge-core/index.js +120 -19
- package/dist/edge-core/index.js.map +1 -1
- package/dist/edge-core/index.mjs +118 -19
- package/dist/edge-core/index.mjs.map +1 -1
- package/dist/gateway/gateway.js +77 -15
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +87 -15
- package/dist/gateway/gateway.mjs.map +1 -1
- package/dist/git-trigger/git-hooks.d.mts +1 -1
- package/dist/git-trigger/git-hooks.d.ts +1 -1
- package/dist/index.d.mts +1314 -34
- package/dist/index.d.ts +1314 -34
- package/dist/index.js +5143 -2621
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5090 -2623
- package/dist/index.mjs.map +1 -1
- package/dist/registration/index.d.mts +28 -2
- package/dist/registration/index.d.ts +28 -2
- package/dist/registration/index.js +18 -3
- package/dist/registration/index.js.map +1 -1
- package/dist/registration/index.mjs +15 -2
- package/dist/registration/index.mjs.map +1 -1
- package/dist/transport/index.d.mts +7 -5
- package/dist/transport/index.d.ts +7 -5
- package/dist/transport/index.js +61 -5
- package/dist/transport/index.js.map +1 -1
- package/dist/transport/index.mjs +67 -11
- package/dist/transport/index.mjs.map +1 -1
- package/dist/{types-C_b4QTyO.d.ts → types-DtJjlW4O.d.ts} +104 -10
- package/dist/{types-BCkwBA84.d.mts → types-Dxf9DBpr.d.mts} +106 -12
- package/dist/{types-BCkwBA84.d.ts → types-Dxf9DBpr.d.ts} +106 -12
- package/dist/{types-DlshIIR-.d.mts → types-Eg6VoBNH.d.mts} +104 -10
- package/dist/ui/index.d.mts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/dist/verify.d.mts +1 -1
- package/dist/verify.d.ts +1 -1
- package/dist/verify.js +20 -6
- package/dist/verify.js.map +1 -1
- package/dist/verify.mjs +20 -6
- package/dist/verify.mjs.map +1 -1
- package/package.json +1 -1
package/dist/adapters/nextjs.js
CHANGED
|
@@ -35,8 +35,422 @@ __export(nextjs_exports, {
|
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(nextjs_exports);
|
|
37
37
|
|
|
38
|
+
// src/metadata-capture.ts
|
|
39
|
+
var CAPTURE_SCHEMA_VERSION = 1;
|
|
40
|
+
var SECRET_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
41
|
+
"cookie",
|
|
42
|
+
"set-cookie",
|
|
43
|
+
"x-csrf-token",
|
|
44
|
+
"proxy-authorization",
|
|
45
|
+
"x-astrasync-signature",
|
|
46
|
+
"x-astrasync-secret",
|
|
47
|
+
"x-hub-signature",
|
|
48
|
+
"x-hub-signature-256",
|
|
49
|
+
// Short-lived verify dedupe token — must never persist in stored metadata.
|
|
50
|
+
"x-astra-verified-hop"
|
|
51
|
+
]);
|
|
52
|
+
var SECRET_HEADER_PREFIXES = ["x-astrasync-", "kya-", "kya_"];
|
|
53
|
+
var CREDENTIAL_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
54
|
+
"authorization",
|
|
55
|
+
"x-api-key",
|
|
56
|
+
"x-goog-api-key",
|
|
57
|
+
"api-key"
|
|
58
|
+
]);
|
|
59
|
+
function isSignatureHeader(name) {
|
|
60
|
+
return name === "signature" || name.endsWith("-signature") || name.endsWith("-signature-256");
|
|
61
|
+
}
|
|
62
|
+
var PLATFORM_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
63
|
+
"openai-organization",
|
|
64
|
+
"openai-project",
|
|
65
|
+
"openai-version",
|
|
66
|
+
"openai-processing-ms",
|
|
67
|
+
"anthropic-version",
|
|
68
|
+
"anthropic-beta",
|
|
69
|
+
"x-goog-user-project",
|
|
70
|
+
"x-goog-api-client",
|
|
71
|
+
"user-agent",
|
|
72
|
+
"x-agent-card-url",
|
|
73
|
+
"x-agent-provider"
|
|
74
|
+
]);
|
|
75
|
+
var PLATFORM_HEADER_PREFIXES = [
|
|
76
|
+
"x-goog-",
|
|
77
|
+
"x-stainless-",
|
|
78
|
+
"x-anthropic-",
|
|
79
|
+
"x-openai-",
|
|
80
|
+
"sec-ch-"
|
|
81
|
+
];
|
|
82
|
+
var KEY_FORMAT_PREFIXES = [
|
|
83
|
+
"sk-ant-api03",
|
|
84
|
+
"sk-ant-api",
|
|
85
|
+
"sk-ant",
|
|
86
|
+
"sk-proj",
|
|
87
|
+
"sk-svcacct",
|
|
88
|
+
"sk-or-v1",
|
|
89
|
+
"sk-or",
|
|
90
|
+
"sk-lf",
|
|
91
|
+
"sk-",
|
|
92
|
+
"gsk_",
|
|
93
|
+
"aiza",
|
|
94
|
+
"ya29",
|
|
95
|
+
"ghp_",
|
|
96
|
+
"gho_",
|
|
97
|
+
"github_pat_",
|
|
98
|
+
"xai-",
|
|
99
|
+
"pplx-",
|
|
100
|
+
"r8_",
|
|
101
|
+
"hf_",
|
|
102
|
+
"astra-",
|
|
103
|
+
"astrae-"
|
|
104
|
+
];
|
|
105
|
+
var MAX_HEADERS = 64;
|
|
106
|
+
var MAX_HEADER_VALUE_BYTES = 1024;
|
|
107
|
+
var MAX_TOTAL_BYTES = 16 * 1024;
|
|
108
|
+
function byteLength(s) {
|
|
109
|
+
let bytes = 0;
|
|
110
|
+
for (let i = 0; i < s.length; i++) {
|
|
111
|
+
const code = s.charCodeAt(i);
|
|
112
|
+
if (code < 128) bytes += 1;
|
|
113
|
+
else if (code < 2048) bytes += 2;
|
|
114
|
+
else if (code >= 55296 && code <= 56319) {
|
|
115
|
+
bytes += 4;
|
|
116
|
+
i++;
|
|
117
|
+
} else bytes += 3;
|
|
118
|
+
}
|
|
119
|
+
return bytes;
|
|
120
|
+
}
|
|
121
|
+
function extractApiKeyFormat(rawValue) {
|
|
122
|
+
if (!rawValue) return void 0;
|
|
123
|
+
let value = rawValue.trim();
|
|
124
|
+
const spaceIdx = value.indexOf(" ");
|
|
125
|
+
if (spaceIdx > 0) {
|
|
126
|
+
const scheme = value.slice(0, spaceIdx).toLowerCase();
|
|
127
|
+
if (scheme === "bearer" || scheme === "basic" || scheme === "token") {
|
|
128
|
+
value = value.slice(spaceIdx + 1).trim();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const lower = value.toLowerCase();
|
|
132
|
+
for (const prefix of KEY_FORMAT_PREFIXES) {
|
|
133
|
+
if (lower.startsWith(prefix)) {
|
|
134
|
+
return prefix;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return void 0;
|
|
138
|
+
}
|
|
139
|
+
function isSecretHeader(name) {
|
|
140
|
+
if (SECRET_HEADER_NAMES.has(name)) return true;
|
|
141
|
+
if (SECRET_HEADER_PREFIXES.some((p) => name.startsWith(p))) return true;
|
|
142
|
+
if (isSignatureHeader(name)) return true;
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
function isPlatformHeader(name) {
|
|
146
|
+
if (PLATFORM_HEADER_NAMES.has(name)) return true;
|
|
147
|
+
return PLATFORM_HEADER_PREFIXES.some((p) => name.startsWith(p));
|
|
148
|
+
}
|
|
149
|
+
function sanitizeHeaders(raw) {
|
|
150
|
+
const headers = {};
|
|
151
|
+
const platformHeaders = {};
|
|
152
|
+
let apiKeyFormat;
|
|
153
|
+
let cookieCount;
|
|
154
|
+
let totalBytes = 0;
|
|
155
|
+
let count = 0;
|
|
156
|
+
if (!raw) return { headers };
|
|
157
|
+
for (const rawName of Object.keys(raw)) {
|
|
158
|
+
if (count >= MAX_HEADERS) break;
|
|
159
|
+
const name = rawName.toLowerCase();
|
|
160
|
+
const rawValue = raw[rawName];
|
|
161
|
+
if (rawValue === void 0) continue;
|
|
162
|
+
const joined = Array.isArray(rawValue) ? rawValue.join(", ") : String(rawValue);
|
|
163
|
+
if (CREDENTIAL_HEADER_NAMES.has(name)) {
|
|
164
|
+
const fmt = extractApiKeyFormat(joined);
|
|
165
|
+
if (fmt && !apiKeyFormat) apiKeyFormat = fmt;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (name === "cookie") {
|
|
169
|
+
cookieCount = joined.split(";").filter((s) => s.trim().length > 0).length;
|
|
170
|
+
}
|
|
171
|
+
if (isSecretHeader(name)) continue;
|
|
172
|
+
let value = joined;
|
|
173
|
+
if (byteLength(value) > MAX_HEADER_VALUE_BYTES) {
|
|
174
|
+
value = value.slice(0, MAX_HEADER_VALUE_BYTES);
|
|
175
|
+
}
|
|
176
|
+
const entryBytes = byteLength(name) + byteLength(value);
|
|
177
|
+
if (totalBytes + entryBytes > MAX_TOTAL_BYTES) continue;
|
|
178
|
+
totalBytes += entryBytes;
|
|
179
|
+
count++;
|
|
180
|
+
headers[name] = value;
|
|
181
|
+
if (isPlatformHeader(name)) platformHeaders[name] = value;
|
|
182
|
+
}
|
|
183
|
+
const result = { headers };
|
|
184
|
+
if (apiKeyFormat) result.apiKeyFormat = apiKeyFormat;
|
|
185
|
+
if (Object.keys(platformHeaders).length > 0) result.platformHeaders = platformHeaders;
|
|
186
|
+
if (cookieCount !== void 0) result.cookieCount = cookieCount;
|
|
187
|
+
return result;
|
|
188
|
+
}
|
|
189
|
+
var CONNECTION_HEADER_SOURCES = [
|
|
190
|
+
["ip", ["cf-connecting-ip", "fastly-client-ip", "true-client-ip", "x-real-ip"]],
|
|
191
|
+
[
|
|
192
|
+
"country",
|
|
193
|
+
["cf-ipcountry", "x-vercel-ip-country", "cloudfront-viewer-country", "fastly-geo-country"]
|
|
194
|
+
],
|
|
195
|
+
["region", ["x-vercel-ip-country-region", "cloudfront-viewer-country-region"]],
|
|
196
|
+
["countryRegionName", ["cloudfront-viewer-country-region-name"]],
|
|
197
|
+
["city", ["x-vercel-ip-city", "cloudfront-viewer-city"]],
|
|
198
|
+
["latitude", ["x-vercel-ip-latitude", "cloudfront-viewer-latitude"]],
|
|
199
|
+
["longitude", ["x-vercel-ip-longitude", "cloudfront-viewer-longitude"]],
|
|
200
|
+
["postalCode", ["x-vercel-ip-postal-code", "cloudfront-viewer-postal-code"]],
|
|
201
|
+
["asn", ["cloudfront-viewer-asn"]],
|
|
202
|
+
["tlsVersion", ["cloudfront-viewer-tls"]],
|
|
203
|
+
["httpVersion", ["cloudfront-viewer-http-version"]],
|
|
204
|
+
["timeZone", ["x-vercel-ip-timezone", "cloudfront-viewer-time-zone"]],
|
|
205
|
+
["metroCode", ["cloudfront-viewer-metro-code"]],
|
|
206
|
+
// TLS fingerprints can only be computed where TLS terminates. When a CDN
|
|
207
|
+
// terminates TLS in front of the merchant, its fingerprint headers ARE the
|
|
208
|
+
// connection-layer truth — normalising them here is the permanent design.
|
|
209
|
+
// CloudFront delivers JA3/JA4 only via an origin request policy and only
|
|
210
|
+
// for HTTPS viewer connections.
|
|
211
|
+
["tlsFingerprint", ["cf-ja3-hash", "cloudfront-viewer-ja3-fingerprint"]],
|
|
212
|
+
["ja4", ["cf-ja4", "cloudfront-viewer-ja4-fingerprint"]],
|
|
213
|
+
// Header-structure fingerprint: browser/SDK-distinctive header ordering.
|
|
214
|
+
["headerOrder", ["cloudfront-viewer-header-order"]],
|
|
215
|
+
["headerCount", ["cloudfront-viewer-header-count"]]
|
|
216
|
+
];
|
|
217
|
+
var MAX_CONNECTION_VALUE_CHARS = 256;
|
|
218
|
+
function deriveConnectionFromHeaders(headers) {
|
|
219
|
+
if (!headers) return void 0;
|
|
220
|
+
const lower = {};
|
|
221
|
+
for (const name of Object.keys(headers)) {
|
|
222
|
+
const value = headers[name];
|
|
223
|
+
if (typeof value === "string" && value.length > 0) lower[name.toLowerCase()] = value;
|
|
224
|
+
}
|
|
225
|
+
const connection = {};
|
|
226
|
+
for (const [key, sources] of CONNECTION_HEADER_SOURCES) {
|
|
227
|
+
for (const source of sources) {
|
|
228
|
+
const value = lower[source]?.trim();
|
|
229
|
+
if (value) {
|
|
230
|
+
connection[key] = value.slice(0, MAX_CONNECTION_VALUE_CHARS);
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
{
|
|
236
|
+
const addr = lower["cloudfront-viewer-address"]?.trim();
|
|
237
|
+
const portMatch = addr?.match(/:(\d+)$/);
|
|
238
|
+
if (portMatch) connection.sourcePort = portMatch[1];
|
|
239
|
+
if (!connection.ip && addr) connection.ip = addr.replace(/:\d+$/, "");
|
|
240
|
+
}
|
|
241
|
+
if (!connection.ip) {
|
|
242
|
+
const xff = lower["x-forwarded-for"];
|
|
243
|
+
const client = xff?.split(",")[0]?.trim();
|
|
244
|
+
if (client) connection.ip = client;
|
|
245
|
+
}
|
|
246
|
+
if (!connection.tlsCipher) {
|
|
247
|
+
const cipher = connection.tlsVersion?.split(":")[1];
|
|
248
|
+
if (cipher) connection.tlsCipher = cipher;
|
|
249
|
+
}
|
|
250
|
+
return Object.keys(connection).length > 0 ? connection : void 0;
|
|
251
|
+
}
|
|
252
|
+
function buildSdkObservedMetadata(rawHeaders) {
|
|
253
|
+
const { headers, apiKeyFormat, platformHeaders, cookieCount } = sanitizeHeaders(rawHeaders);
|
|
254
|
+
const flat = {};
|
|
255
|
+
if (rawHeaders) {
|
|
256
|
+
for (const name of Object.keys(rawHeaders)) {
|
|
257
|
+
const value = rawHeaders[name];
|
|
258
|
+
if (value === void 0) continue;
|
|
259
|
+
flat[name.toLowerCase()] = Array.isArray(value) ? value.join(", ") : String(value);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
const connection = deriveConnectionFromHeaders(flat);
|
|
263
|
+
return {
|
|
264
|
+
headers,
|
|
265
|
+
...apiKeyFormat && { apiKeyFormat },
|
|
266
|
+
...platformHeaders && { platformHeaders },
|
|
267
|
+
...connection && { connection },
|
|
268
|
+
...cookieCount !== void 0 && { cookieCount },
|
|
269
|
+
schemaVersion: CAPTURE_SCHEMA_VERSION
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// src/edge-core/request.ts
|
|
274
|
+
function headerValue(req, name) {
|
|
275
|
+
return req.headers[name.toLowerCase()];
|
|
276
|
+
}
|
|
277
|
+
function requestUrl(req, host) {
|
|
278
|
+
const qs = req.querystring ? `?${req.querystring}` : "";
|
|
279
|
+
return `https://${host}${req.uri}${qs}`;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// src/edge-core/detect.ts
|
|
283
|
+
var SD_JWT_PATTERN = /^eyJ[\w-]+\.[\w-]*\.[\w-]+~/;
|
|
284
|
+
function decodeBase64Url(seg) {
|
|
285
|
+
const b64 = seg.replace(/-/g, "+").replace(/_/g, "/");
|
|
286
|
+
const padded = b64 + "=".repeat((4 - b64.length % 4) % 4);
|
|
287
|
+
const binary = atob(padded);
|
|
288
|
+
const bytes = new Uint8Array(binary.length);
|
|
289
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
290
|
+
return new TextDecoder().decode(bytes);
|
|
291
|
+
}
|
|
292
|
+
function sniffJwtHeader(compact) {
|
|
293
|
+
try {
|
|
294
|
+
const seg = compact.split(".")[0];
|
|
295
|
+
return JSON.parse(decodeBase64Url(seg));
|
|
296
|
+
} catch {
|
|
297
|
+
return void 0;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
function parseJsonBody(bodyText) {
|
|
301
|
+
if (!bodyText) return void 0;
|
|
302
|
+
try {
|
|
303
|
+
const parsed = JSON.parse(bodyText);
|
|
304
|
+
return typeof parsed === "object" && parsed !== null ? parsed : void 0;
|
|
305
|
+
} catch {
|
|
306
|
+
return void 0;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
function detectCommerce(req, host) {
|
|
310
|
+
const url = requestUrl(req, host);
|
|
311
|
+
const method = req.method;
|
|
312
|
+
const headers = req.headers;
|
|
313
|
+
const jsonBody = parseJsonBody(req.bodyText);
|
|
314
|
+
const truncated = req.bodyTruncated ? { bodyTruncated: true } : {};
|
|
315
|
+
const authHeader = headerValue(req, "authorization");
|
|
316
|
+
const bearer = authHeader?.replace(/^Bearer\s+/i, "");
|
|
317
|
+
const mandateHeader = headerValue(req, "x-payment-mandate") ?? headerValue(req, "payment-mandate");
|
|
318
|
+
const sdJwtCandidate = [mandateHeader, bearer].find((v) => v && SD_JWT_PATTERN.test(v));
|
|
319
|
+
if (sdJwtCandidate) {
|
|
320
|
+
const typ = sniffJwtHeader(sdJwtCandidate)?.typ;
|
|
321
|
+
return {
|
|
322
|
+
protocol: "vi-sd-jwt",
|
|
323
|
+
artifacts: { viSdJwt: sdJwtCandidate },
|
|
324
|
+
evidence: { typ, ...truncated }
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
const signature = headerValue(req, "signature");
|
|
328
|
+
const signatureInput = headerValue(req, "signature-input");
|
|
329
|
+
if (signature && signatureInput) {
|
|
330
|
+
const signatureAgent = headerValue(req, "signature-agent");
|
|
331
|
+
const registryHint = headerValue(req, "x-agent-registry");
|
|
332
|
+
const lowerInput = signatureInput.toLowerCase();
|
|
333
|
+
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";
|
|
334
|
+
return {
|
|
335
|
+
protocol,
|
|
336
|
+
artifacts: {
|
|
337
|
+
rfc9421: { headers, method, url, ...req.bodyText && { body: req.bodyText } }
|
|
338
|
+
},
|
|
339
|
+
evidence: {
|
|
340
|
+
...signatureAgent && { signatureAgent },
|
|
341
|
+
...registryHint && { registryHint },
|
|
342
|
+
keyid: signatureInput.match(/keyid="([^"]+)"/)?.[1],
|
|
343
|
+
...truncated
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
const acpTimestamp = headerValue(req, "timestamp");
|
|
348
|
+
if (signature && !signatureInput && (acpTimestamp || headerValue(req, "idempotency-key"))) {
|
|
349
|
+
return {
|
|
350
|
+
protocol: "acp",
|
|
351
|
+
artifacts: {
|
|
352
|
+
acpRequest: {
|
|
353
|
+
method,
|
|
354
|
+
url,
|
|
355
|
+
headers,
|
|
356
|
+
...req.bodyText && { rawBody: req.bodyText },
|
|
357
|
+
...jsonBody && { body: jsonBody }
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
evidence: { hasTimestamp: Boolean(acpTimestamp), ...truncated }
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
if (/\/checkout[-_]sessions(\/|$)/.test(req.uri)) {
|
|
364
|
+
return {
|
|
365
|
+
protocol: "ucp",
|
|
366
|
+
artifacts: { ucpRequest: { method, url, ...jsonBody && { body: jsonBody } } },
|
|
367
|
+
evidence: { path: req.uri, ...truncated }
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
if (jsonBody) {
|
|
371
|
+
const intent = jsonBody.intent_mandate ?? jsonBody.intentMandate;
|
|
372
|
+
const cart = jsonBody.cart_mandate ?? jsonBody.cartMandate;
|
|
373
|
+
const payment = jsonBody.payment_mandate ?? jsonBody.paymentMandate;
|
|
374
|
+
if (typeof intent === "string" || typeof cart === "string" || typeof payment === "string") {
|
|
375
|
+
return {
|
|
376
|
+
protocol: "ap2",
|
|
377
|
+
artifacts: {
|
|
378
|
+
ap2Mandates: {
|
|
379
|
+
...typeof intent === "string" && { intent },
|
|
380
|
+
...typeof cart === "string" && { cart },
|
|
381
|
+
...typeof payment === "string" && { payment }
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
evidence: {
|
|
385
|
+
mandatesPresent: [
|
|
386
|
+
typeof intent === "string" && "intent",
|
|
387
|
+
typeof cart === "string" && "cart",
|
|
388
|
+
typeof payment === "string" && "payment"
|
|
389
|
+
].filter(Boolean),
|
|
390
|
+
...truncated
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
if (authHeader && /^Payment\s+/i.test(authHeader) || headerValue(req, "payment-authorization")) {
|
|
396
|
+
return {
|
|
397
|
+
protocol: "mpp",
|
|
398
|
+
artifacts: {
|
|
399
|
+
mppRequest: {
|
|
400
|
+
method,
|
|
401
|
+
url,
|
|
402
|
+
headers,
|
|
403
|
+
...req.bodyText && { rawBody: req.bodyText },
|
|
404
|
+
...jsonBody && { body: jsonBody }
|
|
405
|
+
}
|
|
406
|
+
},
|
|
407
|
+
evidence: truncated
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
if (headerValue(req, "payment-signature") || headerValue(req, "x-payment")) {
|
|
411
|
+
return {
|
|
412
|
+
protocol: "x402",
|
|
413
|
+
artifacts: {
|
|
414
|
+
x402Request: { method, url, headers, ...jsonBody && { body: jsonBody } }
|
|
415
|
+
},
|
|
416
|
+
evidence: truncated
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
return void 0;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// src/adapters/commerce-detect.ts
|
|
423
|
+
var MAX_BODY_BYTES = 64 * 1024;
|
|
424
|
+
function detect(req, host) {
|
|
425
|
+
try {
|
|
426
|
+
return detectCommerce(req, host)?.artifacts;
|
|
427
|
+
} catch {
|
|
428
|
+
return void 0;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
async function detectFetchCommerce(request) {
|
|
432
|
+
const url = new URL(request.url);
|
|
433
|
+
const headers = {};
|
|
434
|
+
request.headers.forEach((v, k) => headers[k.toLowerCase()] = v);
|
|
435
|
+
let bodyText;
|
|
436
|
+
const length = Number(headers["content-length"] ?? 0);
|
|
437
|
+
if (request.method !== "GET" && request.method !== "HEAD" && length <= MAX_BODY_BYTES) {
|
|
438
|
+
bodyText = await request.clone().text().catch(() => void 0);
|
|
439
|
+
}
|
|
440
|
+
return detect(
|
|
441
|
+
{
|
|
442
|
+
method: request.method,
|
|
443
|
+
uri: url.pathname,
|
|
444
|
+
querystring: url.search.replace(/^\?/, ""),
|
|
445
|
+
headers,
|
|
446
|
+
...bodyText ? { bodyText } : {}
|
|
447
|
+
},
|
|
448
|
+
headers["x-forwarded-host"] ?? url.host
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
|
|
38
452
|
// src/version.ts
|
|
39
|
-
var SDK_VERSION = "5.
|
|
453
|
+
var SDK_VERSION = "5.15.0";
|
|
40
454
|
|
|
41
455
|
// src/http.ts
|
|
42
456
|
var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
|
|
@@ -500,6 +914,7 @@ async function callVerifyAccessAPI(config, request) {
|
|
|
500
914
|
if (requestData.invocationProtocol) body.invocationProtocol = requestData.invocationProtocol;
|
|
501
915
|
if (requestData.commercePhase) body.commercePhase = requestData.commercePhase;
|
|
502
916
|
if (requestData.checkoutSessionId) body.checkoutSessionId = requestData.checkoutSessionId;
|
|
917
|
+
if (requestData.checkoutJwt) body.checkoutJwt = requestData.checkoutJwt;
|
|
503
918
|
if (requestData.checkoutItems) body.checkoutItems = requestData.checkoutItems;
|
|
504
919
|
if (requestData.buyerEmail) body.buyerEmail = requestData.buyerEmail;
|
|
505
920
|
if (requestData.commerceArtifacts) body.commerceArtifacts = requestData.commerceArtifacts;
|
|
@@ -650,7 +1065,8 @@ async function verify(config, request, options) {
|
|
|
650
1065
|
"[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."
|
|
651
1066
|
);
|
|
652
1067
|
}
|
|
653
|
-
|
|
1068
|
+
const uncacheable = Boolean(request.commerceArtifacts);
|
|
1069
|
+
if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache && !uncacheable) {
|
|
654
1070
|
const cached = getCachedResult(request, mergedConfig.counterpartyId);
|
|
655
1071
|
if (cached) {
|
|
656
1072
|
if (mergedConfig.debug) {
|
|
@@ -731,7 +1147,10 @@ async function verify(config, request, options) {
|
|
|
731
1147
|
// MERCHANT caller only (bridge callers strip it before the agent plane).
|
|
732
1148
|
settlementToken: apiResponse.settlementToken,
|
|
733
1149
|
// 5.6.0: fulfilment contact — merchant-only lane, same strip rule.
|
|
734
|
-
fulfilment: apiResponse.fulfilment
|
|
1150
|
+
fulfilment: apiResponse.fulfilment,
|
|
1151
|
+
mandates: apiResponse.mandates,
|
|
1152
|
+
kyaToken: apiResponse.kyaToken,
|
|
1153
|
+
aep: apiResponse.aep
|
|
735
1154
|
};
|
|
736
1155
|
return result2;
|
|
737
1156
|
}
|
|
@@ -741,7 +1160,10 @@ async function verify(config, request, options) {
|
|
|
741
1160
|
trustScore: apiResponse.agent.trustScore,
|
|
742
1161
|
trustLevel: getTrustLevel(apiResponse.agent.trustScore),
|
|
743
1162
|
blockchainAnchored: apiResponse.agent.blockchainStatus === "anchored",
|
|
744
|
-
status: apiResponse.agent.agentStatus
|
|
1163
|
+
status: apiResponse.agent.agentStatus,
|
|
1164
|
+
...apiResponse.agent.protocolIdentities && {
|
|
1165
|
+
protocolIdentities: apiResponse.agent.protocolIdentities
|
|
1166
|
+
}
|
|
745
1167
|
} : void 0;
|
|
746
1168
|
const developer = apiResponse.developer ? {
|
|
747
1169
|
astradId: apiResponse.developer.kyaOwnerId,
|
|
@@ -792,7 +1214,11 @@ async function verify(config, request, options) {
|
|
|
792
1214
|
settlementOutcome: apiResponse.settlementOutcome,
|
|
793
1215
|
settlementToken: apiResponse.settlementToken,
|
|
794
1216
|
// 5.6.0: fulfilment contact — merchant-only lane, same strip rule.
|
|
795
|
-
fulfilment: apiResponse.fulfilment
|
|
1217
|
+
fulfilment: apiResponse.fulfilment,
|
|
1218
|
+
// 5.15.0: merchant-lane AP2 mandates + KYAPay identity token.
|
|
1219
|
+
mandates: apiResponse.mandates,
|
|
1220
|
+
kyaToken: apiResponse.kyaToken,
|
|
1221
|
+
aep: apiResponse.aep
|
|
796
1222
|
};
|
|
797
1223
|
if (result.recommendation === "deny") {
|
|
798
1224
|
result.policyAllowed = false;
|
|
@@ -812,7 +1238,9 @@ async function verify(config, request, options) {
|
|
|
812
1238
|
}
|
|
813
1239
|
const escalates = result.requiresStepUp === true || result.requiresApproval === true || result.recommendation === "step_up_required";
|
|
814
1240
|
if (mergedConfig.cacheTtl !== 0 && result.recommendation !== "deny" && !escalates) {
|
|
815
|
-
|
|
1241
|
+
if (!uncacheable) {
|
|
1242
|
+
cacheResult(request, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);
|
|
1243
|
+
}
|
|
816
1244
|
}
|
|
817
1245
|
return result;
|
|
818
1246
|
}
|
|
@@ -966,239 +1394,30 @@ function performCounterpartyPreCheck(routeConfig, astraCreds, purpose) {
|
|
|
966
1394
|
return failures;
|
|
967
1395
|
}
|
|
968
1396
|
|
|
969
|
-
// src/
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
]);
|
|
983
|
-
var SECRET_HEADER_PREFIXES = ["x-astrasync-", "kya-", "kya_"];
|
|
984
|
-
var CREDENTIAL_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
985
|
-
"authorization",
|
|
986
|
-
"x-api-key",
|
|
987
|
-
"x-goog-api-key",
|
|
988
|
-
"api-key"
|
|
989
|
-
]);
|
|
990
|
-
function isSignatureHeader(name) {
|
|
991
|
-
return name === "signature" || name.endsWith("-signature") || name.endsWith("-signature-256");
|
|
992
|
-
}
|
|
993
|
-
var PLATFORM_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
994
|
-
"openai-organization",
|
|
995
|
-
"openai-project",
|
|
996
|
-
"openai-version",
|
|
997
|
-
"openai-processing-ms",
|
|
998
|
-
"anthropic-version",
|
|
999
|
-
"anthropic-beta",
|
|
1000
|
-
"x-goog-user-project",
|
|
1001
|
-
"x-goog-api-client",
|
|
1002
|
-
"user-agent",
|
|
1003
|
-
"x-agent-card-url",
|
|
1004
|
-
"x-agent-provider"
|
|
1005
|
-
]);
|
|
1006
|
-
var PLATFORM_HEADER_PREFIXES = [
|
|
1007
|
-
"x-goog-",
|
|
1008
|
-
"x-stainless-",
|
|
1009
|
-
"x-anthropic-",
|
|
1010
|
-
"x-openai-",
|
|
1011
|
-
"sec-ch-"
|
|
1012
|
-
];
|
|
1013
|
-
var KEY_FORMAT_PREFIXES = [
|
|
1014
|
-
"sk-ant-api03",
|
|
1015
|
-
"sk-ant-api",
|
|
1016
|
-
"sk-ant",
|
|
1017
|
-
"sk-proj",
|
|
1018
|
-
"sk-svcacct",
|
|
1019
|
-
"sk-or-v1",
|
|
1020
|
-
"sk-or",
|
|
1021
|
-
"sk-lf",
|
|
1022
|
-
"sk-",
|
|
1023
|
-
"gsk_",
|
|
1024
|
-
"aiza",
|
|
1025
|
-
"ya29",
|
|
1026
|
-
"ghp_",
|
|
1027
|
-
"gho_",
|
|
1028
|
-
"github_pat_",
|
|
1029
|
-
"xai-",
|
|
1030
|
-
"pplx-",
|
|
1031
|
-
"r8_",
|
|
1032
|
-
"hf_",
|
|
1033
|
-
"astra-",
|
|
1034
|
-
"astrae-"
|
|
1035
|
-
];
|
|
1036
|
-
var MAX_HEADERS = 64;
|
|
1037
|
-
var MAX_HEADER_VALUE_BYTES = 1024;
|
|
1038
|
-
var MAX_TOTAL_BYTES = 16 * 1024;
|
|
1039
|
-
function byteLength(s) {
|
|
1040
|
-
let bytes = 0;
|
|
1041
|
-
for (let i = 0; i < s.length; i++) {
|
|
1042
|
-
const code = s.charCodeAt(i);
|
|
1043
|
-
if (code < 128) bytes += 1;
|
|
1044
|
-
else if (code < 2048) bytes += 2;
|
|
1045
|
-
else if (code >= 55296 && code <= 56319) {
|
|
1046
|
-
bytes += 4;
|
|
1047
|
-
i++;
|
|
1048
|
-
} else bytes += 3;
|
|
1049
|
-
}
|
|
1050
|
-
return bytes;
|
|
1051
|
-
}
|
|
1052
|
-
function extractApiKeyFormat(rawValue) {
|
|
1053
|
-
if (!rawValue) return void 0;
|
|
1054
|
-
let value = rawValue.trim();
|
|
1055
|
-
const spaceIdx = value.indexOf(" ");
|
|
1056
|
-
if (spaceIdx > 0) {
|
|
1057
|
-
const scheme = value.slice(0, spaceIdx).toLowerCase();
|
|
1058
|
-
if (scheme === "bearer" || scheme === "basic" || scheme === "token") {
|
|
1059
|
-
value = value.slice(spaceIdx + 1).trim();
|
|
1060
|
-
}
|
|
1061
|
-
}
|
|
1062
|
-
const lower = value.toLowerCase();
|
|
1063
|
-
for (const prefix of KEY_FORMAT_PREFIXES) {
|
|
1064
|
-
if (lower.startsWith(prefix)) {
|
|
1065
|
-
return prefix;
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
return void 0;
|
|
1069
|
-
}
|
|
1070
|
-
function isSecretHeader(name) {
|
|
1071
|
-
if (SECRET_HEADER_NAMES.has(name)) return true;
|
|
1072
|
-
if (SECRET_HEADER_PREFIXES.some((p) => name.startsWith(p))) return true;
|
|
1073
|
-
if (isSignatureHeader(name)) return true;
|
|
1074
|
-
return false;
|
|
1075
|
-
}
|
|
1076
|
-
function isPlatformHeader(name) {
|
|
1077
|
-
if (PLATFORM_HEADER_NAMES.has(name)) return true;
|
|
1078
|
-
return PLATFORM_HEADER_PREFIXES.some((p) => name.startsWith(p));
|
|
1079
|
-
}
|
|
1080
|
-
function sanitizeHeaders(raw) {
|
|
1081
|
-
const headers = {};
|
|
1082
|
-
const platformHeaders = {};
|
|
1083
|
-
let apiKeyFormat;
|
|
1084
|
-
let cookieCount;
|
|
1085
|
-
let totalBytes = 0;
|
|
1086
|
-
let count = 0;
|
|
1087
|
-
if (!raw) return { headers };
|
|
1088
|
-
for (const rawName of Object.keys(raw)) {
|
|
1089
|
-
if (count >= MAX_HEADERS) break;
|
|
1090
|
-
const name = rawName.toLowerCase();
|
|
1091
|
-
const rawValue = raw[rawName];
|
|
1092
|
-
if (rawValue === void 0) continue;
|
|
1093
|
-
const joined = Array.isArray(rawValue) ? rawValue.join(", ") : String(rawValue);
|
|
1094
|
-
if (CREDENTIAL_HEADER_NAMES.has(name)) {
|
|
1095
|
-
const fmt = extractApiKeyFormat(joined);
|
|
1096
|
-
if (fmt && !apiKeyFormat) apiKeyFormat = fmt;
|
|
1097
|
-
continue;
|
|
1098
|
-
}
|
|
1099
|
-
if (name === "cookie") {
|
|
1100
|
-
cookieCount = joined.split(";").filter((s) => s.trim().length > 0).length;
|
|
1101
|
-
}
|
|
1102
|
-
if (isSecretHeader(name)) continue;
|
|
1103
|
-
let value = joined;
|
|
1104
|
-
if (byteLength(value) > MAX_HEADER_VALUE_BYTES) {
|
|
1105
|
-
value = value.slice(0, MAX_HEADER_VALUE_BYTES);
|
|
1106
|
-
}
|
|
1107
|
-
const entryBytes = byteLength(name) + byteLength(value);
|
|
1108
|
-
if (totalBytes + entryBytes > MAX_TOTAL_BYTES) continue;
|
|
1109
|
-
totalBytes += entryBytes;
|
|
1110
|
-
count++;
|
|
1111
|
-
headers[name] = value;
|
|
1112
|
-
if (isPlatformHeader(name)) platformHeaders[name] = value;
|
|
1113
|
-
}
|
|
1114
|
-
const result = { headers };
|
|
1115
|
-
if (apiKeyFormat) result.apiKeyFormat = apiKeyFormat;
|
|
1116
|
-
if (Object.keys(platformHeaders).length > 0) result.platformHeaders = platformHeaders;
|
|
1117
|
-
if (cookieCount !== void 0) result.cookieCount = cookieCount;
|
|
1118
|
-
return result;
|
|
1119
|
-
}
|
|
1120
|
-
var CONNECTION_HEADER_SOURCES = [
|
|
1121
|
-
["ip", ["cf-connecting-ip", "fastly-client-ip", "true-client-ip", "x-real-ip"]],
|
|
1122
|
-
[
|
|
1123
|
-
"country",
|
|
1124
|
-
["cf-ipcountry", "x-vercel-ip-country", "cloudfront-viewer-country", "fastly-geo-country"]
|
|
1125
|
-
],
|
|
1126
|
-
["region", ["x-vercel-ip-country-region", "cloudfront-viewer-country-region"]],
|
|
1127
|
-
["countryRegionName", ["cloudfront-viewer-country-region-name"]],
|
|
1128
|
-
["city", ["x-vercel-ip-city", "cloudfront-viewer-city"]],
|
|
1129
|
-
["latitude", ["x-vercel-ip-latitude", "cloudfront-viewer-latitude"]],
|
|
1130
|
-
["longitude", ["x-vercel-ip-longitude", "cloudfront-viewer-longitude"]],
|
|
1131
|
-
["postalCode", ["x-vercel-ip-postal-code", "cloudfront-viewer-postal-code"]],
|
|
1132
|
-
["asn", ["cloudfront-viewer-asn"]],
|
|
1133
|
-
["tlsVersion", ["cloudfront-viewer-tls"]],
|
|
1134
|
-
["httpVersion", ["cloudfront-viewer-http-version"]],
|
|
1135
|
-
["timeZone", ["x-vercel-ip-timezone", "cloudfront-viewer-time-zone"]],
|
|
1136
|
-
["metroCode", ["cloudfront-viewer-metro-code"]],
|
|
1137
|
-
// TLS fingerprints can only be computed where TLS terminates. When a CDN
|
|
1138
|
-
// terminates TLS in front of the merchant, its fingerprint headers ARE the
|
|
1139
|
-
// connection-layer truth — normalising them here is the permanent design.
|
|
1140
|
-
// CloudFront delivers JA3/JA4 only via an origin request policy and only
|
|
1141
|
-
// for HTTPS viewer connections.
|
|
1142
|
-
["tlsFingerprint", ["cf-ja3-hash", "cloudfront-viewer-ja3-fingerprint"]],
|
|
1143
|
-
["ja4", ["cf-ja4", "cloudfront-viewer-ja4-fingerprint"]],
|
|
1144
|
-
// Header-structure fingerprint: browser/SDK-distinctive header ordering.
|
|
1145
|
-
["headerOrder", ["cloudfront-viewer-header-order"]],
|
|
1146
|
-
["headerCount", ["cloudfront-viewer-header-count"]]
|
|
1147
|
-
];
|
|
1148
|
-
var MAX_CONNECTION_VALUE_CHARS = 256;
|
|
1149
|
-
function deriveConnectionFromHeaders(headers) {
|
|
1150
|
-
if (!headers) return void 0;
|
|
1151
|
-
const lower = {};
|
|
1152
|
-
for (const name of Object.keys(headers)) {
|
|
1153
|
-
const value = headers[name];
|
|
1154
|
-
if (typeof value === "string" && value.length > 0) lower[name.toLowerCase()] = value;
|
|
1155
|
-
}
|
|
1156
|
-
const connection = {};
|
|
1157
|
-
for (const [key, sources] of CONNECTION_HEADER_SOURCES) {
|
|
1158
|
-
for (const source of sources) {
|
|
1159
|
-
const value = lower[source]?.trim();
|
|
1160
|
-
if (value) {
|
|
1161
|
-
connection[key] = value.slice(0, MAX_CONNECTION_VALUE_CHARS);
|
|
1162
|
-
break;
|
|
1163
|
-
}
|
|
1397
|
+
// src/route-pattern.ts
|
|
1398
|
+
function routePatternToRegExp(pattern, opts = {}) {
|
|
1399
|
+
let source = "";
|
|
1400
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
1401
|
+
const ch = pattern[i];
|
|
1402
|
+
if (ch === "*") {
|
|
1403
|
+
source += ".*";
|
|
1404
|
+
} else if (ch === ":" && /[A-Za-z_]/.test(pattern[i + 1] ?? "") && (i === 0 || pattern[i - 1] === "/")) {
|
|
1405
|
+
let j = i + 1;
|
|
1406
|
+
while (j < pattern.length && /[A-Za-z0-9_]/.test(pattern[j])) j++;
|
|
1407
|
+
source += "[^/]+";
|
|
1408
|
+
i = j - 1;
|
|
1409
|
+
} else {
|
|
1410
|
+
source += ch.replace(/[.+?^${}()|[\]\\/]/g, "\\$&");
|
|
1164
1411
|
}
|
|
1165
1412
|
}
|
|
1166
|
-
{
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
if (!connection.ip && addr) connection.ip = addr.replace(/:\d+$/, "");
|
|
1171
|
-
}
|
|
1172
|
-
if (!connection.ip) {
|
|
1173
|
-
const xff = lower["x-forwarded-for"];
|
|
1174
|
-
const client = xff?.split(",")[0]?.trim();
|
|
1175
|
-
if (client) connection.ip = client;
|
|
1176
|
-
}
|
|
1177
|
-
if (!connection.tlsCipher) {
|
|
1178
|
-
const cipher = connection.tlsVersion?.split(":")[1];
|
|
1179
|
-
if (cipher) connection.tlsCipher = cipher;
|
|
1413
|
+
try {
|
|
1414
|
+
return new RegExp(`^${source}$`, opts.caseInsensitive ? "i" : "");
|
|
1415
|
+
} catch {
|
|
1416
|
+
return null;
|
|
1180
1417
|
}
|
|
1181
|
-
return Object.keys(connection).length > 0 ? connection : void 0;
|
|
1182
1418
|
}
|
|
1183
|
-
function
|
|
1184
|
-
|
|
1185
|
-
const flat = {};
|
|
1186
|
-
if (rawHeaders) {
|
|
1187
|
-
for (const name of Object.keys(rawHeaders)) {
|
|
1188
|
-
const value = rawHeaders[name];
|
|
1189
|
-
if (value === void 0) continue;
|
|
1190
|
-
flat[name.toLowerCase()] = Array.isArray(value) ? value.join(", ") : String(value);
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
const connection = deriveConnectionFromHeaders(flat);
|
|
1194
|
-
return {
|
|
1195
|
-
headers,
|
|
1196
|
-
...apiKeyFormat && { apiKeyFormat },
|
|
1197
|
-
...platformHeaders && { platformHeaders },
|
|
1198
|
-
...connection && { connection },
|
|
1199
|
-
...cookieCount !== void 0 && { cookieCount },
|
|
1200
|
-
schemaVersion: CAPTURE_SCHEMA_VERSION
|
|
1201
|
-
};
|
|
1419
|
+
function matchRoutePattern(pattern, path, opts = {}) {
|
|
1420
|
+
return routePatternToRegExp(pattern, opts)?.test(path) ?? false;
|
|
1202
1421
|
}
|
|
1203
1422
|
|
|
1204
1423
|
// src/edge-config.ts
|
|
@@ -1233,6 +1452,7 @@ function isEdgeConfig(value) {
|
|
|
1233
1452
|
if (r.sampling !== void 0 && !isSamplingShape(r.sampling)) return false;
|
|
1234
1453
|
}
|
|
1235
1454
|
if (!isSamplingShape(v.sampling)) return false;
|
|
1455
|
+
if (v.minTls !== void 0 && v.minTls !== "1.2" && v.minTls !== "1.3") return false;
|
|
1236
1456
|
if (v.queryValueAllowlist !== void 0) {
|
|
1237
1457
|
if (!Array.isArray(v.queryValueAllowlist) || v.queryValueAllowlist.length > 16 || !v.queryValueAllowlist.every((e) => typeof e === "string" && e.length > 0 && e.length <= 64)) {
|
|
1238
1458
|
return false;
|
|
@@ -1483,6 +1703,11 @@ async function withBudget(promise, ms) {
|
|
|
1483
1703
|
if (timer) clearTimeout(timer);
|
|
1484
1704
|
}
|
|
1485
1705
|
}
|
|
1706
|
+
var CONTAINER_ID = (() => {
|
|
1707
|
+
const c = globalThis.crypto;
|
|
1708
|
+
if (c?.randomUUID) return c.randomUUID();
|
|
1709
|
+
return `c-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;
|
|
1710
|
+
})();
|
|
1486
1711
|
var destKeyOf = (d) => `${d.apiBaseUrl}\0${d.apiKey ?? ""}\0${d.counterparty}\0${d.adapter ?? ""}`;
|
|
1487
1712
|
var BeaconBuffer = class {
|
|
1488
1713
|
constructor(options = {}) {
|
|
@@ -1490,6 +1715,7 @@ var BeaconBuffer = class {
|
|
|
1490
1715
|
this.droppedSinceLast = 0;
|
|
1491
1716
|
this.droppedTotal = 0;
|
|
1492
1717
|
this.deliveredTotal = 0;
|
|
1718
|
+
this.enqueuedTotal = 0;
|
|
1493
1719
|
this.backoffUntil = 0;
|
|
1494
1720
|
this.collapse = /* @__PURE__ */ new Map();
|
|
1495
1721
|
/** Keys the backend answered 401 for — later batches go unauthenticated. */
|
|
@@ -1502,13 +1728,16 @@ var BeaconBuffer = class {
|
|
|
1502
1728
|
collapseMaxKeys: options.collapseMaxKeys ?? 1e3,
|
|
1503
1729
|
requestTimeoutMs: options.requestTimeoutMs ?? 1e4,
|
|
1504
1730
|
retryBackoffMs: options.retryBackoffMs ?? 1e3,
|
|
1731
|
+
holdRepeats: options.holdRepeats ?? false,
|
|
1732
|
+
containerId: options.containerId ?? CONTAINER_ID,
|
|
1505
1733
|
now: options.now ?? Date.now
|
|
1506
1734
|
};
|
|
1507
1735
|
}
|
|
1508
1736
|
/**
|
|
1509
1737
|
* Queue one beacon. With a `collapseKey`, a repeat inside the window is
|
|
1510
|
-
* merged into
|
|
1511
|
-
*
|
|
1738
|
+
* merged into this key's item while that item is still queued (not yet in
|
|
1739
|
+
* flight). Once it has left, the repeat becomes a new item, sendable on
|
|
1740
|
+
* this flush — nothing waits for a later invocation (round 27.6).
|
|
1512
1741
|
*/
|
|
1513
1742
|
enqueue(dest, item, collapseKey) {
|
|
1514
1743
|
const now = this.opts.now();
|
|
@@ -1521,12 +1750,15 @@ var BeaconBuffer = class {
|
|
|
1521
1750
|
return;
|
|
1522
1751
|
}
|
|
1523
1752
|
}
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1753
|
+
if (this.opts.holdRepeats) {
|
|
1754
|
+
entry.held = this.push(
|
|
1755
|
+
dest,
|
|
1756
|
+
{ ...item, repeatCount: 1 },
|
|
1757
|
+
entry.firstSeenAt + this.opts.collapseWindowMs
|
|
1758
|
+
);
|
|
1759
|
+
return;
|
|
1760
|
+
}
|
|
1761
|
+
entry.first = this.push(dest, { ...item, repeatCount: 1 }, now);
|
|
1530
1762
|
return;
|
|
1531
1763
|
}
|
|
1532
1764
|
this.collapse.delete(collapseKey);
|
|
@@ -1562,6 +1794,7 @@ var BeaconBuffer = class {
|
|
|
1562
1794
|
droppedSinceLast: this.droppedSinceLast,
|
|
1563
1795
|
droppedTotal: this.droppedTotal,
|
|
1564
1796
|
deliveredTotal: this.deliveredTotal,
|
|
1797
|
+
enqueuedTotal: this.enqueuedTotal,
|
|
1565
1798
|
flushing: this.inFlight !== void 0
|
|
1566
1799
|
};
|
|
1567
1800
|
}
|
|
@@ -1576,6 +1809,7 @@ var BeaconBuffer = class {
|
|
|
1576
1809
|
inFlight: false
|
|
1577
1810
|
};
|
|
1578
1811
|
this.queue.push(entry);
|
|
1812
|
+
this.enqueuedTotal += 1;
|
|
1579
1813
|
return entry;
|
|
1580
1814
|
}
|
|
1581
1815
|
dropOldest() {
|
|
@@ -1621,7 +1855,11 @@ var BeaconBuffer = class {
|
|
|
1621
1855
|
droppedSinceLast: reportedDropped,
|
|
1622
1856
|
queueDepth: this.queue.length,
|
|
1623
1857
|
adapter: dest.adapter ?? "edge-core",
|
|
1624
|
-
version: SDK_VERSION
|
|
1858
|
+
version: SDK_VERSION,
|
|
1859
|
+
containerId: this.opts.containerId,
|
|
1860
|
+
enqueuedTotal: this.enqueuedTotal,
|
|
1861
|
+
deliveredTotal: this.deliveredTotal,
|
|
1862
|
+
droppedTotal: this.droppedTotal
|
|
1625
1863
|
};
|
|
1626
1864
|
const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
|
|
1627
1865
|
const response = await withBudget(
|
|
@@ -1930,9 +2168,7 @@ function extractCredentialsFromNextRequest(request) {
|
|
|
1930
2168
|
return credentials;
|
|
1931
2169
|
}
|
|
1932
2170
|
function matchRoute(pattern, path) {
|
|
1933
|
-
|
|
1934
|
-
const regex = new RegExp(`^${regexPattern}$`);
|
|
1935
|
-
return regex.test(path);
|
|
2171
|
+
return matchRoutePattern(pattern, path);
|
|
1936
2172
|
}
|
|
1937
2173
|
function findRouteConfig(routes, path, method) {
|
|
1938
2174
|
return routes.find((route) => {
|
|
@@ -2123,7 +2359,8 @@ function createMiddleware(options) {
|
|
|
2123
2359
|
skipPaths = [],
|
|
2124
2360
|
showInterstitial,
|
|
2125
2361
|
showCommerceShield,
|
|
2126
|
-
|
|
2362
|
+
// Round 27.8: opt-in only (the merchant turns it on); was default-on.
|
|
2363
|
+
enableRuntimeChallenge = false,
|
|
2127
2364
|
routesRefreshMs = DEFAULT_ROUTES_REFRESH_MS,
|
|
2128
2365
|
anonymousBeacon = true,
|
|
2129
2366
|
trustVerifiedHop = false,
|
|
@@ -2260,8 +2497,10 @@ function createMiddleware(options) {
|
|
|
2260
2497
|
const originalClientIp = forwardedFor?.split(",")[0]?.trim();
|
|
2261
2498
|
const attemptHeader = request.headers.get("x-astra-attempt-id");
|
|
2262
2499
|
const inboundAttemptId = attemptHeader && /^att_[0-9a-f]{32}$/.test(attemptHeader) ? attemptHeader : void 0;
|
|
2500
|
+
const commerceArtifacts = await detectFetchCommerce(request);
|
|
2263
2501
|
const verificationRequest = {
|
|
2264
2502
|
credentials,
|
|
2503
|
+
...commerceArtifacts && { commerceArtifacts },
|
|
2265
2504
|
purpose,
|
|
2266
2505
|
action: pdlssPair.action,
|
|
2267
2506
|
resource: pathname,
|