@astrasyncai/verification-gateway 3.0.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 (87) 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 +145 -93
  6. package/dist/adapters/express.js.map +1 -1
  7. package/dist/adapters/express.mjs +145 -93
  8. package/dist/adapters/express.mjs.map +1 -1
  9. package/dist/adapters/mcp.d.mts +29 -11
  10. package/dist/adapters/mcp.d.ts +29 -11
  11. package/dist/adapters/mcp.js +43 -102
  12. package/dist/adapters/mcp.js.map +1 -1
  13. package/dist/adapters/mcp.mjs +43 -102
  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 +126 -56
  18. package/dist/adapters/nextjs.js.map +1 -1
  19. package/dist/adapters/nextjs.mjs +126 -56
  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/agent/index.js +3 -0
  30. package/dist/agent/index.js.map +1 -1
  31. package/dist/agent/index.mjs +3 -0
  32. package/dist/agent/index.mjs.map +1 -1
  33. package/dist/browser/background.js +18 -21
  34. package/dist/browser/background.js.map +1 -1
  35. package/dist/browser/background.mjs +18 -21
  36. package/dist/browser/background.mjs.map +1 -1
  37. package/dist/browser/browser-adapter.d.mts +2 -2
  38. package/dist/browser/browser-adapter.d.ts +2 -2
  39. package/dist/cli/index.d.mts +2 -2
  40. package/dist/cli/index.d.ts +2 -2
  41. package/dist/cursor/cursor-adapter.d.mts +2 -2
  42. package/dist/cursor/cursor-adapter.d.ts +2 -2
  43. package/dist/cursor/extension.d.mts +2 -2
  44. package/dist/cursor/extension.d.ts +2 -2
  45. package/dist/cursor/extension.js +18 -21
  46. package/dist/cursor/extension.js.map +1 -1
  47. package/dist/cursor/extension.mjs +18 -21
  48. package/dist/cursor/extension.mjs.map +1 -1
  49. package/dist/{express-CrfwoNAR.d.ts → express-BowlMHQF.d.ts} +1 -1
  50. package/dist/{express-ienhAXps.d.mts → express-CeoSdOAZ.d.mts} +1 -1
  51. package/dist/gateway/gateway.d.mts +2 -2
  52. package/dist/gateway/gateway.d.ts +2 -2
  53. package/dist/gateway/gateway.js +18 -21
  54. package/dist/gateway/gateway.js.map +1 -1
  55. package/dist/gateway/gateway.mjs +18 -21
  56. package/dist/gateway/gateway.mjs.map +1 -1
  57. package/dist/git-trigger/git-hooks.d.mts +2 -2
  58. package/dist/git-trigger/git-hooks.d.ts +2 -2
  59. package/dist/{index-CEg_WG6y.d.mts → index-B51W8gn8.d.mts} +1 -1
  60. package/dist/{index-DC5f8eoQ.d.ts → index-DBmlycVm.d.ts} +1 -1
  61. package/dist/{index-B5e2IDWU.d.mts → index-DtGziFEm.d.mts} +1 -1
  62. package/dist/{index-CCdZxvAr.d.ts → index-DzXXBuLm.d.ts} +1 -1
  63. package/dist/index.d.mts +7 -7
  64. package/dist/index.d.ts +7 -7
  65. package/dist/index.js +209 -191
  66. package/dist/index.js.map +1 -1
  67. package/dist/index.mjs +209 -191
  68. package/dist/index.mjs.map +1 -1
  69. package/dist/local-evaluator/evaluator.d.mts +2 -2
  70. package/dist/local-evaluator/evaluator.d.ts +2 -2
  71. package/dist/{nextjs-DSpisQst.d.mts → nextjs-BW1rzr1I.d.mts} +1 -1
  72. package/dist/{nextjs-66R1KW8e.d.ts → nextjs-V_K0qlAQ.d.ts} +1 -1
  73. package/dist/{sdk-5U_CBRpr.d.mts → sdk-ZYgI7G9f.d.ts} +14 -3
  74. package/dist/{sdk-Bm8np66n.d.ts → sdk-e5jg7sqW.d.mts} +14 -3
  75. package/dist/transport/index.d.mts +2 -2
  76. package/dist/transport/index.d.ts +2 -2
  77. package/dist/transport/index.js +10 -0
  78. package/dist/transport/index.js.map +1 -1
  79. package/dist/transport/index.mjs +10 -0
  80. package/dist/transport/index.mjs.map +1 -1
  81. package/dist/{types-CgDCUfo8.d.mts → types-BNiLZY0i.d.mts} +1 -1
  82. package/dist/{types-R5N4ET6x.d.ts → types-DJi-u3fz.d.ts} +1 -1
  83. package/dist/{types-B3USs-Kx.d.mts → types-rFh4VMH4.d.mts} +30 -2
  84. package/dist/{types-B3USs-Kx.d.ts → types-rFh4VMH4.d.ts} +30 -2
  85. package/dist/ui/index.d.mts +1 -1
  86. package/dist/ui/index.d.ts +1 -1
  87. 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.0.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,16 +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
- })
509
+ body: JSON.stringify({ decision, reason })
525
510
  }).catch(() => {
526
511
  });
527
512
  }
@@ -576,6 +561,13 @@ function extractHttpCredentials(headers) {
576
561
  purpose: { category, action }
577
562
  };
578
563
  }
564
+ const astraAction = getValue(`${HEADER_PREFIX}Action`) ?? getValue("x-astra-action");
565
+ if (astraAction) {
566
+ credentials.pdlss = {
567
+ ...credentials.pdlss,
568
+ purpose: { category: credentials.pdlss?.purpose?.category ?? "", action: astraAction }
569
+ };
570
+ }
579
571
  const duration = getValue(`${HEADER_PREFIX}Duration`) ?? getValue("x-astra-duration");
580
572
  if (duration) {
581
573
  credentials.pdlss = {
@@ -593,6 +585,85 @@ function extractHttpCredentials(headers) {
593
585
  return credentials;
594
586
  }
595
587
 
588
+ // src/adapters/http-pdlss.ts
589
+ var HTTP_METHOD_ACTION_TABLE = {
590
+ GET: "data.read",
591
+ HEAD: "data.read",
592
+ OPTIONS: "data.read",
593
+ POST: "data.write",
594
+ PUT: "data.write",
595
+ PATCH: "data.write",
596
+ DELETE: "data.delete"
597
+ };
598
+ var DEFAULT_HTTP_ACTION = "data.write";
599
+ var DEFAULT_HTTP_PURPOSE = "data";
600
+ function actionForHttpMethod(method) {
601
+ return HTTP_METHOD_ACTION_TABLE[method.toUpperCase()] ?? DEFAULT_HTTP_ACTION;
602
+ }
603
+ function normalizePurposeHeader(value) {
604
+ const colon = value.indexOf(":");
605
+ if (colon >= 0) {
606
+ return { purpose: value.slice(0, colon) };
607
+ }
608
+ const dot = value.indexOf(".");
609
+ if (dot > 0 && dot < value.length - 1) {
610
+ return { purpose: value.slice(0, dot), actionCandidate: value };
611
+ }
612
+ return { purpose: value };
613
+ }
614
+ function resolveHttpPdlss(input) {
615
+ const fromHeader = input.astraPurpose ? normalizePurposeHeader(input.astraPurpose) : void 0;
616
+ let action;
617
+ let actionSource;
618
+ if (input.routeAction) {
619
+ action = input.routeAction;
620
+ actionSource = "route_config";
621
+ } else if (input.hasCustomActionExtractor && input.customAction) {
622
+ action = input.customAction;
623
+ actionSource = "custom_extractor";
624
+ } else if (!input.hasCustomActionExtractor && input.astraAction) {
625
+ action = input.astraAction;
626
+ actionSource = "header";
627
+ } else if (!input.hasCustomActionExtractor && fromHeader?.actionCandidate) {
628
+ action = fromHeader.actionCandidate;
629
+ actionSource = "purpose_header_derived";
630
+ } else {
631
+ action = actionForHttpMethod(input.method);
632
+ actionSource = "method_table";
633
+ }
634
+ let purpose;
635
+ let purposeSource;
636
+ if (input.routePurpose) {
637
+ purpose = input.routePurpose;
638
+ purposeSource = "route_config";
639
+ } else if (input.hasCustomPurposeExtractor) {
640
+ if (input.customPurpose) {
641
+ purpose = input.customPurpose;
642
+ purposeSource = "custom_extractor";
643
+ }
644
+ } else if (fromHeader) {
645
+ purpose = fromHeader.purpose;
646
+ purposeSource = "header";
647
+ } else if (input.legacyPurpose) {
648
+ purpose = input.legacyPurpose;
649
+ purposeSource = "legacy_header";
650
+ } else if (input.queryPurpose) {
651
+ purpose = input.queryPurpose;
652
+ purposeSource = "query";
653
+ }
654
+ if (!purpose) {
655
+ const dot = action.indexOf(".");
656
+ if (dot > 0) {
657
+ purpose = action.slice(0, dot);
658
+ purposeSource = "action_derived";
659
+ } else {
660
+ purpose = DEFAULT_HTTP_PURPOSE;
661
+ purposeSource = "transport_default";
662
+ }
663
+ }
664
+ return { purpose, action, purposeSource, actionSource };
665
+ }
666
+
596
667
  // src/pdlss-pre-check.ts
597
668
  function performCounterpartyPreCheck(routeConfig, astraCreds, purpose) {
598
669
  const failures = [];
@@ -651,33 +722,25 @@ function defaultExtractCredentials(req) {
651
722
  function extractAstraSyncCredentials(req) {
652
723
  return extractHttpCredentials(req.headers);
653
724
  }
654
- function defaultExtractPurpose(req) {
655
- const astraPurpose = req.headers["x-astra-purpose"];
656
- if (astraPurpose) {
657
- const value = Array.isArray(astraPurpose) ? astraPurpose[0] : astraPurpose;
658
- const category = value.split(":")[0];
659
- return category;
660
- }
661
- const purposeHeader = req.headers["x-purpose"] || req.headers["X-Purpose"];
662
- if (purposeHeader) {
663
- return Array.isArray(purposeHeader) ? purposeHeader[0] : purposeHeader;
664
- }
665
- if (req.query.purpose && typeof req.query.purpose === "string") {
666
- return req.query.purpose;
667
- }
668
- switch (req.method) {
669
- case "GET":
670
- return "read_data";
671
- case "POST":
672
- return "write_data";
673
- case "PUT":
674
- case "PATCH":
675
- return "write_data";
676
- case "DELETE":
677
- return "delete_data";
678
- default:
679
- return "general";
680
- }
725
+ function headerValue(value) {
726
+ if (typeof value === "string") return value;
727
+ if (Array.isArray(value)) return value[0];
728
+ return void 0;
729
+ }
730
+ function resolveRequestPdlss(req, routeConfig, customExtractPurpose, customExtractAction) {
731
+ return resolveHttpPdlss({
732
+ method: req.method,
733
+ astraPurpose: headerValue(req.headers["x-astra-purpose"]),
734
+ astraAction: headerValue(req.headers["x-astra-action"]),
735
+ legacyPurpose: headerValue(req.headers["x-purpose"] ?? req.headers["X-Purpose"]),
736
+ queryPurpose: typeof req.query.purpose === "string" ? req.query.purpose : void 0,
737
+ routePurpose: routeConfig?.purpose,
738
+ routeAction: routeConfig?.action,
739
+ hasCustomPurposeExtractor: !!customExtractPurpose,
740
+ customPurpose: customExtractPurpose?.(req),
741
+ hasCustomActionExtractor: !!customExtractAction,
742
+ customAction: customExtractAction?.(req)
743
+ });
681
744
  }
682
745
  function matchRoute(pattern, path, opts) {
683
746
  const regexPattern = pattern.replace(/\*/g, ".*").replace(/\//g, "\\/");
@@ -737,6 +800,7 @@ function createMiddleware(options) {
737
800
  const {
738
801
  extractCredentials: customExtractCredentials,
739
802
  extractPurpose: customExtractPurpose,
803
+ extractAction: customExtractAction,
740
804
  skipPaths = [],
741
805
  onDenied = defaultOnDenied,
742
806
  recordDecisions,
@@ -822,7 +886,21 @@ function createMiddleware(options) {
822
886
  }
823
887
  return next();
824
888
  }
825
- const purpose = customExtractPurpose ? customExtractPurpose(req) : defaultExtractPurpose(req);
889
+ const pdlssPair = resolveRequestPdlss(
890
+ req,
891
+ routeConfig,
892
+ customExtractPurpose,
893
+ customExtractAction
894
+ );
895
+ const purpose = pdlssPair.purpose;
896
+ if (config.debug) {
897
+ console.debug("[express-middleware] pdlss resolved", {
898
+ purpose_source: pdlssPair.purposeSource,
899
+ resolved_purpose: pdlssPair.purpose,
900
+ action_source: pdlssPair.actionSource,
901
+ resolved_action: pdlssPair.action
902
+ });
903
+ }
826
904
  const astraCreds = extractAstraSyncCredentials(req);
827
905
  const counterpartyUrl = config.counterpartyUrl || `${req.protocol}://${req.get("host")}`;
828
906
  const preCheckFailures = performCounterpartyPreCheck(routeConfig, astraCreds, purpose);
@@ -866,10 +944,7 @@ function createMiddleware(options) {
866
944
  const result = await verify(config, {
867
945
  credentials,
868
946
  purpose,
869
- // RFC 7230 § 3.1.1 — HTTP method tokens uppercase by IANA convention.
870
- // Backend evaluator tolerates either case as defense-in-depth
871
- // (round-18.6 batch 2); SDK emits canonical form.
872
- action: req.method.toUpperCase(),
947
+ action: pdlssPair.action,
873
948
  resource: req.path,
874
949
  createSession: shouldRecordDecisions,
875
950
  counterpartyUrl,
@@ -907,35 +982,12 @@ function createMiddleware(options) {
907
982
  }
908
983
  return next();
909
984
  }
910
- if (!hasMinimumAccess(result.accessLevel, routeConfig.minAccessLevel)) {
911
- const insufficientFailure = {
912
- dimension: "access_level.insufficient",
913
- message: `Endpoint requires accessLevel '${routeConfig.minAccessLevel}'; agent has '${result.accessLevel}'.`,
914
- 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."
915
- };
916
- result.failures = [...result.failures ?? [], insufficientFailure];
917
- result.denialReasons = [...result.denialReasons ?? [], insufficientFailure.message];
918
- if (!result.guidance && wellKnownUrls) {
919
- result.guidance = {
920
- message: insufficientFailure.message,
921
- registrationUrl: wellKnownUrls.registrationUrl,
922
- documentationUrl: wellKnownUrls.documentationUrl
923
- };
924
- }
925
- if (shouldRecordDecisions && sessionId) {
926
- recordDecision(config, sessionId, "denied", insufficientFailure.message).catch(() => {
927
- });
928
- }
929
- dedupeFailures(result);
930
- onDenied(result, req, res);
931
- return;
932
- }
933
985
  if (routeConfig.minTrustScore && result.agent) {
934
986
  if (result.agent.trustScore < routeConfig.minTrustScore) {
935
987
  const trustFailure = {
936
- dimension: "access_level.insufficient",
937
- message: `Trust score ${result.agent.trustScore} is below required ${routeConfig.minTrustScore} for this route.`,
938
- 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."
988
+ dimension: "endpoint.trust",
989
+ message: "Trust below the route requirement for this endpoint.",
990
+ guidance: "Trust is below this route's floor. Trust is not overridable \u2014 the agent either meets the endpoint's trust policy or it doesn't. Raise the agent's trust via real signals (KYD, blockchain registration, agent-card), or have the operator lower the route's minTrustScore."
939
991
  };
940
992
  result.failures = [...result.failures ?? [], trustFailure];
941
993
  result.denialReasons = [trustFailure.message];