@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.
Files changed (79) hide show
  1. package/dist/adapter-interface/interface.d.mts +2 -2
  2. package/dist/adapter-interface/interface.d.ts +2 -2
  3. package/dist/adapters/express.d.mts +2 -2
  4. package/dist/adapters/express.d.ts +2 -2
  5. package/dist/adapters/express.js +23 -61
  6. package/dist/adapters/express.js.map +1 -1
  7. package/dist/adapters/express.mjs +23 -61
  8. package/dist/adapters/express.mjs.map +1 -1
  9. package/dist/adapters/mcp.d.mts +12 -7
  10. package/dist/adapters/mcp.d.ts +12 -7
  11. package/dist/adapters/mcp.js +38 -100
  12. package/dist/adapters/mcp.js.map +1 -1
  13. package/dist/adapters/mcp.mjs +38 -100
  14. package/dist/adapters/mcp.mjs.map +1 -1
  15. package/dist/adapters/nextjs.d.mts +2 -2
  16. package/dist/adapters/nextjs.d.ts +2 -2
  17. package/dist/adapters/nextjs.js +20 -29
  18. package/dist/adapters/nextjs.js.map +1 -1
  19. package/dist/adapters/nextjs.mjs +20 -29
  20. package/dist/adapters/nextjs.mjs.map +1 -1
  21. package/dist/adapters/sdk.d.mts +2 -2
  22. package/dist/adapters/sdk.d.ts +2 -2
  23. package/dist/adapters/sdk.js +25 -14
  24. package/dist/adapters/sdk.js.map +1 -1
  25. package/dist/adapters/sdk.mjs +25 -14
  26. package/dist/adapters/sdk.mjs.map +1 -1
  27. package/dist/agent/index.d.mts +2 -2
  28. package/dist/agent/index.d.ts +2 -2
  29. package/dist/browser/background.js +18 -21
  30. package/dist/browser/background.js.map +1 -1
  31. package/dist/browser/background.mjs +18 -21
  32. package/dist/browser/background.mjs.map +1 -1
  33. package/dist/browser/browser-adapter.d.mts +2 -2
  34. package/dist/browser/browser-adapter.d.ts +2 -2
  35. package/dist/cli/index.d.mts +2 -2
  36. package/dist/cli/index.d.ts +2 -2
  37. package/dist/cursor/cursor-adapter.d.mts +2 -2
  38. package/dist/cursor/cursor-adapter.d.ts +2 -2
  39. package/dist/cursor/extension.d.mts +2 -2
  40. package/dist/cursor/extension.d.ts +2 -2
  41. package/dist/cursor/extension.js +18 -21
  42. package/dist/cursor/extension.js.map +1 -1
  43. package/dist/cursor/extension.mjs +18 -21
  44. package/dist/cursor/extension.mjs.map +1 -1
  45. package/dist/{express-DavQ76oF.d.ts → express-BowlMHQF.d.ts} +1 -1
  46. package/dist/{express-DFVBlXr_.d.mts → express-CeoSdOAZ.d.mts} +1 -1
  47. package/dist/gateway/gateway.d.mts +2 -2
  48. package/dist/gateway/gateway.d.ts +2 -2
  49. package/dist/gateway/gateway.js +18 -21
  50. package/dist/gateway/gateway.js.map +1 -1
  51. package/dist/gateway/gateway.mjs +18 -21
  52. package/dist/gateway/gateway.mjs.map +1 -1
  53. package/dist/git-trigger/git-hooks.d.mts +2 -2
  54. package/dist/git-trigger/git-hooks.d.ts +2 -2
  55. package/dist/{index-BhL2R65s.d.mts → index-B51W8gn8.d.mts} +1 -1
  56. package/dist/{index-BhEgEiJL.d.ts → index-DBmlycVm.d.ts} +1 -1
  57. package/dist/{index-BVxantdv.d.mts → index-DtGziFEm.d.mts} +1 -1
  58. package/dist/{index-Dk2nIA4w.d.ts → index-DzXXBuLm.d.ts} +1 -1
  59. package/dist/index.d.mts +7 -7
  60. package/dist/index.d.ts +7 -7
  61. package/dist/index.js +50 -121
  62. package/dist/index.js.map +1 -1
  63. package/dist/index.mjs +50 -121
  64. package/dist/index.mjs.map +1 -1
  65. package/dist/local-evaluator/evaluator.d.mts +2 -2
  66. package/dist/local-evaluator/evaluator.d.ts +2 -2
  67. package/dist/{nextjs-D-maqrNz.d.mts → nextjs-BW1rzr1I.d.mts} +1 -1
  68. package/dist/{nextjs-BXLH1hJj.d.ts → nextjs-V_K0qlAQ.d.ts} +1 -1
  69. package/dist/{sdk-767LaEP8.d.mts → sdk-ZYgI7G9f.d.ts} +14 -3
  70. package/dist/{sdk-K8IgssHI.d.ts → sdk-e5jg7sqW.d.mts} +14 -3
  71. package/dist/transport/index.d.mts +2 -2
  72. package/dist/transport/index.d.ts +2 -2
  73. package/dist/{types-CyFwZ_Yu.d.mts → types-BNiLZY0i.d.mts} +1 -1
  74. package/dist/{types-WIRp_BP_.d.ts → types-DJi-u3fz.d.ts} +1 -1
  75. package/dist/{types-Cuh7ELfr.d.mts → types-rFh4VMH4.d.mts} +5 -2
  76. package/dist/{types-Cuh7ELfr.d.ts → types-rFh4VMH4.d.ts} +5 -2
  77. package/dist/ui/index.d.mts +1 -1
  78. package/dist/ui/index.d.ts +1 -1
  79. package/package.json +1 -1
@@ -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.0";
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
- // v2.3.10 (defect #34, round-4): anonymous traffic has no session →
422
- // correlationId is the linking key for paired local_override events.
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, override) {
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, source: gateSource } = resolveMinAccessLevel(parsed, {
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,