@astrasyncai/verification-gateway 3.1.0 → 3.2.1
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 +46 -61
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +46 -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 +60 -99
- package/dist/adapters/mcp.js.map +1 -1
- package/dist/adapters/mcp.mjs +60 -99
- 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 +37 -30
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +37 -30
- 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 +87 -122
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +87 -122
- 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.1
|
|
10
|
+
var SDK_VERSION = "3.2.1";
|
|
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,38 +506,22 @@ 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
|
-
})
|
|
509
|
+
body: JSON.stringify({ decision, reason })
|
|
525
510
|
}).catch(() => {
|
|
526
511
|
});
|
|
527
512
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
overriddenBy: override.overriddenBy,
|
|
541
|
-
toolName: override.toolName,
|
|
542
|
-
requestedLevel: override.requestedLevel,
|
|
543
|
-
grantedLevel: override.grantedLevel
|
|
544
|
-
})
|
|
545
|
-
}).catch(() => {
|
|
546
|
-
});
|
|
513
|
+
|
|
514
|
+
// src/adapters/approval-gate.ts
|
|
515
|
+
var APPROVAL_REASON = "Transaction is above the autonomous limit and requires human approval, which is not yet available \u2014 it cannot be completed automatically.";
|
|
516
|
+
function requiresHumanApproval(result) {
|
|
517
|
+
return result.requiresStepUp === true || result.requiresApproval === true;
|
|
518
|
+
}
|
|
519
|
+
function annotateApprovalRequired(result) {
|
|
520
|
+
result.failures = [
|
|
521
|
+
...result.failures ?? [],
|
|
522
|
+
{ dimension: "commerce.intent.approval_required", message: APPROVAL_REASON }
|
|
523
|
+
];
|
|
524
|
+
result.denialReasons = [APPROVAL_REASON, ...result.denialReasons ?? []];
|
|
547
525
|
}
|
|
548
526
|
|
|
549
527
|
// src/transport/mcp-server.ts
|
|
@@ -758,7 +736,6 @@ function createMcpMiddleware(options) {
|
|
|
758
736
|
return next();
|
|
759
737
|
}
|
|
760
738
|
req.mcpRequest = parsed;
|
|
761
|
-
const wellKnownUrls = config.apiBaseUrl ? await getWellKnownUrls(config.apiBaseUrl).catch(() => void 0) : void 0;
|
|
762
739
|
const headerRaw = req.headers["x-astra-id"] ?? req.headers["x-astra-agentid"];
|
|
763
740
|
const headerAstraId = typeof headerRaw === "string" ? headerRaw : Array.isArray(headerRaw) ? headerRaw[0] : void 0;
|
|
764
741
|
const bodyAstraId = parsed.agentIdFromBody;
|
|
@@ -795,7 +772,7 @@ function createMcpMiddleware(options) {
|
|
|
795
772
|
return next();
|
|
796
773
|
}
|
|
797
774
|
}
|
|
798
|
-
const { level: minAccessLevel
|
|
775
|
+
const { level: minAccessLevel } = resolveMinAccessLevel(parsed, {
|
|
799
776
|
toolGates,
|
|
800
777
|
methodGates
|
|
801
778
|
});
|
|
@@ -831,6 +808,23 @@ function createMcpMiddleware(options) {
|
|
|
831
808
|
resolved_action: pdlss.action
|
|
832
809
|
});
|
|
833
810
|
}
|
|
811
|
+
if (!pdlss.purpose) {
|
|
812
|
+
const id = req.body?.id ?? null;
|
|
813
|
+
res.status(400).json({
|
|
814
|
+
jsonrpc: "2.0",
|
|
815
|
+
id,
|
|
816
|
+
error: {
|
|
817
|
+
code: -32602,
|
|
818
|
+
message: "PDLSS_PURPOSE_REQUIRED",
|
|
819
|
+
data: {
|
|
820
|
+
dimension: "pdlss.purpose",
|
|
821
|
+
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.",
|
|
822
|
+
resolvedAction: pdlss.action
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
});
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
834
828
|
const counterpartyUrl = config.counterpartyUrl || `${req.protocol}://${req.get("host")}${req.path}`;
|
|
835
829
|
const shouldRecordDecisions = recordDecisions !== false;
|
|
836
830
|
const result = await verify(config, {
|
|
@@ -854,7 +848,6 @@ function createMcpMiddleware(options) {
|
|
|
854
848
|
});
|
|
855
849
|
req.agentVerification = result;
|
|
856
850
|
const sessionId = result.sessionId;
|
|
857
|
-
const correlationId = result.correlationId;
|
|
858
851
|
if (!result.identityVerified || !result.policyAllowed) {
|
|
859
852
|
if (shouldRecordDecisions && sessionId) {
|
|
860
853
|
recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
|
|
@@ -864,6 +857,16 @@ function createMcpMiddleware(options) {
|
|
|
864
857
|
onDenied(result, req, res);
|
|
865
858
|
return;
|
|
866
859
|
}
|
|
860
|
+
if (requiresHumanApproval(result)) {
|
|
861
|
+
annotateApprovalRequired(result);
|
|
862
|
+
if (shouldRecordDecisions && sessionId) {
|
|
863
|
+
recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
dedupeFailures(result);
|
|
867
|
+
onDenied(result, req, res);
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
867
870
|
if (!shouldEnforce) {
|
|
868
871
|
if (config.setPassThroughHeader) {
|
|
869
872
|
res.setHeader("X-Astra-Gateway-Mode", "enforced");
|
|
@@ -875,48 +878,6 @@ function createMcpMiddleware(options) {
|
|
|
875
878
|
}
|
|
876
879
|
return next();
|
|
877
880
|
}
|
|
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
881
|
if (effectiveAstraId) {
|
|
921
882
|
res.setHeader(
|
|
922
883
|
MCP_VERIFIED_HOP_HEADER,
|