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