@astrasyncai/verification-gateway 3.1.0 → 3.2.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/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 +23 -61
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +23 -61
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/mcp.d.mts +12 -7
- package/dist/adapters/mcp.d.ts +12 -7
- package/dist/adapters/mcp.js +38 -100
- package/dist/adapters/mcp.js.map +1 -1
- package/dist/adapters/mcp.mjs +38 -100
- 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 +20 -29
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +20 -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 +25 -14
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +25 -14
- 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/browser/background.js +18 -21
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +18 -21
- 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 +18 -21
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +18 -21
- package/dist/cursor/extension.mjs.map +1 -1
- package/dist/{express-DavQ76oF.d.ts → express-BowlMHQF.d.ts} +1 -1
- package/dist/{express-DFVBlXr_.d.mts → express-CeoSdOAZ.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 +18 -21
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +18 -21
- 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-BhL2R65s.d.mts → index-B51W8gn8.d.mts} +1 -1
- package/dist/{index-BhEgEiJL.d.ts → index-DBmlycVm.d.ts} +1 -1
- package/dist/{index-BVxantdv.d.mts → index-DtGziFEm.d.mts} +1 -1
- package/dist/{index-Dk2nIA4w.d.ts → index-DzXXBuLm.d.ts} +1 -1
- package/dist/index.d.mts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +50 -121
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +50 -121
- 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/{nextjs-D-maqrNz.d.mts → nextjs-BW1rzr1I.d.mts} +1 -1
- package/dist/{nextjs-BXLH1hJj.d.ts → nextjs-V_K0qlAQ.d.ts} +1 -1
- package/dist/{sdk-767LaEP8.d.mts → sdk-ZYgI7G9f.d.ts} +14 -3
- package/dist/{sdk-K8IgssHI.d.ts → sdk-e5jg7sqW.d.mts} +14 -3
- package/dist/transport/index.d.mts +2 -2
- package/dist/transport/index.d.ts +2 -2
- package/dist/{types-CyFwZ_Yu.d.mts → types-BNiLZY0i.d.mts} +1 -1
- package/dist/{types-WIRp_BP_.d.ts → types-DJi-u3fz.d.ts} +1 -1
- package/dist/{types-Cuh7ELfr.d.mts → types-rFh4VMH4.d.mts} +5 -2
- package/dist/{types-Cuh7ELfr.d.ts → types-rFh4VMH4.d.ts} +5 -2
- package/dist/ui/index.d.mts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/package.json +1 -1
package/dist/adapters/mcp.mjs
CHANGED
|
@@ -1,24 +1,13 @@
|
|
|
1
1
|
// src/access-levels.ts
|
|
2
|
-
var ACCESS_LEVEL_HIERARCHY = {
|
|
3
|
-
none: 0,
|
|
4
|
-
restricted: 1,
|
|
5
|
-
"read-only": 2,
|
|
6
|
-
standard: 3,
|
|
7
|
-
full: 4,
|
|
8
|
-
internal: 5
|
|
9
|
-
};
|
|
10
2
|
function getTrustLevel(score) {
|
|
11
3
|
if (score >= 80) return "PLATINUM";
|
|
12
4
|
if (score >= 60) return "GOLD";
|
|
13
5
|
if (score >= 40) return "SILVER";
|
|
14
6
|
return "BRONZE";
|
|
15
7
|
}
|
|
16
|
-
function hasMinimumAccess(actual, required) {
|
|
17
|
-
return ACCESS_LEVEL_HIERARCHY[actual] >= ACCESS_LEVEL_HIERARCHY[required];
|
|
18
|
-
}
|
|
19
8
|
|
|
20
9
|
// src/version.ts
|
|
21
|
-
var SDK_VERSION = "3.
|
|
10
|
+
var SDK_VERSION = "3.2.0";
|
|
22
11
|
|
|
23
12
|
// src/well-known.ts
|
|
24
13
|
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
@@ -123,7 +112,7 @@ async function performInitCheck(apiBaseUrl, debug, strictInit) {
|
|
|
123
112
|
}
|
|
124
113
|
}
|
|
125
114
|
var verificationCache = /* @__PURE__ */ new Map();
|
|
126
|
-
function getCacheKey(request) {
|
|
115
|
+
function getCacheKey(request, counterpartyId) {
|
|
127
116
|
const c = request.credentials;
|
|
128
117
|
return [
|
|
129
118
|
c.astraId || "",
|
|
@@ -136,6 +125,14 @@ function getCacheKey(request) {
|
|
|
136
125
|
request.jurisdiction || "",
|
|
137
126
|
request.transactionValue ?? "",
|
|
138
127
|
request.currency || "",
|
|
128
|
+
// SECURITY (cross-merchant cache leak): the merchant identity is sent via
|
|
129
|
+
// `config.counterpartyId`, NOT on the request, so it was previously absent
|
|
130
|
+
// from the key — two verifies for the SAME agent/purpose/action/value but
|
|
131
|
+
// DIFFERENT merchants collided, and a grant at a permissive merchant (low
|
|
132
|
+
// trust floor) was served for a stricter one. Same bug class as the
|
|
133
|
+
// duration omission (F-A1-07). counterpartyId affects the backend verdict
|
|
134
|
+
// (trust floor / per-route policy), so it MUST key the cache.
|
|
135
|
+
counterpartyId || "",
|
|
139
136
|
request.counterpartyUrl || "",
|
|
140
137
|
request.counterpartyType || "",
|
|
141
138
|
request.isSubAgentRequest ? "1" : "0",
|
|
@@ -159,8 +156,8 @@ function getCacheKey(request) {
|
|
|
159
156
|
request.callerMetadata?.agentCardUrl || ""
|
|
160
157
|
].join("|");
|
|
161
158
|
}
|
|
162
|
-
function getCachedResult(request) {
|
|
163
|
-
const key = getCacheKey(request);
|
|
159
|
+
function getCachedResult(request, counterpartyId) {
|
|
160
|
+
const key = getCacheKey(request, counterpartyId);
|
|
164
161
|
const cached = verificationCache.get(key);
|
|
165
162
|
if (cached && cached.expiresAt > Date.now()) {
|
|
166
163
|
return cached.result;
|
|
@@ -172,9 +169,9 @@ function getCachedResult(request) {
|
|
|
172
169
|
}
|
|
173
170
|
var DEFAULT_AUTONOMOUS_TTL_SECONDS = 60;
|
|
174
171
|
var DEFAULT_STEP_UP_TTL_SECONDS = 300;
|
|
175
|
-
function cacheResult(request, result, configuredTtl) {
|
|
172
|
+
function cacheResult(request, result, configuredTtl, counterpartyId) {
|
|
176
173
|
const ttlSeconds = configuredTtl && configuredTtl > 0 ? configuredTtl : result.requiresStepUp ? DEFAULT_STEP_UP_TTL_SECONDS : DEFAULT_AUTONOMOUS_TTL_SECONDS;
|
|
177
|
-
const key = getCacheKey(request);
|
|
174
|
+
const key = getCacheKey(request, counterpartyId);
|
|
178
175
|
verificationCache.set(key, {
|
|
179
176
|
result,
|
|
180
177
|
expiresAt: Date.now() + ttlSeconds * 1e3
|
|
@@ -366,7 +363,7 @@ async function verify(config, request) {
|
|
|
366
363
|
);
|
|
367
364
|
}
|
|
368
365
|
if (mergedConfig.cacheTtl !== 0) {
|
|
369
|
-
const cached = getCachedResult(request);
|
|
366
|
+
const cached = getCachedResult(request, mergedConfig.counterpartyId);
|
|
370
367
|
if (cached) {
|
|
371
368
|
if (mergedConfig.debug) {
|
|
372
369
|
console.log("[VerificationGateway] Returning cached result");
|
|
@@ -418,8 +415,8 @@ async function verify(config, request) {
|
|
|
418
415
|
verifiedAt: /* @__PURE__ */ new Date(),
|
|
419
416
|
// Extract sessionId so decisions can be recorded for denials too
|
|
420
417
|
sessionId: apiResponse.sessionId,
|
|
421
|
-
//
|
|
422
|
-
//
|
|
418
|
+
// Anonymous traffic has no session → correlationId is the per-attempt
|
|
419
|
+
// linking key (the sessionId-equivalent for anonymous callers).
|
|
423
420
|
correlationId: apiResponse.correlationId,
|
|
424
421
|
recommendation: apiResponse.recommendation,
|
|
425
422
|
recommendationReasons: apiResponse.recommendationReasons
|
|
@@ -493,17 +490,14 @@ async function verify(config, request) {
|
|
|
493
490
|
};
|
|
494
491
|
} else if (result.recommendation === "step_up_required") {
|
|
495
492
|
result.requiresStepUp = true;
|
|
496
|
-
if (ACCESS_LEVEL_HIERARCHY[result.accessLevel] > ACCESS_LEVEL_HIERARCHY["read-only"]) {
|
|
497
|
-
result.accessLevel = "read-only";
|
|
498
|
-
}
|
|
499
493
|
result.denialReasons = result.recommendationReasons || ["Step-up verification required"];
|
|
500
494
|
}
|
|
501
495
|
if (mergedConfig.cacheTtl !== 0 && result.recommendation !== "deny") {
|
|
502
|
-
cacheResult(request, result, mergedConfig.cacheTtl);
|
|
496
|
+
cacheResult(request, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);
|
|
503
497
|
}
|
|
504
498
|
return result;
|
|
505
499
|
}
|
|
506
|
-
async function recordDecision(config, sessionId, decision, reason
|
|
500
|
+
async function recordDecision(config, sessionId, decision, reason) {
|
|
507
501
|
const headers = { "Content-Type": "application/json" };
|
|
508
502
|
if (config.apiKey) {
|
|
509
503
|
headers["Authorization"] = `Bearer ${config.apiKey}`;
|
|
@@ -512,36 +506,7 @@ async function recordDecision(config, sessionId, decision, reason, override) {
|
|
|
512
506
|
await fetch(`${config.apiBaseUrl}/agents/verify-access/${sessionId}/decision`, {
|
|
513
507
|
method: "POST",
|
|
514
508
|
headers,
|
|
515
|
-
body: JSON.stringify({
|
|
516
|
-
decision,
|
|
517
|
-
reason,
|
|
518
|
-
...override && {
|
|
519
|
-
overriddenBy: override.overriddenBy,
|
|
520
|
-
toolName: override.toolName,
|
|
521
|
-
requestedLevel: override.requestedLevel,
|
|
522
|
-
grantedLevel: override.grantedLevel
|
|
523
|
-
}
|
|
524
|
-
})
|
|
525
|
-
}).catch(() => {
|
|
526
|
-
});
|
|
527
|
-
}
|
|
528
|
-
async function recordAnonymousLocalOverride(config, correlationId, override, reason) {
|
|
529
|
-
const headers = { "Content-Type": "application/json" };
|
|
530
|
-
if (config.apiKey) {
|
|
531
|
-
headers["Authorization"] = `Bearer ${config.apiKey}`;
|
|
532
|
-
headers["X-API-Key"] = config.apiKey;
|
|
533
|
-
}
|
|
534
|
-
await fetch(`${config.apiBaseUrl}/agents/verify-access/local-override`, {
|
|
535
|
-
method: "POST",
|
|
536
|
-
headers,
|
|
537
|
-
body: JSON.stringify({
|
|
538
|
-
correlationId,
|
|
539
|
-
reason,
|
|
540
|
-
overriddenBy: override.overriddenBy,
|
|
541
|
-
toolName: override.toolName,
|
|
542
|
-
requestedLevel: override.requestedLevel,
|
|
543
|
-
grantedLevel: override.grantedLevel
|
|
544
|
-
})
|
|
509
|
+
body: JSON.stringify({ decision, reason })
|
|
545
510
|
}).catch(() => {
|
|
546
511
|
});
|
|
547
512
|
}
|
|
@@ -758,7 +723,6 @@ function createMcpMiddleware(options) {
|
|
|
758
723
|
return next();
|
|
759
724
|
}
|
|
760
725
|
req.mcpRequest = parsed;
|
|
761
|
-
const wellKnownUrls = config.apiBaseUrl ? await getWellKnownUrls(config.apiBaseUrl).catch(() => void 0) : void 0;
|
|
762
726
|
const headerRaw = req.headers["x-astra-id"] ?? req.headers["x-astra-agentid"];
|
|
763
727
|
const headerAstraId = typeof headerRaw === "string" ? headerRaw : Array.isArray(headerRaw) ? headerRaw[0] : void 0;
|
|
764
728
|
const bodyAstraId = parsed.agentIdFromBody;
|
|
@@ -795,7 +759,7 @@ function createMcpMiddleware(options) {
|
|
|
795
759
|
return next();
|
|
796
760
|
}
|
|
797
761
|
}
|
|
798
|
-
const { level: minAccessLevel
|
|
762
|
+
const { level: minAccessLevel } = resolveMinAccessLevel(parsed, {
|
|
799
763
|
toolGates,
|
|
800
764
|
methodGates
|
|
801
765
|
});
|
|
@@ -831,6 +795,23 @@ function createMcpMiddleware(options) {
|
|
|
831
795
|
resolved_action: pdlss.action
|
|
832
796
|
});
|
|
833
797
|
}
|
|
798
|
+
if (!pdlss.purpose) {
|
|
799
|
+
const id = req.body?.id ?? null;
|
|
800
|
+
res.status(400).json({
|
|
801
|
+
jsonrpc: "2.0",
|
|
802
|
+
id,
|
|
803
|
+
error: {
|
|
804
|
+
code: -32602,
|
|
805
|
+
message: "PDLSS_PURPOSE_REQUIRED",
|
|
806
|
+
data: {
|
|
807
|
+
dimension: "pdlss.purpose",
|
|
808
|
+
detail: "This tool is access-gated but the call declared no PDLSS purpose. Supply a bare-category purpose via the X-Astra-Purpose header or params._meta.astrasync.purpose, or have the merchant set the tool\u2019s purpose in its toolGate config.",
|
|
809
|
+
resolvedAction: pdlss.action
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
});
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
834
815
|
const counterpartyUrl = config.counterpartyUrl || `${req.protocol}://${req.get("host")}${req.path}`;
|
|
835
816
|
const shouldRecordDecisions = recordDecisions !== false;
|
|
836
817
|
const result = await verify(config, {
|
|
@@ -854,7 +835,6 @@ function createMcpMiddleware(options) {
|
|
|
854
835
|
});
|
|
855
836
|
req.agentVerification = result;
|
|
856
837
|
const sessionId = result.sessionId;
|
|
857
|
-
const correlationId = result.correlationId;
|
|
858
838
|
if (!result.identityVerified || !result.policyAllowed) {
|
|
859
839
|
if (shouldRecordDecisions && sessionId) {
|
|
860
840
|
recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
|
|
@@ -875,48 +855,6 @@ function createMcpMiddleware(options) {
|
|
|
875
855
|
}
|
|
876
856
|
return next();
|
|
877
857
|
}
|
|
878
|
-
if (!hasMinimumAccess(result.accessLevel, minAccessLevel)) {
|
|
879
|
-
const insufficientFailure = {
|
|
880
|
-
dimension: "access_level.insufficient",
|
|
881
|
-
message: `Tool requires accessLevel '${minAccessLevel}'; agent has '${result.accessLevel}'.`,
|
|
882
|
-
guidance: "Request elevated access via step-up verification (coming soon \u2014 ships this month). Step-up lets the agent owner approve a one-time elevation for this specific counterparty + purpose without changing the agent's baseline trust score."
|
|
883
|
-
};
|
|
884
|
-
result.failures = [...result.failures ?? [], insufficientFailure];
|
|
885
|
-
result.denialReasons = [...result.denialReasons ?? [], insufficientFailure.message];
|
|
886
|
-
if (!result.guidance && wellKnownUrls) {
|
|
887
|
-
result.guidance = {
|
|
888
|
-
message: insufficientFailure.message,
|
|
889
|
-
registrationUrl: wellKnownUrls.registrationUrl,
|
|
890
|
-
documentationUrl: wellKnownUrls.documentationUrl
|
|
891
|
-
};
|
|
892
|
-
}
|
|
893
|
-
if (shouldRecordDecisions) {
|
|
894
|
-
const overrideKind = gateSource === "toolGate" ? "toolGate" : gateSource === "methodGate" ? "methodGate" : "other";
|
|
895
|
-
const override = {
|
|
896
|
-
overriddenBy: overrideKind,
|
|
897
|
-
...parsed.toolName && { toolName: parsed.toolName },
|
|
898
|
-
requestedLevel: minAccessLevel,
|
|
899
|
-
grantedLevel: result.accessLevel
|
|
900
|
-
};
|
|
901
|
-
if (sessionId) {
|
|
902
|
-
recordDecision(config, sessionId, "denied", result.denialReasons?.[0], override).catch(
|
|
903
|
-
() => {
|
|
904
|
-
}
|
|
905
|
-
);
|
|
906
|
-
} else if (correlationId) {
|
|
907
|
-
recordAnonymousLocalOverride(
|
|
908
|
-
config,
|
|
909
|
-
correlationId,
|
|
910
|
-
override,
|
|
911
|
-
result.denialReasons?.[0]
|
|
912
|
-
).catch(() => {
|
|
913
|
-
});
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
dedupeFailures(result);
|
|
917
|
-
onDenied(result, req, res);
|
|
918
|
-
return;
|
|
919
|
-
}
|
|
920
858
|
if (effectiveAstraId) {
|
|
921
859
|
res.setHeader(
|
|
922
860
|
MCP_VERIFIED_HOP_HEADER,
|