@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
|
@@ -1,5 +1,448 @@
|
|
|
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 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
|
+
|
|
433
|
+
// src/types.ts
|
|
434
|
+
function routePolicyOf(route) {
|
|
435
|
+
if (!route) return void 0;
|
|
436
|
+
const owners = route.allowedOwners?.filter(Boolean) ?? [];
|
|
437
|
+
const policy = {
|
|
438
|
+
...owners.length > 0 && { allowedOwners: owners },
|
|
439
|
+
...route.requiresRuntimeChallenge === true && { requiresRuntimeChallenge: true }
|
|
440
|
+
};
|
|
441
|
+
return Object.keys(policy).length > 0 ? policy : void 0;
|
|
442
|
+
}
|
|
443
|
+
|
|
1
444
|
// src/version.ts
|
|
2
|
-
var SDK_VERSION = "5.
|
|
445
|
+
var SDK_VERSION = "5.16.0";
|
|
3
446
|
|
|
4
447
|
// src/http.ts
|
|
5
448
|
var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
|
|
@@ -299,6 +742,8 @@ function getCacheKey(request, counterpartyId) {
|
|
|
299
742
|
request.durationRequired ?? "",
|
|
300
743
|
request.invocationProtocol || "",
|
|
301
744
|
request.enableRuntimeChallenge ? "1" : "0",
|
|
745
|
+
// 5.16.0: route policy changes the verdict (owners / challenge).
|
|
746
|
+
request.routePolicy ? JSON.stringify(request.routePolicy) : "",
|
|
302
747
|
// callerMetadata fields contribute to risk model; include the ones
|
|
303
748
|
// backend reads. sourceIp/userAgent/forwardedFor change per-request
|
|
304
749
|
// so their inclusion effectively forces a re-check for any varying
|
|
@@ -505,6 +950,7 @@ async function callVerifyAccessAPI(config, request) {
|
|
|
505
950
|
if (requestData.subAgentDepth !== void 0) body.subAgentDepth = requestData.subAgentDepth;
|
|
506
951
|
if (requestData.enableRuntimeChallenge)
|
|
507
952
|
body.enableRuntimeChallenge = requestData.enableRuntimeChallenge;
|
|
953
|
+
if (requestData.routePolicy) body.routePolicy = requestData.routePolicy;
|
|
508
954
|
if (requestData.createSession) body.createSession = requestData.createSession;
|
|
509
955
|
if (requestData.durationRequired) body.durationRequired = requestData.durationRequired;
|
|
510
956
|
if (requestData.counterpartyType) body.counterpartyType = requestData.counterpartyType;
|
|
@@ -515,6 +961,7 @@ async function callVerifyAccessAPI(config, request) {
|
|
|
515
961
|
if (requestData.invocationProtocol) body.invocationProtocol = requestData.invocationProtocol;
|
|
516
962
|
if (requestData.commercePhase) body.commercePhase = requestData.commercePhase;
|
|
517
963
|
if (requestData.checkoutSessionId) body.checkoutSessionId = requestData.checkoutSessionId;
|
|
964
|
+
if (requestData.checkoutJwt) body.checkoutJwt = requestData.checkoutJwt;
|
|
518
965
|
if (requestData.checkoutItems) body.checkoutItems = requestData.checkoutItems;
|
|
519
966
|
if (requestData.buyerEmail) body.buyerEmail = requestData.buyerEmail;
|
|
520
967
|
if (requestData.commerceArtifacts) body.commerceArtifacts = requestData.commerceArtifacts;
|
|
@@ -665,7 +1112,8 @@ async function verify(config, request, options) {
|
|
|
665
1112
|
"[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
1113
|
);
|
|
667
1114
|
}
|
|
668
|
-
|
|
1115
|
+
const uncacheable = Boolean(request.commerceArtifacts);
|
|
1116
|
+
if (mergedConfig.cacheTtl !== 0 && !options?.bypassCache && !uncacheable) {
|
|
669
1117
|
const cached = getCachedResult(request, mergedConfig.counterpartyId);
|
|
670
1118
|
if (cached) {
|
|
671
1119
|
if (mergedConfig.debug) {
|
|
@@ -746,7 +1194,10 @@ async function verify(config, request, options) {
|
|
|
746
1194
|
// MERCHANT caller only (bridge callers strip it before the agent plane).
|
|
747
1195
|
settlementToken: apiResponse.settlementToken,
|
|
748
1196
|
// 5.6.0: fulfilment contact — merchant-only lane, same strip rule.
|
|
749
|
-
fulfilment: apiResponse.fulfilment
|
|
1197
|
+
fulfilment: apiResponse.fulfilment,
|
|
1198
|
+
mandates: apiResponse.mandates,
|
|
1199
|
+
kyaToken: apiResponse.kyaToken,
|
|
1200
|
+
aep: apiResponse.aep
|
|
750
1201
|
};
|
|
751
1202
|
return result2;
|
|
752
1203
|
}
|
|
@@ -756,7 +1207,10 @@ async function verify(config, request, options) {
|
|
|
756
1207
|
trustScore: apiResponse.agent.trustScore,
|
|
757
1208
|
trustLevel: getTrustLevel(apiResponse.agent.trustScore),
|
|
758
1209
|
blockchainAnchored: apiResponse.agent.blockchainStatus === "anchored",
|
|
759
|
-
status: apiResponse.agent.agentStatus
|
|
1210
|
+
status: apiResponse.agent.agentStatus,
|
|
1211
|
+
...apiResponse.agent.protocolIdentities && {
|
|
1212
|
+
protocolIdentities: apiResponse.agent.protocolIdentities
|
|
1213
|
+
}
|
|
760
1214
|
} : void 0;
|
|
761
1215
|
const developer = apiResponse.developer ? {
|
|
762
1216
|
astradId: apiResponse.developer.kyaOwnerId,
|
|
@@ -807,7 +1261,11 @@ async function verify(config, request, options) {
|
|
|
807
1261
|
settlementOutcome: apiResponse.settlementOutcome,
|
|
808
1262
|
settlementToken: apiResponse.settlementToken,
|
|
809
1263
|
// 5.6.0: fulfilment contact — merchant-only lane, same strip rule.
|
|
810
|
-
fulfilment: apiResponse.fulfilment
|
|
1264
|
+
fulfilment: apiResponse.fulfilment,
|
|
1265
|
+
// 5.15.0: merchant-lane AP2 mandates + KYAPay identity token.
|
|
1266
|
+
mandates: apiResponse.mandates,
|
|
1267
|
+
kyaToken: apiResponse.kyaToken,
|
|
1268
|
+
aep: apiResponse.aep
|
|
811
1269
|
};
|
|
812
1270
|
if (result.recommendation === "deny") {
|
|
813
1271
|
result.policyAllowed = false;
|
|
@@ -827,7 +1285,9 @@ async function verify(config, request, options) {
|
|
|
827
1285
|
}
|
|
828
1286
|
const escalates = result.requiresStepUp === true || result.requiresApproval === true || result.recommendation === "step_up_required";
|
|
829
1287
|
if (mergedConfig.cacheTtl !== 0 && result.recommendation !== "deny" && !escalates) {
|
|
830
|
-
|
|
1288
|
+
if (!uncacheable) {
|
|
1289
|
+
cacheResult(request, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);
|
|
1290
|
+
}
|
|
831
1291
|
}
|
|
832
1292
|
return result;
|
|
833
1293
|
}
|
|
@@ -1111,239 +1571,30 @@ function performCounterpartyPreCheck(routeConfig, astraCreds, purpose) {
|
|
|
1111
1571
|
return failures;
|
|
1112
1572
|
}
|
|
1113
1573
|
|
|
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
|
-
}
|
|
1574
|
+
// src/route-pattern.ts
|
|
1575
|
+
function routePatternToRegExp(pattern, opts = {}) {
|
|
1576
|
+
let source = "";
|
|
1577
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
1578
|
+
const ch = pattern[i];
|
|
1579
|
+
if (ch === "*") {
|
|
1580
|
+
source += ".*";
|
|
1581
|
+
} else if (ch === ":" && /[A-Za-z_]/.test(pattern[i + 1] ?? "") && (i === 0 || pattern[i - 1] === "/")) {
|
|
1582
|
+
let j = i + 1;
|
|
1583
|
+
while (j < pattern.length && /[A-Za-z0-9_]/.test(pattern[j])) j++;
|
|
1584
|
+
source += "[^/]+";
|
|
1585
|
+
i = j - 1;
|
|
1586
|
+
} else {
|
|
1587
|
+
source += ch.replace(/[.+?^${}()|[\]\\/]/g, "\\$&");
|
|
1309
1588
|
}
|
|
1310
1589
|
}
|
|
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;
|
|
1590
|
+
try {
|
|
1591
|
+
return new RegExp(`^${source}$`, opts.caseInsensitive ? "i" : "");
|
|
1592
|
+
} catch {
|
|
1593
|
+
return null;
|
|
1325
1594
|
}
|
|
1326
|
-
return Object.keys(connection).length > 0 ? connection : void 0;
|
|
1327
1595
|
}
|
|
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
|
-
};
|
|
1596
|
+
function matchRoutePattern(pattern, path, opts = {}) {
|
|
1597
|
+
return routePatternToRegExp(pattern, opts)?.test(path) ?? false;
|
|
1347
1598
|
}
|
|
1348
1599
|
|
|
1349
1600
|
// src/edge-config.ts
|
|
@@ -1378,6 +1629,7 @@ function isEdgeConfig(value) {
|
|
|
1378
1629
|
if (r.sampling !== void 0 && !isSamplingShape(r.sampling)) return false;
|
|
1379
1630
|
}
|
|
1380
1631
|
if (!isSamplingShape(v.sampling)) return false;
|
|
1632
|
+
if (v.minTls !== void 0 && v.minTls !== "1.2" && v.minTls !== "1.3") return false;
|
|
1381
1633
|
if (v.queryValueAllowlist !== void 0) {
|
|
1382
1634
|
if (!Array.isArray(v.queryValueAllowlist) || v.queryValueAllowlist.length > 16 || !v.queryValueAllowlist.every((e) => typeof e === "string" && e.length > 0 && e.length <= 64)) {
|
|
1383
1635
|
return false;
|
|
@@ -1965,14 +2217,11 @@ function resolveRequestPdlss(req, routeConfig, customExtractPurpose, customExtra
|
|
|
1965
2217
|
});
|
|
1966
2218
|
}
|
|
1967
2219
|
function matchRoute(pattern, path, opts) {
|
|
1968
|
-
const
|
|
1969
|
-
const caseSensitiveRegex = new RegExp(`^${regexPattern}$`);
|
|
1970
|
-
const caseSensitiveResult = caseSensitiveRegex.test(path);
|
|
2220
|
+
const caseSensitiveResult = matchRoutePattern(pattern, path);
|
|
1971
2221
|
if (!opts?.caseInsensitive && !opts?.logShadowDivergence) {
|
|
1972
2222
|
return caseSensitiveResult;
|
|
1973
2223
|
}
|
|
1974
|
-
const
|
|
1975
|
-
const caseInsensitiveResult = caseInsensitiveRegex.test(path);
|
|
2224
|
+
const caseInsensitiveResult = matchRoutePattern(pattern, path, { caseInsensitive: true });
|
|
1976
2225
|
if (opts?.logShadowDivergence && caseSensitiveResult !== caseInsensitiveResult) {
|
|
1977
2226
|
console.warn(
|
|
1978
2227
|
`[SHADOW] matchRoute case-insensitive would change result: pattern=${pattern} path=${path} caseSensitive=${caseSensitiveResult} caseInsensitive=${caseInsensitiveResult} correlationId=${opts.correlationId ?? "unknown"}`
|
|
@@ -2235,8 +2484,10 @@ function createMiddleware(options) {
|
|
|
2235
2484
|
}
|
|
2236
2485
|
const attemptHeader = req.headers["x-astra-attempt-id"];
|
|
2237
2486
|
const inboundAttemptId = typeof attemptHeader === "string" && /^att_[0-9a-f]{32}$/.test(attemptHeader) ? attemptHeader : void 0;
|
|
2487
|
+
const commerceArtifacts = detectExpressCommerce(req);
|
|
2238
2488
|
const verificationRequest = {
|
|
2239
2489
|
credentials,
|
|
2490
|
+
...commerceArtifacts && { commerceArtifacts },
|
|
2240
2491
|
purpose,
|
|
2241
2492
|
action: pdlssPair.action,
|
|
2242
2493
|
resource: req.path,
|
|
@@ -2244,6 +2495,7 @@ function createMiddleware(options) {
|
|
|
2244
2495
|
counterpartyUrl,
|
|
2245
2496
|
counterpartyType: config.counterpartyType || "api",
|
|
2246
2497
|
enableRuntimeChallenge,
|
|
2498
|
+
...routePolicyOf(routeConfig) && { routePolicy: routePolicyOf(routeConfig) },
|
|
2247
2499
|
durationRequired: astraCreds?.pdlss?.duration?.maxSessionDuration,
|
|
2248
2500
|
...txValue !== void 0 && { transactionValue: txValue },
|
|
2249
2501
|
...txCurrency && { currency: txCurrency },
|