@astrasyncai/verification-gateway 2.4.11 → 2.4.14
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/dist/adapter-interface/interface.d.mts +2 -2
- package/dist/adapter-interface/interface.d.ts +2 -2
- package/dist/adapters/express.d.mts +2 -2
- package/dist/adapters/express.d.ts +2 -2
- package/dist/adapters/express.js +129 -36
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +129 -36
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/mcp.d.mts +26 -4
- package/dist/adapters/mcp.d.ts +26 -4
- package/dist/adapters/mcp.js +94 -28
- package/dist/adapters/mcp.js.map +1 -1
- package/dist/adapters/mcp.mjs +94 -28
- package/dist/adapters/mcp.mjs.map +1 -1
- package/dist/adapters/nextjs.d.mts +2 -2
- package/dist/adapters/nextjs.d.ts +2 -2
- package/dist/adapters/nextjs.js +75 -29
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +75 -29
- package/dist/adapters/nextjs.mjs.map +1 -1
- package/dist/adapters/sdk.d.mts +2 -2
- package/dist/adapters/sdk.d.ts +2 -2
- package/dist/adapters/sdk.js +45 -22
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +45 -22
- package/dist/adapters/sdk.mjs.map +1 -1
- package/dist/agent/index.d.mts +2 -2
- package/dist/agent/index.d.ts +2 -2
- package/dist/agent/index.js +29 -0
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/index.mjs +29 -0
- package/dist/agent/index.mjs.map +1 -1
- package/dist/browser/background.js +86 -24
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +86 -24
- package/dist/browser/background.mjs.map +1 -1
- package/dist/browser/browser-adapter.d.mts +2 -2
- package/dist/browser/browser-adapter.d.ts +2 -2
- package/dist/cli/index.d.mts +2 -2
- package/dist/cli/index.d.ts +2 -2
- package/dist/cursor/cursor-adapter.d.mts +2 -2
- package/dist/cursor/cursor-adapter.d.ts +2 -2
- package/dist/cursor/extension.d.mts +2 -2
- package/dist/cursor/extension.d.ts +2 -2
- package/dist/cursor/extension.js +86 -24
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +86 -24
- package/dist/cursor/extension.mjs.map +1 -1
- package/dist/{express-C1ePFB7n.d.ts → express-CrfwoNAR.d.ts} +1 -1
- package/dist/{express-4WStX3PV.d.mts → express-ienhAXps.d.mts} +1 -1
- package/dist/gateway/gateway.d.mts +2 -2
- package/dist/gateway/gateway.d.ts +2 -2
- package/dist/gateway/gateway.js +86 -24
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +86 -24
- package/dist/gateway/gateway.mjs.map +1 -1
- package/dist/git-trigger/git-hooks.d.mts +2 -2
- package/dist/git-trigger/git-hooks.d.ts +2 -2
- package/dist/{index-ChPX4WHl.d.mts → index-B5e2IDWU.d.mts} +1 -1
- package/dist/{index-CzJMCgEy.d.ts → index-CCdZxvAr.d.ts} +71 -6
- package/dist/{index-D8IEntil.d.mts → index-CEg_WG6y.d.mts} +71 -6
- package/dist/{index-Cjm-zBeZ.d.ts → index-DC5f8eoQ.d.ts} +1 -1
- package/dist/index.d.mts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +344 -73
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +344 -73
- package/dist/index.mjs.map +1 -1
- package/dist/local-evaluator/evaluator.d.mts +2 -2
- package/dist/local-evaluator/evaluator.d.ts +2 -2
- package/dist/local-evaluator/evaluator.js +12 -2
- package/dist/local-evaluator/evaluator.js.map +1 -1
- package/dist/local-evaluator/evaluator.mjs +12 -2
- package/dist/local-evaluator/evaluator.mjs.map +1 -1
- package/dist/{nextjs-BIORS__0.d.ts → nextjs-66R1KW8e.d.ts} +1 -1
- package/dist/{nextjs-CjzHdaXA.d.mts → nextjs-DSpisQst.d.mts} +1 -1
- package/dist/{sdk-Chhz-FcT.d.mts → sdk-5U_CBRpr.d.mts} +1 -1
- package/dist/{sdk-CqTEQAc6.d.ts → sdk-Bm8np66n.d.ts} +1 -1
- package/dist/transport/index.d.mts +2 -2
- package/dist/transport/index.d.ts +2 -2
- package/dist/transport/index.js +146 -28
- package/dist/transport/index.js.map +1 -1
- package/dist/transport/index.mjs +146 -28
- package/dist/transport/index.mjs.map +1 -1
- package/dist/{types-L15pYd2c.d.mts → types-B3USs-Kx.d.mts} +42 -1
- package/dist/{types-L15pYd2c.d.ts → types-B3USs-Kx.d.ts} +42 -1
- package/dist/{types-DNK2BgIf.d.mts → types-CgDCUfo8.d.mts} +1 -1
- package/dist/{types-DoWIuzfj.d.ts → types-R5N4ET6x.d.ts} +1 -1
- package/dist/ui/index.d.mts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AstraSyncGateway } from '../gateway/gateway.mjs';
|
|
2
|
-
import { A as AgentAction, I as InterceptResult, P as PDLSSContext, V as VerificationDecision } from '../types-
|
|
3
|
-
import '../types-
|
|
2
|
+
import { A as AgentAction, I as InterceptResult, P as PDLSSContext, V as VerificationDecision } from '../types-CgDCUfo8.mjs';
|
|
3
|
+
import '../types-B3USs-Kx.mjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* PlatformAdapter Interface
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AstraSyncGateway } from '../gateway/gateway.js';
|
|
2
|
-
import { A as AgentAction, I as InterceptResult, P as PDLSSContext, V as VerificationDecision } from '../types-
|
|
3
|
-
import '../types-
|
|
2
|
+
import { A as AgentAction, I as InterceptResult, P as PDLSSContext, V as VerificationDecision } from '../types-R5N4ET6x.js';
|
|
3
|
+
import '../types-B3USs-Kx.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* PlatformAdapter Interface
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import 'express';
|
|
2
|
-
import '../types-
|
|
3
|
-
export { c as createMiddleware, a as extractAstraSyncCredentials } from '../express-
|
|
2
|
+
import '../types-B3USs-Kx.mjs';
|
|
3
|
+
export { c as createMiddleware, a as extractAstraSyncCredentials } from '../express-ienhAXps.mjs';
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import 'express';
|
|
2
|
-
import '../types-
|
|
3
|
-
export { c as createMiddleware, a as extractAstraSyncCredentials } from '../express-
|
|
2
|
+
import '../types-B3USs-Kx.js';
|
|
3
|
+
export { c as createMiddleware, a as extractAstraSyncCredentials } from '../express-CrfwoNAR.js';
|
package/dist/adapters/express.js
CHANGED
|
@@ -45,7 +45,7 @@ function hasMinimumAccess(actual, required) {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
// src/version.ts
|
|
48
|
-
var SDK_VERSION = "2.4.
|
|
48
|
+
var SDK_VERSION = "2.4.13";
|
|
49
49
|
|
|
50
50
|
// src/verify.ts
|
|
51
51
|
var DEFAULT_CONFIG = {
|
|
@@ -64,22 +64,27 @@ var DEFAULT_CONFIG = {
|
|
|
64
64
|
};
|
|
65
65
|
var initCheckPerformed = false;
|
|
66
66
|
var deprecationWarningShown = false;
|
|
67
|
-
async function performInitCheck(apiBaseUrl, debug) {
|
|
67
|
+
async function performInitCheck(apiBaseUrl, debug, strictInit) {
|
|
68
68
|
initCheckPerformed = true;
|
|
69
69
|
try {
|
|
70
70
|
const probeUrl = `${apiBaseUrl}/agents/verify-access`;
|
|
71
71
|
const response = await fetch(probeUrl, { method: "HEAD" });
|
|
72
72
|
const contentType = response.headers.get("content-type") ?? "";
|
|
73
73
|
if (contentType.startsWith("text/html")) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
const message = `[VerificationGateway] apiBaseUrl '${apiBaseUrl}' returned HTML (content-type: ${contentType}). This usually means apiBaseUrl is pointing at a marketing site instead of the API. Expected: 'https://astrasync.ai/api' (prod) or 'https://staging.astrasync.ai/api' (staging).`;
|
|
75
|
+
if (strictInit) {
|
|
76
|
+
throw new Error(`${message} (strictInit=true)`);
|
|
77
|
+
}
|
|
78
|
+
console.warn(`${message} Set disableInitChecks: true on GatewayConfig to silence.`);
|
|
77
79
|
} else if (debug) {
|
|
78
80
|
console.log(
|
|
79
81
|
`[VerificationGateway] init check passed for ${apiBaseUrl} (content-type: ${contentType})`
|
|
80
82
|
);
|
|
81
83
|
}
|
|
82
84
|
} catch (err) {
|
|
85
|
+
if (strictInit) {
|
|
86
|
+
throw err;
|
|
87
|
+
}
|
|
83
88
|
if (debug) {
|
|
84
89
|
console.log(`[VerificationGateway] init check failed (non-blocking): ${String(err)}`);
|
|
85
90
|
}
|
|
@@ -103,7 +108,23 @@ function getCacheKey(request) {
|
|
|
103
108
|
request.counterpartyType || "",
|
|
104
109
|
request.isSubAgentRequest ? "1" : "0",
|
|
105
110
|
request.parentAgentId || "",
|
|
106
|
-
request.subAgentDepth ?? ""
|
|
111
|
+
request.subAgentDepth ?? "",
|
|
112
|
+
// Audit F-A1-07: previously-missing dimensions that DO affect the
|
|
113
|
+
// backend verdict. Without these, two requests with different
|
|
114
|
+
// durations (e.g. 60s vs 86400s) collided on the same cache key and
|
|
115
|
+
// the shorter-duration allow served the longer-duration request.
|
|
116
|
+
request.durationRequired ?? "",
|
|
117
|
+
request.invocationProtocol || "",
|
|
118
|
+
request.enableRuntimeChallenge ? "1" : "0",
|
|
119
|
+
// callerMetadata fields contribute to risk model; include the ones
|
|
120
|
+
// backend reads. sourceIp/userAgent/forwardedFor change per-request
|
|
121
|
+
// so their inclusion effectively forces a re-check for any varying
|
|
122
|
+
// client (the right behavior — IP-driven anomaly scoring shouldn't
|
|
123
|
+
// be cached across IPs).
|
|
124
|
+
request.callerMetadata?.sourceIp || "",
|
|
125
|
+
request.callerMetadata?.userAgent || "",
|
|
126
|
+
request.callerMetadata?.forwardedFor || "",
|
|
127
|
+
request.callerMetadata?.agentCardUrl || ""
|
|
107
128
|
].join("|");
|
|
108
129
|
}
|
|
109
130
|
function getCachedResult(request) {
|
|
@@ -129,9 +150,13 @@ function cacheResult(request, result, configuredTtl) {
|
|
|
129
150
|
}
|
|
130
151
|
function extractCredentials(headers, query) {
|
|
131
152
|
const credentials = {};
|
|
153
|
+
const ASTRA_ID_PATTERN = /^ASTRAE?-[A-Za-z0-9_-]{1,64}$/;
|
|
132
154
|
const astraIdHeader = headers["x-astra-id"] || headers["X-Astra-Id"] || headers["X-ASTRA-ID"] || headers["x-astra-agentid"] || headers["X-Astra-AgentId"] || headers["x-astra-agent-id"] || headers["X-Astra-Agent-Id"] || headers["X-ASTRA-AGENT-ID"];
|
|
133
155
|
if (astraIdHeader) {
|
|
134
|
-
|
|
156
|
+
const raw = Array.isArray(astraIdHeader) ? astraIdHeader[0] : typeof astraIdHeader === "string" ? astraIdHeader : void 0;
|
|
157
|
+
if (typeof raw === "string" && ASTRA_ID_PATTERN.test(raw)) {
|
|
158
|
+
credentials.astraId = raw;
|
|
159
|
+
}
|
|
135
160
|
}
|
|
136
161
|
const apiKeyHeader = headers["x-api-key"] || headers["X-Api-Key"] || headers["X-API-KEY"];
|
|
137
162
|
if (apiKeyHeader) {
|
|
@@ -140,9 +165,11 @@ function extractCredentials(headers, query) {
|
|
|
140
165
|
const authHeader = headers["authorization"] || headers["Authorization"];
|
|
141
166
|
if (authHeader) {
|
|
142
167
|
const authValue = Array.isArray(authHeader) ? authHeader[0] : authHeader;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
168
|
+
if (typeof authValue === "string") {
|
|
169
|
+
credentials.authorizationHeader = authValue;
|
|
170
|
+
if (authValue.startsWith("Bearer ")) {
|
|
171
|
+
credentials.jwt = authValue.slice(7);
|
|
172
|
+
}
|
|
146
173
|
}
|
|
147
174
|
}
|
|
148
175
|
if (query) {
|
|
@@ -160,7 +187,7 @@ function createGuidanceResponse(config, reason, options = {}) {
|
|
|
160
187
|
const isApiError = source === "api_error";
|
|
161
188
|
const guidance = isApiError ? {
|
|
162
189
|
message: "Verification is temporarily unavailable. Retry with exponential backoff; if the issue persists, contact support with the correlationId.",
|
|
163
|
-
registrationUrl: `${config.apiBaseUrl.replace("/api", "")}/register`,
|
|
190
|
+
registrationUrl: `${config.apiBaseUrl.replace("/api", "")}/agents/register`,
|
|
164
191
|
documentationUrl: `${config.apiBaseUrl.replace("/api", "")}/docs/agent-access`,
|
|
165
192
|
steps: [
|
|
166
193
|
"Retry the request with exponential backoff",
|
|
@@ -168,7 +195,7 @@ function createGuidanceResponse(config, reason, options = {}) {
|
|
|
168
195
|
]
|
|
169
196
|
} : {
|
|
170
197
|
message: "This service verifies AI agents before granting access. Please register your agent with AstraSync.",
|
|
171
|
-
registrationUrl: `${config.apiBaseUrl.replace("/api", "")}/register`,
|
|
198
|
+
registrationUrl: `${config.apiBaseUrl.replace("/api", "")}/agents/register`,
|
|
172
199
|
documentationUrl: `${config.apiBaseUrl.replace("/api", "")}/docs/agent-access`,
|
|
173
200
|
steps: [
|
|
174
201
|
"Register for an AstraSync account",
|
|
@@ -245,12 +272,8 @@ async function callVerifyAccessAPI(config, request) {
|
|
|
245
272
|
"Content-Type": "application/json",
|
|
246
273
|
...config.customHeaders
|
|
247
274
|
};
|
|
248
|
-
if (credentials.authorizationHeader) {
|
|
249
|
-
headers["Authorization"] = credentials.authorizationHeader;
|
|
250
|
-
} else if (config.apiKey) {
|
|
251
|
-
headers["Authorization"] = `Bearer ${config.apiKey}`;
|
|
252
|
-
}
|
|
253
275
|
if (config.apiKey) {
|
|
276
|
+
headers["Authorization"] = `Bearer ${config.apiKey}`;
|
|
254
277
|
headers["X-API-Key"] = config.apiKey;
|
|
255
278
|
}
|
|
256
279
|
try {
|
|
@@ -296,7 +319,11 @@ async function callVerifyAccessAPI(config, request) {
|
|
|
296
319
|
async function verify(config, request) {
|
|
297
320
|
const mergedConfig = { ...DEFAULT_CONFIG, ...config };
|
|
298
321
|
if (!initCheckPerformed && !mergedConfig.disableInitChecks && mergedConfig.apiBaseUrl) {
|
|
299
|
-
|
|
322
|
+
if (mergedConfig.strictInit) {
|
|
323
|
+
await performInitCheck(mergedConfig.apiBaseUrl, mergedConfig.debug, true);
|
|
324
|
+
} else {
|
|
325
|
+
void performInitCheck(mergedConfig.apiBaseUrl, mergedConfig.debug, false);
|
|
326
|
+
}
|
|
300
327
|
}
|
|
301
328
|
if (!deprecationWarningShown && (config.minTrustScore !== void 0 || config.minTrustScoreForFull !== void 0)) {
|
|
302
329
|
deprecationWarningShown = true;
|
|
@@ -350,7 +377,7 @@ async function verify(config, request) {
|
|
|
350
377
|
requiresApproval: apiResponse.access?.requiresApproval,
|
|
351
378
|
guidance: {
|
|
352
379
|
message: apiResponse.access?.reason || "Access denied by PDLSS policy",
|
|
353
|
-
registrationUrl: `${mergedConfig.apiBaseUrl?.replace("/api", "")}/register`,
|
|
380
|
+
registrationUrl: `${mergedConfig.apiBaseUrl?.replace("/api", "")}/agents/register`,
|
|
354
381
|
documentationUrl: `${mergedConfig.apiBaseUrl?.replace("/api", "")}/docs/pdlss`
|
|
355
382
|
},
|
|
356
383
|
verifiedAt: /* @__PURE__ */ new Date(),
|
|
@@ -420,13 +447,15 @@ async function verify(config, request) {
|
|
|
420
447
|
result.denialReasons = result.recommendationReasons || [
|
|
421
448
|
"Access denied by AstraSync recommendation"
|
|
422
449
|
];
|
|
423
|
-
|
|
424
|
-
result.
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
450
|
+
result.guidance = result.runtimeChallenge ? {
|
|
451
|
+
message: `Verification failed: ${result.runtimeChallenge.reason || "runtime challenge failed"}`,
|
|
452
|
+
registrationUrl: `${mergedConfig.apiBaseUrl?.replace("/api", "")}/agents/register`,
|
|
453
|
+
documentationUrl: `${mergedConfig.apiBaseUrl?.replace("/api", "")}/docs/runtime-challenge`
|
|
454
|
+
} : {
|
|
455
|
+
message: result.recommendationReasons?.[0] || "Access denied by AstraSync recommendation",
|
|
456
|
+
registrationUrl: `${mergedConfig.apiBaseUrl?.replace("/api", "")}/agents/register`,
|
|
457
|
+
documentationUrl: `${mergedConfig.apiBaseUrl?.replace("/api", "")}/docs/pdlss`
|
|
458
|
+
};
|
|
430
459
|
} else if (result.recommendation === "step_up_required") {
|
|
431
460
|
result.requiresStepUp = true;
|
|
432
461
|
if (ACCESS_LEVEL_HIERARCHY[result.accessLevel] > ACCESS_LEVEL_HIERARCHY["read-only"]) {
|
|
@@ -615,18 +644,40 @@ function defaultExtractPurpose(req) {
|
|
|
615
644
|
return "general";
|
|
616
645
|
}
|
|
617
646
|
}
|
|
618
|
-
function matchRoute(pattern, path) {
|
|
647
|
+
function matchRoute(pattern, path, opts) {
|
|
619
648
|
const regexPattern = pattern.replace(/\*/g, ".*").replace(/\//g, "\\/");
|
|
620
|
-
const
|
|
621
|
-
|
|
649
|
+
const caseSensitiveRegex = new RegExp(`^${regexPattern}$`);
|
|
650
|
+
const caseSensitiveResult = caseSensitiveRegex.test(path);
|
|
651
|
+
if (!opts?.caseInsensitive && !opts?.logShadowDivergence) {
|
|
652
|
+
return caseSensitiveResult;
|
|
653
|
+
}
|
|
654
|
+
const caseInsensitiveRegex = new RegExp(`^${regexPattern}$`, "i");
|
|
655
|
+
const caseInsensitiveResult = caseInsensitiveRegex.test(path);
|
|
656
|
+
if (opts?.logShadowDivergence && caseSensitiveResult !== caseInsensitiveResult) {
|
|
657
|
+
console.warn(
|
|
658
|
+
`[SHADOW] matchRoute case-insensitive would change result: pattern=${pattern} path=${path} caseSensitive=${caseSensitiveResult} caseInsensitive=${caseInsensitiveResult} correlationId=${opts.correlationId ?? "unknown"}`
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
return opts?.caseInsensitive ? caseInsensitiveResult : caseSensitiveResult;
|
|
622
662
|
}
|
|
623
|
-
function findRouteConfig(routes, path, method) {
|
|
663
|
+
function findRouteConfig(routes, path, method, opts) {
|
|
624
664
|
return routes.find((route) => {
|
|
625
665
|
const methodMatches = route.method === "*" || route.method.toUpperCase() === method.toUpperCase();
|
|
626
|
-
const pathMatches = matchRoute(route.pattern, path);
|
|
666
|
+
const pathMatches = matchRoute(route.pattern, path, opts);
|
|
627
667
|
return methodMatches && pathMatches;
|
|
628
668
|
});
|
|
629
669
|
}
|
|
670
|
+
function dedupeFailures(result) {
|
|
671
|
+
if (result.failures && result.failures.length > 1) {
|
|
672
|
+
const seen = /* @__PURE__ */ new Set();
|
|
673
|
+
result.failures = result.failures.filter((f) => {
|
|
674
|
+
const key = `${f.dimension}|${f.message}|${f.guidance ?? ""}`;
|
|
675
|
+
if (seen.has(key)) return false;
|
|
676
|
+
seen.add(key);
|
|
677
|
+
return true;
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
}
|
|
630
681
|
function defaultOnDenied(result, _req, res) {
|
|
631
682
|
const statusCode = !result.identityVerified ? 401 : 403;
|
|
632
683
|
res.setHeader("X-Astra-Gateway-Mode", "enforced");
|
|
@@ -653,6 +704,8 @@ function createMiddleware(options) {
|
|
|
653
704
|
recordDecisions,
|
|
654
705
|
enableRuntimeChallenge = true,
|
|
655
706
|
routesRefreshMs = DEFAULT_ROUTES_REFRESH_MS,
|
|
707
|
+
failOnError = "open",
|
|
708
|
+
caseInsensitiveRouteMatch = false,
|
|
656
709
|
...config
|
|
657
710
|
} = options;
|
|
658
711
|
let cachedRoutes = [];
|
|
@@ -675,7 +728,7 @@ function createMiddleware(options) {
|
|
|
675
728
|
cachedRoutes = fetched;
|
|
676
729
|
lastFetchAt = Date.now();
|
|
677
730
|
if (cachedRoutes.length === 0 && !warnedEmptyRoutes) {
|
|
678
|
-
const dashboard = config.dashboardUrl ?? "https://
|
|
731
|
+
const dashboard = config.dashboardUrl ?? "https://astrasync.ai/dashboard";
|
|
679
732
|
console.warn(
|
|
680
733
|
`[VerificationGateway] No route policy configured for ${config.counterpartyId}. Gateway is in pass-through mode for ALL traffic until you add at least one route. Configure at ${dashboard}/dashboard/endpoints/${config.counterpartyId}/routes`
|
|
681
734
|
);
|
|
@@ -701,7 +754,12 @@ function createMiddleware(options) {
|
|
|
701
754
|
refreshing = null;
|
|
702
755
|
});
|
|
703
756
|
}
|
|
704
|
-
const
|
|
757
|
+
const correlationId = req.headers["x-request-id"] || req.headers["x-correlation-id"];
|
|
758
|
+
const routeConfig = findRouteConfig(cachedRoutes, req.path, req.method, {
|
|
759
|
+
caseInsensitive: caseInsensitiveRouteMatch,
|
|
760
|
+
logShadowDivergence: true,
|
|
761
|
+
correlationId
|
|
762
|
+
});
|
|
705
763
|
if (!routeConfig) {
|
|
706
764
|
if (config.setPassThroughHeader) {
|
|
707
765
|
res.setHeader("X-Astra-Gateway-Mode", "unenforced");
|
|
@@ -733,7 +791,7 @@ function createMiddleware(options) {
|
|
|
733
791
|
denialReasons: preCheckFailures.map((f) => f.message),
|
|
734
792
|
guidance: {
|
|
735
793
|
message: "Request exceeds counterparty-defined PDLSS limits.",
|
|
736
|
-
registrationUrl: `${config.apiBaseUrl?.replace("/api", "")}/register`,
|
|
794
|
+
registrationUrl: `${config.apiBaseUrl?.replace("/api", "")}/agents/register`,
|
|
737
795
|
documentationUrl: `${config.apiBaseUrl?.replace("/api", "")}/docs/pdlss`
|
|
738
796
|
},
|
|
739
797
|
verifiedAt: /* @__PURE__ */ new Date()
|
|
@@ -748,18 +806,27 @@ function createMiddleware(options) {
|
|
|
748
806
|
requestMethod: req.method
|
|
749
807
|
}).catch(() => {
|
|
750
808
|
});
|
|
809
|
+
dedupeFailures(result2);
|
|
751
810
|
onDenied(result2, req, res);
|
|
752
811
|
return;
|
|
753
812
|
}
|
|
754
813
|
const shouldRecordDecisions = recordDecisions !== false;
|
|
755
814
|
const forwardedFor = req.headers["x-forwarded-for"];
|
|
756
815
|
const forwardedForStr = Array.isArray(forwardedFor) ? forwardedFor.join(", ") : forwardedFor;
|
|
757
|
-
const originalClientIp = forwardedForStr ? forwardedForStr.split(",")[0].trim() :
|
|
816
|
+
const originalClientIp = req.ip ?? (forwardedForStr ? forwardedForStr.split(",")[0].trim() : void 0);
|
|
817
|
+
if (!req.ip && forwardedForStr) {
|
|
818
|
+
console.warn(
|
|
819
|
+
"[VerificationGateway] req.ip unset \u2014 falling back to leftmost X-Forwarded-For. Configure Express trust proxy correctly to avoid spoofable client IPs in audit logs."
|
|
820
|
+
);
|
|
821
|
+
}
|
|
758
822
|
const agentCardUrl = typeof req.headers["x-astrasync-agent-card"] === "string" ? req.headers["x-astrasync-agent-card"] : void 0;
|
|
759
823
|
const result = await verify(config, {
|
|
760
824
|
credentials,
|
|
761
825
|
purpose,
|
|
762
|
-
|
|
826
|
+
// RFC 7230 § 3.1.1 — HTTP method tokens uppercase by IANA convention.
|
|
827
|
+
// Backend evaluator tolerates either case as defense-in-depth
|
|
828
|
+
// (round-18.6 batch 2); SDK emits canonical form.
|
|
829
|
+
action: req.method.toUpperCase(),
|
|
763
830
|
resource: req.path,
|
|
764
831
|
createSession: shouldRecordDecisions,
|
|
765
832
|
counterpartyUrl,
|
|
@@ -782,6 +849,7 @@ function createMiddleware(options) {
|
|
|
782
849
|
recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
|
|
783
850
|
});
|
|
784
851
|
}
|
|
852
|
+
dedupeFailures(result);
|
|
785
853
|
onDenied(result, req, res);
|
|
786
854
|
return;
|
|
787
855
|
}
|
|
@@ -808,6 +876,7 @@ function createMiddleware(options) {
|
|
|
808
876
|
recordDecision(config, sessionId, "denied", insufficientFailure.message).catch(() => {
|
|
809
877
|
});
|
|
810
878
|
}
|
|
879
|
+
dedupeFailures(result);
|
|
811
880
|
onDenied(result, req, res);
|
|
812
881
|
return;
|
|
813
882
|
}
|
|
@@ -824,6 +893,7 @@ function createMiddleware(options) {
|
|
|
824
893
|
recordDecision(config, sessionId, "denied", trustFailure.message).catch(() => {
|
|
825
894
|
});
|
|
826
895
|
}
|
|
896
|
+
dedupeFailures(result);
|
|
827
897
|
onDenied(result, req, res);
|
|
828
898
|
return;
|
|
829
899
|
}
|
|
@@ -838,7 +908,30 @@ function createMiddleware(options) {
|
|
|
838
908
|
}
|
|
839
909
|
next();
|
|
840
910
|
} catch (error) {
|
|
911
|
+
const errorClass = error instanceof Error ? error.constructor.name : typeof error;
|
|
912
|
+
const correlationId = req.headers["x-request-id"] || req.headers["x-correlation-id"] || `gen-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
841
913
|
console.error("[VerificationGateway] Middleware error:", error);
|
|
914
|
+
console.warn(
|
|
915
|
+
`[SHADOW] would-have-denied: errorClass=${errorClass} route=${req.method}:${req.path} merchantId=${config.counterpartyId ?? "unknown"} correlationId=${correlationId}`
|
|
916
|
+
);
|
|
917
|
+
if (failOnError === "closed") {
|
|
918
|
+
const result = {
|
|
919
|
+
identityVerified: false,
|
|
920
|
+
policyAllowed: false,
|
|
921
|
+
accessLevel: "none",
|
|
922
|
+
denialReasons: [`Verification middleware internal error: ${errorClass}`],
|
|
923
|
+
failures: [
|
|
924
|
+
{
|
|
925
|
+
dimension: "middleware.internal_error",
|
|
926
|
+
message: `Middleware threw ${errorClass} \u2014 failing closed`
|
|
927
|
+
}
|
|
928
|
+
],
|
|
929
|
+
verifiedAt: /* @__PURE__ */ new Date(),
|
|
930
|
+
correlationId
|
|
931
|
+
};
|
|
932
|
+
dedupeFailures(result);
|
|
933
|
+
return onDenied(result, req, res);
|
|
934
|
+
}
|
|
842
935
|
next();
|
|
843
936
|
}
|
|
844
937
|
};
|