@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
|
@@ -1,5 +1,437 @@
|
|
|
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 lowerHeaders(headers) {
|
|
388
|
+
const out = {};
|
|
389
|
+
if (typeof Headers !== "undefined" && headers instanceof Headers) {
|
|
390
|
+
headers.forEach((v, k) => out[k.toLowerCase()] = v);
|
|
391
|
+
return out;
|
|
392
|
+
}
|
|
393
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
394
|
+
if (v === void 0) continue;
|
|
395
|
+
out[k.toLowerCase()] = Array.isArray(v) ? v.join(", ") : v;
|
|
396
|
+
}
|
|
397
|
+
return out;
|
|
398
|
+
}
|
|
399
|
+
function detect(req, host) {
|
|
400
|
+
try {
|
|
401
|
+
return detectCommerce(req, host)?.artifacts;
|
|
402
|
+
} catch {
|
|
403
|
+
return void 0;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
function detectExpressCommerce(req) {
|
|
407
|
+
const headers = lowerHeaders(req.headers);
|
|
408
|
+
const [uri, query = ""] = (req.originalUrl ?? req.url).split("?");
|
|
409
|
+
let bodyText;
|
|
410
|
+
if (req.rawBody !== void 0) {
|
|
411
|
+
bodyText = typeof req.rawBody === "string" ? req.rawBody : req.rawBody.toString("utf8");
|
|
412
|
+
} else if (typeof req.body === "string") {
|
|
413
|
+
bodyText = req.body;
|
|
414
|
+
} else if (req.body && typeof req.body === "object" && Object.keys(req.body).length > 0) {
|
|
415
|
+
bodyText = JSON.stringify(req.body);
|
|
416
|
+
}
|
|
417
|
+
const truncated = bodyText !== void 0 && bodyText.length > MAX_BODY_BYTES;
|
|
418
|
+
return detect(
|
|
419
|
+
{
|
|
420
|
+
method: req.method,
|
|
421
|
+
uri,
|
|
422
|
+
querystring: query,
|
|
423
|
+
headers,
|
|
424
|
+
...bodyText !== void 0 && {
|
|
425
|
+
bodyText: truncated ? bodyText.slice(0, MAX_BODY_BYTES) : bodyText,
|
|
426
|
+
bodyTruncated: truncated
|
|
427
|
+
}
|
|
428
|
+
},
|
|
429
|
+
headers["x-forwarded-host"] ?? headers.host ?? "localhost"
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
|
|
1
433
|
// src/version.ts
|
|
2
|
-
var SDK_VERSION = "5.
|
|
434
|
+
var SDK_VERSION = "5.15.0";
|
|
3
435
|
|
|
4
436
|
// src/http.ts
|
|
5
437
|
var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
|
|
@@ -515,6 +947,7 @@ async function callVerifyAccessAPI(config, request) {
|
|
|
515
947
|
if (requestData.invocationProtocol) body.invocationProtocol = requestData.invocationProtocol;
|
|
516
948
|
if (requestData.commercePhase) body.commercePhase = requestData.commercePhase;
|
|
517
949
|
if (requestData.checkoutSessionId) body.checkoutSessionId = requestData.checkoutSessionId;
|
|
950
|
+
if (requestData.checkoutJwt) body.checkoutJwt = requestData.checkoutJwt;
|
|
518
951
|
if (requestData.checkoutItems) body.checkoutItems = requestData.checkoutItems;
|
|
519
952
|
if (requestData.buyerEmail) body.buyerEmail = requestData.buyerEmail;
|
|
520
953
|
if (requestData.commerceArtifacts) body.commerceArtifacts = requestData.commerceArtifacts;
|
|
@@ -665,7 +1098,8 @@ async function verify(config, request, options) {
|
|
|
665
1098
|
"[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."
|
|
666
1099
|
);
|
|
667
1100
|
}
|
|
668
|
-
|
|
1101
|
+
const uncacheable = Boolean(request.commerceArtifacts);
|
|
1102
|
+
if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache && !uncacheable) {
|
|
669
1103
|
const cached = getCachedResult(request, mergedConfig.counterpartyId);
|
|
670
1104
|
if (cached) {
|
|
671
1105
|
if (mergedConfig.debug) {
|
|
@@ -746,7 +1180,10 @@ async function verify(config, request, options) {
|
|
|
746
1180
|
// MERCHANT caller only (bridge callers strip it before the agent plane).
|
|
747
1181
|
settlementToken: apiResponse.settlementToken,
|
|
748
1182
|
// 5.6.0: fulfilment contact — merchant-only lane, same strip rule.
|
|
749
|
-
fulfilment: apiResponse.fulfilment
|
|
1183
|
+
fulfilment: apiResponse.fulfilment,
|
|
1184
|
+
mandates: apiResponse.mandates,
|
|
1185
|
+
kyaToken: apiResponse.kyaToken,
|
|
1186
|
+
aep: apiResponse.aep
|
|
750
1187
|
};
|
|
751
1188
|
return result2;
|
|
752
1189
|
}
|
|
@@ -756,7 +1193,10 @@ async function verify(config, request, options) {
|
|
|
756
1193
|
trustScore: apiResponse.agent.trustScore,
|
|
757
1194
|
trustLevel: getTrustLevel(apiResponse.agent.trustScore),
|
|
758
1195
|
blockchainAnchored: apiResponse.agent.blockchainStatus === "anchored",
|
|
759
|
-
status: apiResponse.agent.agentStatus
|
|
1196
|
+
status: apiResponse.agent.agentStatus,
|
|
1197
|
+
...apiResponse.agent.protocolIdentities && {
|
|
1198
|
+
protocolIdentities: apiResponse.agent.protocolIdentities
|
|
1199
|
+
}
|
|
760
1200
|
} : void 0;
|
|
761
1201
|
const developer = apiResponse.developer ? {
|
|
762
1202
|
astradId: apiResponse.developer.kyaOwnerId,
|
|
@@ -807,7 +1247,11 @@ async function verify(config, request, options) {
|
|
|
807
1247
|
settlementOutcome: apiResponse.settlementOutcome,
|
|
808
1248
|
settlementToken: apiResponse.settlementToken,
|
|
809
1249
|
// 5.6.0: fulfilment contact — merchant-only lane, same strip rule.
|
|
810
|
-
fulfilment: apiResponse.fulfilment
|
|
1250
|
+
fulfilment: apiResponse.fulfilment,
|
|
1251
|
+
// 5.15.0: merchant-lane AP2 mandates + KYAPay identity token.
|
|
1252
|
+
mandates: apiResponse.mandates,
|
|
1253
|
+
kyaToken: apiResponse.kyaToken,
|
|
1254
|
+
aep: apiResponse.aep
|
|
811
1255
|
};
|
|
812
1256
|
if (result.recommendation === "deny") {
|
|
813
1257
|
result.policyAllowed = false;
|
|
@@ -827,7 +1271,9 @@ async function verify(config, request, options) {
|
|
|
827
1271
|
}
|
|
828
1272
|
const escalates = result.requiresStepUp === true || result.requiresApproval === true || result.recommendation === "step_up_required";
|
|
829
1273
|
if (mergedConfig.cacheTtl !== 0 && result.recommendation !== "deny" && !escalates) {
|
|
830
|
-
|
|
1274
|
+
if (!uncacheable) {
|
|
1275
|
+
cacheResult(request, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);
|
|
1276
|
+
}
|
|
831
1277
|
}
|
|
832
1278
|
return result;
|
|
833
1279
|
}
|
|
@@ -1111,239 +1557,30 @@ function performCounterpartyPreCheck(routeConfig, astraCreds, purpose) {
|
|
|
1111
1557
|
return failures;
|
|
1112
1558
|
}
|
|
1113
1559
|
|
|
1114
|
-
// src/
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
]);
|
|
1128
|
-
var SECRET_HEADER_PREFIXES = ["x-astrasync-", "kya-", "kya_"];
|
|
1129
|
-
var CREDENTIAL_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
1130
|
-
"authorization",
|
|
1131
|
-
"x-api-key",
|
|
1132
|
-
"x-goog-api-key",
|
|
1133
|
-
"api-key"
|
|
1134
|
-
]);
|
|
1135
|
-
function isSignatureHeader(name) {
|
|
1136
|
-
return name === "signature" || name.endsWith("-signature") || name.endsWith("-signature-256");
|
|
1137
|
-
}
|
|
1138
|
-
var PLATFORM_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
1139
|
-
"openai-organization",
|
|
1140
|
-
"openai-project",
|
|
1141
|
-
"openai-version",
|
|
1142
|
-
"openai-processing-ms",
|
|
1143
|
-
"anthropic-version",
|
|
1144
|
-
"anthropic-beta",
|
|
1145
|
-
"x-goog-user-project",
|
|
1146
|
-
"x-goog-api-client",
|
|
1147
|
-
"user-agent",
|
|
1148
|
-
"x-agent-card-url",
|
|
1149
|
-
"x-agent-provider"
|
|
1150
|
-
]);
|
|
1151
|
-
var PLATFORM_HEADER_PREFIXES = [
|
|
1152
|
-
"x-goog-",
|
|
1153
|
-
"x-stainless-",
|
|
1154
|
-
"x-anthropic-",
|
|
1155
|
-
"x-openai-",
|
|
1156
|
-
"sec-ch-"
|
|
1157
|
-
];
|
|
1158
|
-
var KEY_FORMAT_PREFIXES = [
|
|
1159
|
-
"sk-ant-api03",
|
|
1160
|
-
"sk-ant-api",
|
|
1161
|
-
"sk-ant",
|
|
1162
|
-
"sk-proj",
|
|
1163
|
-
"sk-svcacct",
|
|
1164
|
-
"sk-or-v1",
|
|
1165
|
-
"sk-or",
|
|
1166
|
-
"sk-lf",
|
|
1167
|
-
"sk-",
|
|
1168
|
-
"gsk_",
|
|
1169
|
-
"aiza",
|
|
1170
|
-
"ya29",
|
|
1171
|
-
"ghp_",
|
|
1172
|
-
"gho_",
|
|
1173
|
-
"github_pat_",
|
|
1174
|
-
"xai-",
|
|
1175
|
-
"pplx-",
|
|
1176
|
-
"r8_",
|
|
1177
|
-
"hf_",
|
|
1178
|
-
"astra-",
|
|
1179
|
-
"astrae-"
|
|
1180
|
-
];
|
|
1181
|
-
var MAX_HEADERS = 64;
|
|
1182
|
-
var MAX_HEADER_VALUE_BYTES = 1024;
|
|
1183
|
-
var MAX_TOTAL_BYTES = 16 * 1024;
|
|
1184
|
-
function byteLength(s) {
|
|
1185
|
-
let bytes = 0;
|
|
1186
|
-
for (let i = 0; i < s.length; i++) {
|
|
1187
|
-
const code = s.charCodeAt(i);
|
|
1188
|
-
if (code < 128) bytes += 1;
|
|
1189
|
-
else if (code < 2048) bytes += 2;
|
|
1190
|
-
else if (code >= 55296 && code <= 56319) {
|
|
1191
|
-
bytes += 4;
|
|
1192
|
-
i++;
|
|
1193
|
-
} else bytes += 3;
|
|
1194
|
-
}
|
|
1195
|
-
return bytes;
|
|
1196
|
-
}
|
|
1197
|
-
function extractApiKeyFormat(rawValue) {
|
|
1198
|
-
if (!rawValue) return void 0;
|
|
1199
|
-
let value = rawValue.trim();
|
|
1200
|
-
const spaceIdx = value.indexOf(" ");
|
|
1201
|
-
if (spaceIdx > 0) {
|
|
1202
|
-
const scheme = value.slice(0, spaceIdx).toLowerCase();
|
|
1203
|
-
if (scheme === "bearer" || scheme === "basic" || scheme === "token") {
|
|
1204
|
-
value = value.slice(spaceIdx + 1).trim();
|
|
1205
|
-
}
|
|
1206
|
-
}
|
|
1207
|
-
const lower = value.toLowerCase();
|
|
1208
|
-
for (const prefix of KEY_FORMAT_PREFIXES) {
|
|
1209
|
-
if (lower.startsWith(prefix)) {
|
|
1210
|
-
return prefix;
|
|
1211
|
-
}
|
|
1212
|
-
}
|
|
1213
|
-
return void 0;
|
|
1214
|
-
}
|
|
1215
|
-
function isSecretHeader(name) {
|
|
1216
|
-
if (SECRET_HEADER_NAMES.has(name)) return true;
|
|
1217
|
-
if (SECRET_HEADER_PREFIXES.some((p) => name.startsWith(p))) return true;
|
|
1218
|
-
if (isSignatureHeader(name)) return true;
|
|
1219
|
-
return false;
|
|
1220
|
-
}
|
|
1221
|
-
function isPlatformHeader(name) {
|
|
1222
|
-
if (PLATFORM_HEADER_NAMES.has(name)) return true;
|
|
1223
|
-
return PLATFORM_HEADER_PREFIXES.some((p) => name.startsWith(p));
|
|
1224
|
-
}
|
|
1225
|
-
function sanitizeHeaders(raw) {
|
|
1226
|
-
const headers = {};
|
|
1227
|
-
const platformHeaders = {};
|
|
1228
|
-
let apiKeyFormat;
|
|
1229
|
-
let cookieCount;
|
|
1230
|
-
let totalBytes = 0;
|
|
1231
|
-
let count = 0;
|
|
1232
|
-
if (!raw) return { headers };
|
|
1233
|
-
for (const rawName of Object.keys(raw)) {
|
|
1234
|
-
if (count >= MAX_HEADERS) break;
|
|
1235
|
-
const name = rawName.toLowerCase();
|
|
1236
|
-
const rawValue = raw[rawName];
|
|
1237
|
-
if (rawValue === void 0) continue;
|
|
1238
|
-
const joined = Array.isArray(rawValue) ? rawValue.join(", ") : String(rawValue);
|
|
1239
|
-
if (CREDENTIAL_HEADER_NAMES.has(name)) {
|
|
1240
|
-
const fmt = extractApiKeyFormat(joined);
|
|
1241
|
-
if (fmt && !apiKeyFormat) apiKeyFormat = fmt;
|
|
1242
|
-
continue;
|
|
1243
|
-
}
|
|
1244
|
-
if (name === "cookie") {
|
|
1245
|
-
cookieCount = joined.split(";").filter((s) => s.trim().length > 0).length;
|
|
1246
|
-
}
|
|
1247
|
-
if (isSecretHeader(name)) continue;
|
|
1248
|
-
let value = joined;
|
|
1249
|
-
if (byteLength(value) > MAX_HEADER_VALUE_BYTES) {
|
|
1250
|
-
value = value.slice(0, MAX_HEADER_VALUE_BYTES);
|
|
1251
|
-
}
|
|
1252
|
-
const entryBytes = byteLength(name) + byteLength(value);
|
|
1253
|
-
if (totalBytes + entryBytes > MAX_TOTAL_BYTES) continue;
|
|
1254
|
-
totalBytes += entryBytes;
|
|
1255
|
-
count++;
|
|
1256
|
-
headers[name] = value;
|
|
1257
|
-
if (isPlatformHeader(name)) platformHeaders[name] = value;
|
|
1258
|
-
}
|
|
1259
|
-
const result = { headers };
|
|
1260
|
-
if (apiKeyFormat) result.apiKeyFormat = apiKeyFormat;
|
|
1261
|
-
if (Object.keys(platformHeaders).length > 0) result.platformHeaders = platformHeaders;
|
|
1262
|
-
if (cookieCount !== void 0) result.cookieCount = cookieCount;
|
|
1263
|
-
return result;
|
|
1264
|
-
}
|
|
1265
|
-
var CONNECTION_HEADER_SOURCES = [
|
|
1266
|
-
["ip", ["cf-connecting-ip", "fastly-client-ip", "true-client-ip", "x-real-ip"]],
|
|
1267
|
-
[
|
|
1268
|
-
"country",
|
|
1269
|
-
["cf-ipcountry", "x-vercel-ip-country", "cloudfront-viewer-country", "fastly-geo-country"]
|
|
1270
|
-
],
|
|
1271
|
-
["region", ["x-vercel-ip-country-region", "cloudfront-viewer-country-region"]],
|
|
1272
|
-
["countryRegionName", ["cloudfront-viewer-country-region-name"]],
|
|
1273
|
-
["city", ["x-vercel-ip-city", "cloudfront-viewer-city"]],
|
|
1274
|
-
["latitude", ["x-vercel-ip-latitude", "cloudfront-viewer-latitude"]],
|
|
1275
|
-
["longitude", ["x-vercel-ip-longitude", "cloudfront-viewer-longitude"]],
|
|
1276
|
-
["postalCode", ["x-vercel-ip-postal-code", "cloudfront-viewer-postal-code"]],
|
|
1277
|
-
["asn", ["cloudfront-viewer-asn"]],
|
|
1278
|
-
["tlsVersion", ["cloudfront-viewer-tls"]],
|
|
1279
|
-
["httpVersion", ["cloudfront-viewer-http-version"]],
|
|
1280
|
-
["timeZone", ["x-vercel-ip-timezone", "cloudfront-viewer-time-zone"]],
|
|
1281
|
-
["metroCode", ["cloudfront-viewer-metro-code"]],
|
|
1282
|
-
// TLS fingerprints can only be computed where TLS terminates. When a CDN
|
|
1283
|
-
// terminates TLS in front of the merchant, its fingerprint headers ARE the
|
|
1284
|
-
// connection-layer truth — normalising them here is the permanent design.
|
|
1285
|
-
// CloudFront delivers JA3/JA4 only via an origin request policy and only
|
|
1286
|
-
// for HTTPS viewer connections.
|
|
1287
|
-
["tlsFingerprint", ["cf-ja3-hash", "cloudfront-viewer-ja3-fingerprint"]],
|
|
1288
|
-
["ja4", ["cf-ja4", "cloudfront-viewer-ja4-fingerprint"]],
|
|
1289
|
-
// Header-structure fingerprint: browser/SDK-distinctive header ordering.
|
|
1290
|
-
["headerOrder", ["cloudfront-viewer-header-order"]],
|
|
1291
|
-
["headerCount", ["cloudfront-viewer-header-count"]]
|
|
1292
|
-
];
|
|
1293
|
-
var MAX_CONNECTION_VALUE_CHARS = 256;
|
|
1294
|
-
function deriveConnectionFromHeaders(headers) {
|
|
1295
|
-
if (!headers) return void 0;
|
|
1296
|
-
const lower = {};
|
|
1297
|
-
for (const name of Object.keys(headers)) {
|
|
1298
|
-
const value = headers[name];
|
|
1299
|
-
if (typeof value === "string" && value.length > 0) lower[name.toLowerCase()] = value;
|
|
1300
|
-
}
|
|
1301
|
-
const connection = {};
|
|
1302
|
-
for (const [key, sources] of CONNECTION_HEADER_SOURCES) {
|
|
1303
|
-
for (const source of sources) {
|
|
1304
|
-
const value = lower[source]?.trim();
|
|
1305
|
-
if (value) {
|
|
1306
|
-
connection[key] = value.slice(0, MAX_CONNECTION_VALUE_CHARS);
|
|
1307
|
-
break;
|
|
1308
|
-
}
|
|
1560
|
+
// src/route-pattern.ts
|
|
1561
|
+
function routePatternToRegExp(pattern, opts = {}) {
|
|
1562
|
+
let source = "";
|
|
1563
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
1564
|
+
const ch = pattern[i];
|
|
1565
|
+
if (ch === "*") {
|
|
1566
|
+
source += ".*";
|
|
1567
|
+
} else if (ch === ":" && /[A-Za-z_]/.test(pattern[i + 1] ?? "") && (i === 0 || pattern[i - 1] === "/")) {
|
|
1568
|
+
let j = i + 1;
|
|
1569
|
+
while (j < pattern.length && /[A-Za-z0-9_]/.test(pattern[j])) j++;
|
|
1570
|
+
source += "[^/]+";
|
|
1571
|
+
i = j - 1;
|
|
1572
|
+
} else {
|
|
1573
|
+
source += ch.replace(/[.+?^${}()|[\]\\/]/g, "\\$&");
|
|
1309
1574
|
}
|
|
1310
1575
|
}
|
|
1311
|
-
{
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
if (!connection.ip && addr) connection.ip = addr.replace(/:\d+$/, "");
|
|
1316
|
-
}
|
|
1317
|
-
if (!connection.ip) {
|
|
1318
|
-
const xff = lower["x-forwarded-for"];
|
|
1319
|
-
const client = xff?.split(",")[0]?.trim();
|
|
1320
|
-
if (client) connection.ip = client;
|
|
1321
|
-
}
|
|
1322
|
-
if (!connection.tlsCipher) {
|
|
1323
|
-
const cipher = connection.tlsVersion?.split(":")[1];
|
|
1324
|
-
if (cipher) connection.tlsCipher = cipher;
|
|
1576
|
+
try {
|
|
1577
|
+
return new RegExp(`^${source}$`, opts.caseInsensitive ? "i" : "");
|
|
1578
|
+
} catch {
|
|
1579
|
+
return null;
|
|
1325
1580
|
}
|
|
1326
|
-
return Object.keys(connection).length > 0 ? connection : void 0;
|
|
1327
1581
|
}
|
|
1328
|
-
function
|
|
1329
|
-
|
|
1330
|
-
const flat = {};
|
|
1331
|
-
if (rawHeaders) {
|
|
1332
|
-
for (const name of Object.keys(rawHeaders)) {
|
|
1333
|
-
const value = rawHeaders[name];
|
|
1334
|
-
if (value === void 0) continue;
|
|
1335
|
-
flat[name.toLowerCase()] = Array.isArray(value) ? value.join(", ") : String(value);
|
|
1336
|
-
}
|
|
1337
|
-
}
|
|
1338
|
-
const connection = deriveConnectionFromHeaders(flat);
|
|
1339
|
-
return {
|
|
1340
|
-
headers,
|
|
1341
|
-
...apiKeyFormat && { apiKeyFormat },
|
|
1342
|
-
...platformHeaders && { platformHeaders },
|
|
1343
|
-
...connection && { connection },
|
|
1344
|
-
...cookieCount !== void 0 && { cookieCount },
|
|
1345
|
-
schemaVersion: CAPTURE_SCHEMA_VERSION
|
|
1346
|
-
};
|
|
1582
|
+
function matchRoutePattern(pattern, path, opts = {}) {
|
|
1583
|
+
return routePatternToRegExp(pattern, opts)?.test(path) ?? false;
|
|
1347
1584
|
}
|
|
1348
1585
|
|
|
1349
1586
|
// src/edge-config.ts
|
|
@@ -1378,6 +1615,7 @@ function isEdgeConfig(value) {
|
|
|
1378
1615
|
if (r.sampling !== void 0 && !isSamplingShape(r.sampling)) return false;
|
|
1379
1616
|
}
|
|
1380
1617
|
if (!isSamplingShape(v.sampling)) return false;
|
|
1618
|
+
if (v.minTls !== void 0 && v.minTls !== "1.2" && v.minTls !== "1.3") return false;
|
|
1381
1619
|
if (v.queryValueAllowlist !== void 0) {
|
|
1382
1620
|
if (!Array.isArray(v.queryValueAllowlist) || v.queryValueAllowlist.length > 16 || !v.queryValueAllowlist.every((e) => typeof e === "string" && e.length > 0 && e.length <= 64)) {
|
|
1383
1621
|
return false;
|
|
@@ -1628,6 +1866,11 @@ async function withBudget(promise, ms) {
|
|
|
1628
1866
|
if (timer) clearTimeout(timer);
|
|
1629
1867
|
}
|
|
1630
1868
|
}
|
|
1869
|
+
var CONTAINER_ID = (() => {
|
|
1870
|
+
const c = globalThis.crypto;
|
|
1871
|
+
if (c?.randomUUID) return c.randomUUID();
|
|
1872
|
+
return `c-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;
|
|
1873
|
+
})();
|
|
1631
1874
|
var destKeyOf = (d) => `${d.apiBaseUrl}\0${d.apiKey ?? ""}\0${d.counterparty}\0${d.adapter ?? ""}`;
|
|
1632
1875
|
var BeaconBuffer = class {
|
|
1633
1876
|
constructor(options = {}) {
|
|
@@ -1635,6 +1878,7 @@ var BeaconBuffer = class {
|
|
|
1635
1878
|
this.droppedSinceLast = 0;
|
|
1636
1879
|
this.droppedTotal = 0;
|
|
1637
1880
|
this.deliveredTotal = 0;
|
|
1881
|
+
this.enqueuedTotal = 0;
|
|
1638
1882
|
this.backoffUntil = 0;
|
|
1639
1883
|
this.collapse = /* @__PURE__ */ new Map();
|
|
1640
1884
|
/** Keys the backend answered 401 for — later batches go unauthenticated. */
|
|
@@ -1647,13 +1891,16 @@ var BeaconBuffer = class {
|
|
|
1647
1891
|
collapseMaxKeys: options.collapseMaxKeys ?? 1e3,
|
|
1648
1892
|
requestTimeoutMs: options.requestTimeoutMs ?? 1e4,
|
|
1649
1893
|
retryBackoffMs: options.retryBackoffMs ?? 1e3,
|
|
1894
|
+
holdRepeats: options.holdRepeats ?? false,
|
|
1895
|
+
containerId: options.containerId ?? CONTAINER_ID,
|
|
1650
1896
|
now: options.now ?? Date.now
|
|
1651
1897
|
};
|
|
1652
1898
|
}
|
|
1653
1899
|
/**
|
|
1654
1900
|
* Queue one beacon. With a `collapseKey`, a repeat inside the window is
|
|
1655
|
-
* merged into
|
|
1656
|
-
*
|
|
1901
|
+
* merged into this key's item while that item is still queued (not yet in
|
|
1902
|
+
* flight). Once it has left, the repeat becomes a new item, sendable on
|
|
1903
|
+
* this flush — nothing waits for a later invocation (round 27.6).
|
|
1657
1904
|
*/
|
|
1658
1905
|
enqueue(dest, item, collapseKey) {
|
|
1659
1906
|
const now = this.opts.now();
|
|
@@ -1666,12 +1913,15 @@ var BeaconBuffer = class {
|
|
|
1666
1913
|
return;
|
|
1667
1914
|
}
|
|
1668
1915
|
}
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1916
|
+
if (this.opts.holdRepeats) {
|
|
1917
|
+
entry.held = this.push(
|
|
1918
|
+
dest,
|
|
1919
|
+
{ ...item, repeatCount: 1 },
|
|
1920
|
+
entry.firstSeenAt + this.opts.collapseWindowMs
|
|
1921
|
+
);
|
|
1922
|
+
return;
|
|
1923
|
+
}
|
|
1924
|
+
entry.first = this.push(dest, { ...item, repeatCount: 1 }, now);
|
|
1675
1925
|
return;
|
|
1676
1926
|
}
|
|
1677
1927
|
this.collapse.delete(collapseKey);
|
|
@@ -1707,6 +1957,7 @@ var BeaconBuffer = class {
|
|
|
1707
1957
|
droppedSinceLast: this.droppedSinceLast,
|
|
1708
1958
|
droppedTotal: this.droppedTotal,
|
|
1709
1959
|
deliveredTotal: this.deliveredTotal,
|
|
1960
|
+
enqueuedTotal: this.enqueuedTotal,
|
|
1710
1961
|
flushing: this.inFlight !== void 0
|
|
1711
1962
|
};
|
|
1712
1963
|
}
|
|
@@ -1721,6 +1972,7 @@ var BeaconBuffer = class {
|
|
|
1721
1972
|
inFlight: false
|
|
1722
1973
|
};
|
|
1723
1974
|
this.queue.push(entry);
|
|
1975
|
+
this.enqueuedTotal += 1;
|
|
1724
1976
|
return entry;
|
|
1725
1977
|
}
|
|
1726
1978
|
dropOldest() {
|
|
@@ -1766,7 +2018,11 @@ var BeaconBuffer = class {
|
|
|
1766
2018
|
droppedSinceLast: reportedDropped,
|
|
1767
2019
|
queueDepth: this.queue.length,
|
|
1768
2020
|
adapter: dest.adapter ?? "edge-core",
|
|
1769
|
-
version: SDK_VERSION
|
|
2021
|
+
version: SDK_VERSION,
|
|
2022
|
+
containerId: this.opts.containerId,
|
|
2023
|
+
enqueuedTotal: this.enqueuedTotal,
|
|
2024
|
+
deliveredTotal: this.deliveredTotal,
|
|
2025
|
+
droppedTotal: this.droppedTotal
|
|
1770
2026
|
};
|
|
1771
2027
|
const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
|
|
1772
2028
|
const response = await withBudget(
|
|
@@ -1947,14 +2203,11 @@ function resolveRequestPdlss(req, routeConfig, customExtractPurpose, customExtra
|
|
|
1947
2203
|
});
|
|
1948
2204
|
}
|
|
1949
2205
|
function matchRoute(pattern, path, opts) {
|
|
1950
|
-
const
|
|
1951
|
-
const caseSensitiveRegex = new RegExp(`^${regexPattern}$`);
|
|
1952
|
-
const caseSensitiveResult = caseSensitiveRegex.test(path);
|
|
2206
|
+
const caseSensitiveResult = matchRoutePattern(pattern, path);
|
|
1953
2207
|
if (!opts?.caseInsensitive && !opts?.logShadowDivergence) {
|
|
1954
2208
|
return caseSensitiveResult;
|
|
1955
2209
|
}
|
|
1956
|
-
const
|
|
1957
|
-
const caseInsensitiveResult = caseInsensitiveRegex.test(path);
|
|
2210
|
+
const caseInsensitiveResult = matchRoutePattern(pattern, path, { caseInsensitive: true });
|
|
1958
2211
|
if (opts?.logShadowDivergence && caseSensitiveResult !== caseInsensitiveResult) {
|
|
1959
2212
|
console.warn(
|
|
1960
2213
|
`[SHADOW] matchRoute case-insensitive would change result: pattern=${pattern} path=${path} caseSensitive=${caseSensitiveResult} caseInsensitive=${caseInsensitiveResult} correlationId=${opts.correlationId ?? "unknown"}`
|
|
@@ -2217,8 +2470,10 @@ function createMiddleware(options) {
|
|
|
2217
2470
|
}
|
|
2218
2471
|
const attemptHeader = req.headers["x-astra-attempt-id"];
|
|
2219
2472
|
const inboundAttemptId = typeof attemptHeader === "string" && /^att_[0-9a-f]{32}$/.test(attemptHeader) ? attemptHeader : void 0;
|
|
2473
|
+
const commerceArtifacts = detectExpressCommerce(req);
|
|
2220
2474
|
const verificationRequest = {
|
|
2221
2475
|
credentials,
|
|
2476
|
+
...commerceArtifacts && { commerceArtifacts },
|
|
2222
2477
|
purpose,
|
|
2223
2478
|
action: pdlssPair.action,
|
|
2224
2479
|
resource: req.path,
|