@astrasyncai/verification-gateway 5.14.0 → 5.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -18
- package/dist/adapters/express.d.mts +1 -1
- package/dist/adapters/express.d.ts +1 -1
- package/dist/adapters/express.js +492 -240
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +492 -240
- 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 +23 -6
- package/dist/adapters/mcp.js.map +1 -1
- package/dist/adapters/mcp.mjs +23 -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 +475 -239
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +475 -239
- 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 +83 -23
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +83 -23
- package/dist/adapters/sdk.mjs.map +1 -1
- package/dist/agent/index.d.mts +69 -54
- package/dist/agent/index.d.ts +69 -54
- package/dist/agent/index.js +24 -14
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/index.mjs +24 -14
- package/dist/agent/index.mjs.map +1 -1
- package/dist/bin/astrasync-claude-hook.js +58 -11
- package/dist/bin/astrasync-codex-hook.js +58 -11
- package/dist/bin/astrasync-guard.js +177 -11
- package/dist/bin/astrasync.js +433 -38
- package/dist/browser/background.js +53 -6
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +63 -6
- 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 +58 -11
- package/dist/codex/index.js.map +1 -1
- package/dist/codex/index.mjs +68 -11
- package/dist/codex/index.mjs.map +1 -1
- package/dist/cursor/extension.js +58 -11
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +68 -11
- 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 +11 -2
- package/dist/edge-core/index.d.ts +11 -2
- package/dist/edge-core/index.js +97 -11
- package/dist/edge-core/index.js.map +1 -1
- package/dist/edge-core/index.mjs +95 -11
- package/dist/edge-core/index.mjs.map +1 -1
- package/dist/gateway/gateway.js +53 -6
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +63 -6
- 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 +1407 -88
- package/dist/index.d.ts +1407 -88
- package/dist/index.js +5148 -2616
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5094 -2618
- 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-BCkwBA84.d.mts → types-Blz520bR.d.mts} +116 -12
- package/dist/{types-BCkwBA84.d.ts → types-Blz520bR.d.ts} +116 -12
- package/dist/{types-C_b4QTyO.d.ts → types-Bm1QvGqI.d.ts} +114 -10
- package/dist/{types-DlshIIR-.d.mts → types-DBe4hgfT.d.mts} +114 -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 +23 -6
- package/dist/verify.js.map +1 -1
- package/dist/verify.mjs +23 -6
- package/dist/verify.mjs.map +1 -1
- package/package.json +1 -1
package/dist/adapters/nextjs.js
CHANGED
|
@@ -35,8 +35,433 @@ __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) && headerValue(req, "ucp-agent")) {
|
|
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
|
+
|
|
452
|
+
// src/types.ts
|
|
453
|
+
function routePolicyOf(route) {
|
|
454
|
+
if (!route) return void 0;
|
|
455
|
+
const owners = route.allowedOwners?.filter(Boolean) ?? [];
|
|
456
|
+
const policy = {
|
|
457
|
+
...owners.length > 0 && { allowedOwners: owners },
|
|
458
|
+
...route.requiresRuntimeChallenge === true && { requiresRuntimeChallenge: true }
|
|
459
|
+
};
|
|
460
|
+
return Object.keys(policy).length > 0 ? policy : void 0;
|
|
461
|
+
}
|
|
462
|
+
|
|
38
463
|
// src/version.ts
|
|
39
|
-
var SDK_VERSION = "5.
|
|
464
|
+
var SDK_VERSION = "5.16.0";
|
|
40
465
|
|
|
41
466
|
// src/http.ts
|
|
42
467
|
var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
|
|
@@ -284,6 +709,8 @@ function getCacheKey(request, counterpartyId) {
|
|
|
284
709
|
request.durationRequired ?? "",
|
|
285
710
|
request.invocationProtocol || "",
|
|
286
711
|
request.enableRuntimeChallenge ? "1" : "0",
|
|
712
|
+
// 5.16.0: route policy changes the verdict (owners / challenge).
|
|
713
|
+
request.routePolicy ? JSON.stringify(request.routePolicy) : "",
|
|
287
714
|
// callerMetadata fields contribute to risk model; include the ones
|
|
288
715
|
// backend reads. sourceIp/userAgent/forwardedFor change per-request
|
|
289
716
|
// so their inclusion effectively forces a re-check for any varying
|
|
@@ -490,6 +917,7 @@ async function callVerifyAccessAPI(config, request) {
|
|
|
490
917
|
if (requestData.subAgentDepth !== void 0) body.subAgentDepth = requestData.subAgentDepth;
|
|
491
918
|
if (requestData.enableRuntimeChallenge)
|
|
492
919
|
body.enableRuntimeChallenge = requestData.enableRuntimeChallenge;
|
|
920
|
+
if (requestData.routePolicy) body.routePolicy = requestData.routePolicy;
|
|
493
921
|
if (requestData.createSession) body.createSession = requestData.createSession;
|
|
494
922
|
if (requestData.durationRequired) body.durationRequired = requestData.durationRequired;
|
|
495
923
|
if (requestData.counterpartyType) body.counterpartyType = requestData.counterpartyType;
|
|
@@ -500,6 +928,7 @@ async function callVerifyAccessAPI(config, request) {
|
|
|
500
928
|
if (requestData.invocationProtocol) body.invocationProtocol = requestData.invocationProtocol;
|
|
501
929
|
if (requestData.commercePhase) body.commercePhase = requestData.commercePhase;
|
|
502
930
|
if (requestData.checkoutSessionId) body.checkoutSessionId = requestData.checkoutSessionId;
|
|
931
|
+
if (requestData.checkoutJwt) body.checkoutJwt = requestData.checkoutJwt;
|
|
503
932
|
if (requestData.checkoutItems) body.checkoutItems = requestData.checkoutItems;
|
|
504
933
|
if (requestData.buyerEmail) body.buyerEmail = requestData.buyerEmail;
|
|
505
934
|
if (requestData.commerceArtifacts) body.commerceArtifacts = requestData.commerceArtifacts;
|
|
@@ -650,7 +1079,8 @@ async function verify(config, request, options) {
|
|
|
650
1079
|
"[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
1080
|
);
|
|
652
1081
|
}
|
|
653
|
-
|
|
1082
|
+
const uncacheable = Boolean(request.commerceArtifacts);
|
|
1083
|
+
if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache && !uncacheable) {
|
|
654
1084
|
const cached = getCachedResult(request, mergedConfig.counterpartyId);
|
|
655
1085
|
if (cached) {
|
|
656
1086
|
if (mergedConfig.debug) {
|
|
@@ -731,7 +1161,10 @@ async function verify(config, request, options) {
|
|
|
731
1161
|
// MERCHANT caller only (bridge callers strip it before the agent plane).
|
|
732
1162
|
settlementToken: apiResponse.settlementToken,
|
|
733
1163
|
// 5.6.0: fulfilment contact — merchant-only lane, same strip rule.
|
|
734
|
-
fulfilment: apiResponse.fulfilment
|
|
1164
|
+
fulfilment: apiResponse.fulfilment,
|
|
1165
|
+
mandates: apiResponse.mandates,
|
|
1166
|
+
kyaToken: apiResponse.kyaToken,
|
|
1167
|
+
aep: apiResponse.aep
|
|
735
1168
|
};
|
|
736
1169
|
return result2;
|
|
737
1170
|
}
|
|
@@ -741,7 +1174,10 @@ async function verify(config, request, options) {
|
|
|
741
1174
|
trustScore: apiResponse.agent.trustScore,
|
|
742
1175
|
trustLevel: getTrustLevel(apiResponse.agent.trustScore),
|
|
743
1176
|
blockchainAnchored: apiResponse.agent.blockchainStatus === "anchored",
|
|
744
|
-
status: apiResponse.agent.agentStatus
|
|
1177
|
+
status: apiResponse.agent.agentStatus,
|
|
1178
|
+
...apiResponse.agent.protocolIdentities && {
|
|
1179
|
+
protocolIdentities: apiResponse.agent.protocolIdentities
|
|
1180
|
+
}
|
|
745
1181
|
} : void 0;
|
|
746
1182
|
const developer = apiResponse.developer ? {
|
|
747
1183
|
astradId: apiResponse.developer.kyaOwnerId,
|
|
@@ -792,7 +1228,11 @@ async function verify(config, request, options) {
|
|
|
792
1228
|
settlementOutcome: apiResponse.settlementOutcome,
|
|
793
1229
|
settlementToken: apiResponse.settlementToken,
|
|
794
1230
|
// 5.6.0: fulfilment contact — merchant-only lane, same strip rule.
|
|
795
|
-
fulfilment: apiResponse.fulfilment
|
|
1231
|
+
fulfilment: apiResponse.fulfilment,
|
|
1232
|
+
// 5.15.0: merchant-lane AP2 mandates + KYAPay identity token.
|
|
1233
|
+
mandates: apiResponse.mandates,
|
|
1234
|
+
kyaToken: apiResponse.kyaToken,
|
|
1235
|
+
aep: apiResponse.aep
|
|
796
1236
|
};
|
|
797
1237
|
if (result.recommendation === "deny") {
|
|
798
1238
|
result.policyAllowed = false;
|
|
@@ -812,7 +1252,9 @@ async function verify(config, request, options) {
|
|
|
812
1252
|
}
|
|
813
1253
|
const escalates = result.requiresStepUp === true || result.requiresApproval === true || result.recommendation === "step_up_required";
|
|
814
1254
|
if (mergedConfig.cacheTtl !== 0 && result.recommendation !== "deny" && !escalates) {
|
|
815
|
-
|
|
1255
|
+
if (!uncacheable) {
|
|
1256
|
+
cacheResult(request, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);
|
|
1257
|
+
}
|
|
816
1258
|
}
|
|
817
1259
|
return result;
|
|
818
1260
|
}
|
|
@@ -966,239 +1408,30 @@ function performCounterpartyPreCheck(routeConfig, astraCreds, purpose) {
|
|
|
966
1408
|
return failures;
|
|
967
1409
|
}
|
|
968
1410
|
|
|
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
|
-
}
|
|
1411
|
+
// src/route-pattern.ts
|
|
1412
|
+
function routePatternToRegExp(pattern, opts = {}) {
|
|
1413
|
+
let source = "";
|
|
1414
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
1415
|
+
const ch = pattern[i];
|
|
1416
|
+
if (ch === "*") {
|
|
1417
|
+
source += ".*";
|
|
1418
|
+
} else if (ch === ":" && /[A-Za-z_]/.test(pattern[i + 1] ?? "") && (i === 0 || pattern[i - 1] === "/")) {
|
|
1419
|
+
let j = i + 1;
|
|
1420
|
+
while (j < pattern.length && /[A-Za-z0-9_]/.test(pattern[j])) j++;
|
|
1421
|
+
source += "[^/]+";
|
|
1422
|
+
i = j - 1;
|
|
1423
|
+
} else {
|
|
1424
|
+
source += ch.replace(/[.+?^${}()|[\]\\/]/g, "\\$&");
|
|
1164
1425
|
}
|
|
1165
1426
|
}
|
|
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;
|
|
1427
|
+
try {
|
|
1428
|
+
return new RegExp(`^${source}$`, opts.caseInsensitive ? "i" : "");
|
|
1429
|
+
} catch {
|
|
1430
|
+
return null;
|
|
1180
1431
|
}
|
|
1181
|
-
return Object.keys(connection).length > 0 ? connection : void 0;
|
|
1182
1432
|
}
|
|
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
|
-
};
|
|
1433
|
+
function matchRoutePattern(pattern, path, opts = {}) {
|
|
1434
|
+
return routePatternToRegExp(pattern, opts)?.test(path) ?? false;
|
|
1202
1435
|
}
|
|
1203
1436
|
|
|
1204
1437
|
// src/edge-config.ts
|
|
@@ -1233,6 +1466,7 @@ function isEdgeConfig(value) {
|
|
|
1233
1466
|
if (r.sampling !== void 0 && !isSamplingShape(r.sampling)) return false;
|
|
1234
1467
|
}
|
|
1235
1468
|
if (!isSamplingShape(v.sampling)) return false;
|
|
1469
|
+
if (v.minTls !== void 0 && v.minTls !== "1.2" && v.minTls !== "1.3") return false;
|
|
1236
1470
|
if (v.queryValueAllowlist !== void 0) {
|
|
1237
1471
|
if (!Array.isArray(v.queryValueAllowlist) || v.queryValueAllowlist.length > 16 || !v.queryValueAllowlist.every((e) => typeof e === "string" && e.length > 0 && e.length <= 64)) {
|
|
1238
1472
|
return false;
|
|
@@ -1948,9 +2182,7 @@ function extractCredentialsFromNextRequest(request) {
|
|
|
1948
2182
|
return credentials;
|
|
1949
2183
|
}
|
|
1950
2184
|
function matchRoute(pattern, path) {
|
|
1951
|
-
|
|
1952
|
-
const regex = new RegExp(`^${regexPattern}$`);
|
|
1953
|
-
return regex.test(path);
|
|
2185
|
+
return matchRoutePattern(pattern, path);
|
|
1954
2186
|
}
|
|
1955
2187
|
function findRouteConfig(routes, path, method) {
|
|
1956
2188
|
return routes.find((route) => {
|
|
@@ -2141,7 +2373,8 @@ function createMiddleware(options) {
|
|
|
2141
2373
|
skipPaths = [],
|
|
2142
2374
|
showInterstitial,
|
|
2143
2375
|
showCommerceShield,
|
|
2144
|
-
|
|
2376
|
+
// Round 27.8: opt-in only (the merchant turns it on); was default-on.
|
|
2377
|
+
enableRuntimeChallenge = false,
|
|
2145
2378
|
routesRefreshMs = DEFAULT_ROUTES_REFRESH_MS,
|
|
2146
2379
|
anonymousBeacon = true,
|
|
2147
2380
|
trustVerifiedHop = false,
|
|
@@ -2278,14 +2511,17 @@ function createMiddleware(options) {
|
|
|
2278
2511
|
const originalClientIp = forwardedFor?.split(",")[0]?.trim();
|
|
2279
2512
|
const attemptHeader = request.headers.get("x-astra-attempt-id");
|
|
2280
2513
|
const inboundAttemptId = attemptHeader && /^att_[0-9a-f]{32}$/.test(attemptHeader) ? attemptHeader : void 0;
|
|
2514
|
+
const commerceArtifacts = await detectFetchCommerce(request);
|
|
2281
2515
|
const verificationRequest = {
|
|
2282
2516
|
credentials,
|
|
2517
|
+
...commerceArtifacts && { commerceArtifacts },
|
|
2283
2518
|
purpose,
|
|
2284
2519
|
action: pdlssPair.action,
|
|
2285
2520
|
resource: pathname,
|
|
2286
2521
|
counterpartyUrl,
|
|
2287
2522
|
counterpartyType: config.counterpartyType || "website",
|
|
2288
2523
|
enableRuntimeChallenge,
|
|
2524
|
+
...routePolicyOf(routeConfig) && { routePolicy: routePolicyOf(routeConfig) },
|
|
2289
2525
|
durationRequired: astraCreds?.pdlss?.duration?.maxSessionDuration,
|
|
2290
2526
|
...inboundAttemptId && { attemptId: inboundAttemptId },
|
|
2291
2527
|
callerMetadata: {
|