@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.mjs
CHANGED
|
@@ -1,5 +1,419 @@
|
|
|
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)) {
|
|
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
|
+
|
|
1
415
|
// src/version.ts
|
|
2
|
-
var SDK_VERSION = "5.
|
|
416
|
+
var SDK_VERSION = "5.15.0";
|
|
3
417
|
|
|
4
418
|
// src/http.ts
|
|
5
419
|
var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
|
|
@@ -463,6 +877,7 @@ async function callVerifyAccessAPI(config, request) {
|
|
|
463
877
|
if (requestData.invocationProtocol) body.invocationProtocol = requestData.invocationProtocol;
|
|
464
878
|
if (requestData.commercePhase) body.commercePhase = requestData.commercePhase;
|
|
465
879
|
if (requestData.checkoutSessionId) body.checkoutSessionId = requestData.checkoutSessionId;
|
|
880
|
+
if (requestData.checkoutJwt) body.checkoutJwt = requestData.checkoutJwt;
|
|
466
881
|
if (requestData.checkoutItems) body.checkoutItems = requestData.checkoutItems;
|
|
467
882
|
if (requestData.buyerEmail) body.buyerEmail = requestData.buyerEmail;
|
|
468
883
|
if (requestData.commerceArtifacts) body.commerceArtifacts = requestData.commerceArtifacts;
|
|
@@ -613,7 +1028,8 @@ async function verify(config, request, options) {
|
|
|
613
1028
|
"[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
1029
|
);
|
|
615
1030
|
}
|
|
616
|
-
|
|
1031
|
+
const uncacheable = Boolean(request.commerceArtifacts);
|
|
1032
|
+
if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache && !uncacheable) {
|
|
617
1033
|
const cached = getCachedResult(request, mergedConfig.counterpartyId);
|
|
618
1034
|
if (cached) {
|
|
619
1035
|
if (mergedConfig.debug) {
|
|
@@ -694,7 +1110,10 @@ async function verify(config, request, options) {
|
|
|
694
1110
|
// MERCHANT caller only (bridge callers strip it before the agent plane).
|
|
695
1111
|
settlementToken: apiResponse.settlementToken,
|
|
696
1112
|
// 5.6.0: fulfilment contact — merchant-only lane, same strip rule.
|
|
697
|
-
fulfilment: apiResponse.fulfilment
|
|
1113
|
+
fulfilment: apiResponse.fulfilment,
|
|
1114
|
+
mandates: apiResponse.mandates,
|
|
1115
|
+
kyaToken: apiResponse.kyaToken,
|
|
1116
|
+
aep: apiResponse.aep
|
|
698
1117
|
};
|
|
699
1118
|
return result2;
|
|
700
1119
|
}
|
|
@@ -704,7 +1123,10 @@ async function verify(config, request, options) {
|
|
|
704
1123
|
trustScore: apiResponse.agent.trustScore,
|
|
705
1124
|
trustLevel: getTrustLevel(apiResponse.agent.trustScore),
|
|
706
1125
|
blockchainAnchored: apiResponse.agent.blockchainStatus === "anchored",
|
|
707
|
-
status: apiResponse.agent.agentStatus
|
|
1126
|
+
status: apiResponse.agent.agentStatus,
|
|
1127
|
+
...apiResponse.agent.protocolIdentities && {
|
|
1128
|
+
protocolIdentities: apiResponse.agent.protocolIdentities
|
|
1129
|
+
}
|
|
708
1130
|
} : void 0;
|
|
709
1131
|
const developer = apiResponse.developer ? {
|
|
710
1132
|
astradId: apiResponse.developer.kyaOwnerId,
|
|
@@ -755,7 +1177,11 @@ async function verify(config, request, options) {
|
|
|
755
1177
|
settlementOutcome: apiResponse.settlementOutcome,
|
|
756
1178
|
settlementToken: apiResponse.settlementToken,
|
|
757
1179
|
// 5.6.0: fulfilment contact — merchant-only lane, same strip rule.
|
|
758
|
-
fulfilment: apiResponse.fulfilment
|
|
1180
|
+
fulfilment: apiResponse.fulfilment,
|
|
1181
|
+
// 5.15.0: merchant-lane AP2 mandates + KYAPay identity token.
|
|
1182
|
+
mandates: apiResponse.mandates,
|
|
1183
|
+
kyaToken: apiResponse.kyaToken,
|
|
1184
|
+
aep: apiResponse.aep
|
|
759
1185
|
};
|
|
760
1186
|
if (result.recommendation === "deny") {
|
|
761
1187
|
result.policyAllowed = false;
|
|
@@ -775,7 +1201,9 @@ async function verify(config, request, options) {
|
|
|
775
1201
|
}
|
|
776
1202
|
const escalates = result.requiresStepUp === true || result.requiresApproval === true || result.recommendation === "step_up_required";
|
|
777
1203
|
if (mergedConfig.cacheTtl !== 0 && result.recommendation !== "deny" && !escalates) {
|
|
778
|
-
|
|
1204
|
+
if (!uncacheable) {
|
|
1205
|
+
cacheResult(request, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);
|
|
1206
|
+
}
|
|
779
1207
|
}
|
|
780
1208
|
return result;
|
|
781
1209
|
}
|
|
@@ -929,239 +1357,30 @@ function performCounterpartyPreCheck(routeConfig, astraCreds, purpose) {
|
|
|
929
1357
|
return failures;
|
|
930
1358
|
}
|
|
931
1359
|
|
|
932
|
-
// src/
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
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
|
-
}
|
|
1360
|
+
// src/route-pattern.ts
|
|
1361
|
+
function routePatternToRegExp(pattern, opts = {}) {
|
|
1362
|
+
let source = "";
|
|
1363
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
1364
|
+
const ch = pattern[i];
|
|
1365
|
+
if (ch === "*") {
|
|
1366
|
+
source += ".*";
|
|
1367
|
+
} else if (ch === ":" && /[A-Za-z_]/.test(pattern[i + 1] ?? "") && (i === 0 || pattern[i - 1] === "/")) {
|
|
1368
|
+
let j = i + 1;
|
|
1369
|
+
while (j < pattern.length && /[A-Za-z0-9_]/.test(pattern[j])) j++;
|
|
1370
|
+
source += "[^/]+";
|
|
1371
|
+
i = j - 1;
|
|
1372
|
+
} else {
|
|
1373
|
+
source += ch.replace(/[.+?^${}()|[\]\\/]/g, "\\$&");
|
|
1127
1374
|
}
|
|
1128
1375
|
}
|
|
1129
|
-
{
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
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;
|
|
1376
|
+
try {
|
|
1377
|
+
return new RegExp(`^${source}$`, opts.caseInsensitive ? "i" : "");
|
|
1378
|
+
} catch {
|
|
1379
|
+
return null;
|
|
1143
1380
|
}
|
|
1144
|
-
return Object.keys(connection).length > 0 ? connection : void 0;
|
|
1145
1381
|
}
|
|
1146
|
-
function
|
|
1147
|
-
|
|
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
|
-
};
|
|
1382
|
+
function matchRoutePattern(pattern, path, opts = {}) {
|
|
1383
|
+
return routePatternToRegExp(pattern, opts)?.test(path) ?? false;
|
|
1165
1384
|
}
|
|
1166
1385
|
|
|
1167
1386
|
// src/edge-config.ts
|
|
@@ -1196,6 +1415,7 @@ function isEdgeConfig(value) {
|
|
|
1196
1415
|
if (r.sampling !== void 0 && !isSamplingShape(r.sampling)) return false;
|
|
1197
1416
|
}
|
|
1198
1417
|
if (!isSamplingShape(v.sampling)) return false;
|
|
1418
|
+
if (v.minTls !== void 0 && v.minTls !== "1.2" && v.minTls !== "1.3") return false;
|
|
1199
1419
|
if (v.queryValueAllowlist !== void 0) {
|
|
1200
1420
|
if (!Array.isArray(v.queryValueAllowlist) || v.queryValueAllowlist.length > 16 || !v.queryValueAllowlist.every((e) => typeof e === "string" && e.length > 0 && e.length <= 64)) {
|
|
1201
1421
|
return false;
|
|
@@ -1446,6 +1666,11 @@ async function withBudget(promise, ms) {
|
|
|
1446
1666
|
if (timer) clearTimeout(timer);
|
|
1447
1667
|
}
|
|
1448
1668
|
}
|
|
1669
|
+
var CONTAINER_ID = (() => {
|
|
1670
|
+
const c = globalThis.crypto;
|
|
1671
|
+
if (c?.randomUUID) return c.randomUUID();
|
|
1672
|
+
return `c-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;
|
|
1673
|
+
})();
|
|
1449
1674
|
var destKeyOf = (d) => `${d.apiBaseUrl}\0${d.apiKey ?? ""}\0${d.counterparty}\0${d.adapter ?? ""}`;
|
|
1450
1675
|
var BeaconBuffer = class {
|
|
1451
1676
|
constructor(options = {}) {
|
|
@@ -1453,6 +1678,7 @@ var BeaconBuffer = class {
|
|
|
1453
1678
|
this.droppedSinceLast = 0;
|
|
1454
1679
|
this.droppedTotal = 0;
|
|
1455
1680
|
this.deliveredTotal = 0;
|
|
1681
|
+
this.enqueuedTotal = 0;
|
|
1456
1682
|
this.backoffUntil = 0;
|
|
1457
1683
|
this.collapse = /* @__PURE__ */ new Map();
|
|
1458
1684
|
/** Keys the backend answered 401 for — later batches go unauthenticated. */
|
|
@@ -1465,13 +1691,16 @@ var BeaconBuffer = class {
|
|
|
1465
1691
|
collapseMaxKeys: options.collapseMaxKeys ?? 1e3,
|
|
1466
1692
|
requestTimeoutMs: options.requestTimeoutMs ?? 1e4,
|
|
1467
1693
|
retryBackoffMs: options.retryBackoffMs ?? 1e3,
|
|
1694
|
+
holdRepeats: options.holdRepeats ?? false,
|
|
1695
|
+
containerId: options.containerId ?? CONTAINER_ID,
|
|
1468
1696
|
now: options.now ?? Date.now
|
|
1469
1697
|
};
|
|
1470
1698
|
}
|
|
1471
1699
|
/**
|
|
1472
1700
|
* Queue one beacon. With a `collapseKey`, a repeat inside the window is
|
|
1473
|
-
* merged into
|
|
1474
|
-
*
|
|
1701
|
+
* merged into this key's item while that item is still queued (not yet in
|
|
1702
|
+
* flight). Once it has left, the repeat becomes a new item, sendable on
|
|
1703
|
+
* this flush — nothing waits for a later invocation (round 27.6).
|
|
1475
1704
|
*/
|
|
1476
1705
|
enqueue(dest, item, collapseKey) {
|
|
1477
1706
|
const now = this.opts.now();
|
|
@@ -1484,12 +1713,15 @@ var BeaconBuffer = class {
|
|
|
1484
1713
|
return;
|
|
1485
1714
|
}
|
|
1486
1715
|
}
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1716
|
+
if (this.opts.holdRepeats) {
|
|
1717
|
+
entry.held = this.push(
|
|
1718
|
+
dest,
|
|
1719
|
+
{ ...item, repeatCount: 1 },
|
|
1720
|
+
entry.firstSeenAt + this.opts.collapseWindowMs
|
|
1721
|
+
);
|
|
1722
|
+
return;
|
|
1723
|
+
}
|
|
1724
|
+
entry.first = this.push(dest, { ...item, repeatCount: 1 }, now);
|
|
1493
1725
|
return;
|
|
1494
1726
|
}
|
|
1495
1727
|
this.collapse.delete(collapseKey);
|
|
@@ -1525,6 +1757,7 @@ var BeaconBuffer = class {
|
|
|
1525
1757
|
droppedSinceLast: this.droppedSinceLast,
|
|
1526
1758
|
droppedTotal: this.droppedTotal,
|
|
1527
1759
|
deliveredTotal: this.deliveredTotal,
|
|
1760
|
+
enqueuedTotal: this.enqueuedTotal,
|
|
1528
1761
|
flushing: this.inFlight !== void 0
|
|
1529
1762
|
};
|
|
1530
1763
|
}
|
|
@@ -1539,6 +1772,7 @@ var BeaconBuffer = class {
|
|
|
1539
1772
|
inFlight: false
|
|
1540
1773
|
};
|
|
1541
1774
|
this.queue.push(entry);
|
|
1775
|
+
this.enqueuedTotal += 1;
|
|
1542
1776
|
return entry;
|
|
1543
1777
|
}
|
|
1544
1778
|
dropOldest() {
|
|
@@ -1584,7 +1818,11 @@ var BeaconBuffer = class {
|
|
|
1584
1818
|
droppedSinceLast: reportedDropped,
|
|
1585
1819
|
queueDepth: this.queue.length,
|
|
1586
1820
|
adapter: dest.adapter ?? "edge-core",
|
|
1587
|
-
version: SDK_VERSION
|
|
1821
|
+
version: SDK_VERSION,
|
|
1822
|
+
containerId: this.opts.containerId,
|
|
1823
|
+
enqueuedTotal: this.enqueuedTotal,
|
|
1824
|
+
deliveredTotal: this.deliveredTotal,
|
|
1825
|
+
droppedTotal: this.droppedTotal
|
|
1588
1826
|
};
|
|
1589
1827
|
const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
|
|
1590
1828
|
const response = await withBudget(
|
|
@@ -1893,9 +2131,7 @@ function extractCredentialsFromNextRequest(request) {
|
|
|
1893
2131
|
return credentials;
|
|
1894
2132
|
}
|
|
1895
2133
|
function matchRoute(pattern, path) {
|
|
1896
|
-
|
|
1897
|
-
const regex = new RegExp(`^${regexPattern}$`);
|
|
1898
|
-
return regex.test(path);
|
|
2134
|
+
return matchRoutePattern(pattern, path);
|
|
1899
2135
|
}
|
|
1900
2136
|
function findRouteConfig(routes, path, method) {
|
|
1901
2137
|
return routes.find((route) => {
|
|
@@ -2086,7 +2322,8 @@ function createMiddleware(options) {
|
|
|
2086
2322
|
skipPaths = [],
|
|
2087
2323
|
showInterstitial,
|
|
2088
2324
|
showCommerceShield,
|
|
2089
|
-
|
|
2325
|
+
// Round 27.8: opt-in only (the merchant turns it on); was default-on.
|
|
2326
|
+
enableRuntimeChallenge = false,
|
|
2090
2327
|
routesRefreshMs = DEFAULT_ROUTES_REFRESH_MS,
|
|
2091
2328
|
anonymousBeacon = true,
|
|
2092
2329
|
trustVerifiedHop = false,
|
|
@@ -2223,8 +2460,10 @@ function createMiddleware(options) {
|
|
|
2223
2460
|
const originalClientIp = forwardedFor?.split(",")[0]?.trim();
|
|
2224
2461
|
const attemptHeader = request.headers.get("x-astra-attempt-id");
|
|
2225
2462
|
const inboundAttemptId = attemptHeader && /^att_[0-9a-f]{32}$/.test(attemptHeader) ? attemptHeader : void 0;
|
|
2463
|
+
const commerceArtifacts = await detectFetchCommerce(request);
|
|
2226
2464
|
const verificationRequest = {
|
|
2227
2465
|
credentials,
|
|
2466
|
+
...commerceArtifacts && { commerceArtifacts },
|
|
2228
2467
|
purpose,
|
|
2229
2468
|
action: pdlssPair.action,
|
|
2230
2469
|
resource: pathname,
|