@astrasyncai/verification-gateway 2.4.12 → 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.
Files changed (91) 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 +125 -35
  6. package/dist/adapters/express.js.map +1 -1
  7. package/dist/adapters/express.mjs +125 -35
  8. package/dist/adapters/express.mjs.map +1 -1
  9. package/dist/adapters/mcp.d.mts +26 -4
  10. package/dist/adapters/mcp.d.ts +26 -4
  11. package/dist/adapters/mcp.js +94 -28
  12. package/dist/adapters/mcp.js.map +1 -1
  13. package/dist/adapters/mcp.mjs +94 -28
  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 +71 -28
  18. package/dist/adapters/nextjs.js.map +1 -1
  19. package/dist/adapters/nextjs.mjs +71 -28
  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 +45 -22
  24. package/dist/adapters/sdk.js.map +1 -1
  25. package/dist/adapters/sdk.mjs +45 -22
  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 +29 -0
  30. package/dist/agent/index.js.map +1 -1
  31. package/dist/agent/index.mjs +29 -0
  32. package/dist/agent/index.mjs.map +1 -1
  33. package/dist/browser/background.js +86 -24
  34. package/dist/browser/background.js.map +1 -1
  35. package/dist/browser/background.mjs +86 -24
  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 +86 -24
  46. package/dist/cursor/extension.js.map +1 -1
  47. package/dist/cursor/extension.mjs +86 -24
  48. package/dist/cursor/extension.mjs.map +1 -1
  49. package/dist/{express-C1ePFB7n.d.ts → express-CrfwoNAR.d.ts} +1 -1
  50. package/dist/{express-4WStX3PV.d.mts → express-ienhAXps.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 +86 -24
  54. package/dist/gateway/gateway.js.map +1 -1
  55. package/dist/gateway/gateway.mjs +86 -24
  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-ChPX4WHl.d.mts → index-B5e2IDWU.d.mts} +1 -1
  60. package/dist/{index-CzJMCgEy.d.ts → index-CCdZxvAr.d.ts} +71 -6
  61. package/dist/{index-D8IEntil.d.mts → index-CEg_WG6y.d.mts} +71 -6
  62. package/dist/{index-Cjm-zBeZ.d.ts → index-DC5f8eoQ.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 +336 -71
  66. package/dist/index.js.map +1 -1
  67. package/dist/index.mjs +336 -71
  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/local-evaluator/evaluator.js +12 -2
  72. package/dist/local-evaluator/evaluator.js.map +1 -1
  73. package/dist/local-evaluator/evaluator.mjs +12 -2
  74. package/dist/local-evaluator/evaluator.mjs.map +1 -1
  75. package/dist/{nextjs-BIORS__0.d.ts → nextjs-66R1KW8e.d.ts} +1 -1
  76. package/dist/{nextjs-CjzHdaXA.d.mts → nextjs-DSpisQst.d.mts} +1 -1
  77. package/dist/{sdk-Chhz-FcT.d.mts → sdk-5U_CBRpr.d.mts} +1 -1
  78. package/dist/{sdk-CqTEQAc6.d.ts → sdk-Bm8np66n.d.ts} +1 -1
  79. package/dist/transport/index.d.mts +2 -2
  80. package/dist/transport/index.d.ts +2 -2
  81. package/dist/transport/index.js +146 -28
  82. package/dist/transport/index.js.map +1 -1
  83. package/dist/transport/index.mjs +146 -28
  84. package/dist/transport/index.mjs.map +1 -1
  85. package/dist/{types-L15pYd2c.d.mts → types-B3USs-Kx.d.mts} +42 -1
  86. package/dist/{types-L15pYd2c.d.ts → types-B3USs-Kx.d.ts} +42 -1
  87. package/dist/{types-DNK2BgIf.d.mts → types-CgDCUfo8.d.mts} +1 -1
  88. package/dist/{types-DoWIuzfj.d.ts → types-R5N4ET6x.d.ts} +1 -1
  89. package/dist/ui/index.d.mts +1 -1
  90. package/dist/ui/index.d.ts +1 -1
  91. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { Request, Response, RequestHandler } from 'express';
2
- import { A as AccessLevel, G as GatewayConfig, i as VerificationResult } from '../types-L15pYd2c.js';
2
+ import { A as AccessLevel, G as GatewayConfig, i as VerificationResult } from '../types-B3USs-Kx.js';
3
3
 
4
4
  /**
5
5
  * MCP server-side helpers — companion to `transport/mcp.ts` (which handles the
@@ -280,9 +280,22 @@ interface McpMiddlewareOptions extends GatewayConfig {
280
280
  /** Custom denied handler. Defaults to a structured JSON-RPC error response. */
281
281
  onDenied?: (result: VerificationResult, req: Request, res: Response) => void;
282
282
  /**
283
- * If `false`, don't trust an inbound `X-Astra-Verified-Hop` header (always
284
- * call verify-access). Default `true` recommended for inner-hop endpoints
285
- * called by your own MCP tools.
283
+ * If `true`, trust an inbound `X-Astra-Verified-Hop` header to skip
284
+ * verify-access when it carries a recent marker for the resolved agent.
285
+ *
286
+ * **DEFAULT FLIPPED TO `false` in SDK 2.4.13** (audit F-A1-01). The marker
287
+ * is plaintext semicolon-delimited with no HMAC, so any client that sets
288
+ * the header (with the right format + fresh timestamp + matching ASTRA-id)
289
+ * skipped verify-access entirely. The per-process verify-access result
290
+ * cache already dedupes legitimate repeat calls — this optimization
291
+ * provided no additional savings, only a bypass surface.
292
+ *
293
+ * Set this to `true` ONLY if you control every upstream MCP hop that sets
294
+ * the header AND you trust the network path between hops (mTLS, internal
295
+ * VPC, etc.). For most installs, leave at the safe default.
296
+ *
297
+ * Future SDK release will either remove this option entirely OR add HMAC
298
+ * signing to the marker. Tracked as round-19+ work.
286
299
  */
287
300
  trustVerifiedHop?: boolean;
288
301
  /** Window for accepting an upstream verified-hop marker. Default 60_000ms. */
@@ -294,6 +307,15 @@ interface McpMiddlewareOptions extends GatewayConfig {
294
307
  recordDecisions?: boolean;
295
308
  /** Forward runtime challenge (default `true`). */
296
309
  enableRuntimeChallenge?: boolean;
310
+ /**
311
+ * Posture when the MCP middleware itself throws an internal error.
312
+ * Default `'open'` for backward compatibility in SDK 2.4.13. Shadow logs
313
+ * record what WOULD be denied with `failOnError: 'closed'` so merchants
314
+ * can grep correlationId for impact analysis during the 1-week
315
+ * observation window. Default flips to `'closed'` in a follow-up release.
316
+ * Audit F-A1-06.
317
+ */
318
+ failOnError?: 'open' | 'closed';
297
319
  }
298
320
  /**
299
321
  * Create the MCP middleware. Attach AFTER `express.json()` — the body must
@@ -51,7 +51,7 @@ function hasMinimumAccess(actual, required) {
51
51
  }
52
52
 
53
53
  // src/version.ts
54
- var SDK_VERSION = "2.4.12";
54
+ var SDK_VERSION = "2.4.13";
55
55
 
56
56
  // src/verify.ts
57
57
  var DEFAULT_CONFIG = {
@@ -70,22 +70,27 @@ var DEFAULT_CONFIG = {
70
70
  };
71
71
  var initCheckPerformed = false;
72
72
  var deprecationWarningShown = false;
73
- async function performInitCheck(apiBaseUrl, debug) {
73
+ async function performInitCheck(apiBaseUrl, debug, strictInit) {
74
74
  initCheckPerformed = true;
75
75
  try {
76
76
  const probeUrl = `${apiBaseUrl}/agents/verify-access`;
77
77
  const response = await fetch(probeUrl, { method: "HEAD" });
78
78
  const contentType = response.headers.get("content-type") ?? "";
79
79
  if (contentType.startsWith("text/html")) {
80
- console.warn(
81
- `[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). Set disableInitChecks: true on GatewayConfig to silence this warning.`
82
- );
80
+ 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).`;
81
+ if (strictInit) {
82
+ throw new Error(`${message} (strictInit=true)`);
83
+ }
84
+ console.warn(`${message} Set disableInitChecks: true on GatewayConfig to silence.`);
83
85
  } else if (debug) {
84
86
  console.log(
85
87
  `[VerificationGateway] init check passed for ${apiBaseUrl} (content-type: ${contentType})`
86
88
  );
87
89
  }
88
90
  } catch (err) {
91
+ if (strictInit) {
92
+ throw err;
93
+ }
89
94
  if (debug) {
90
95
  console.log(`[VerificationGateway] init check failed (non-blocking): ${String(err)}`);
91
96
  }
@@ -109,7 +114,23 @@ function getCacheKey(request) {
109
114
  request.counterpartyType || "",
110
115
  request.isSubAgentRequest ? "1" : "0",
111
116
  request.parentAgentId || "",
112
- request.subAgentDepth ?? ""
117
+ request.subAgentDepth ?? "",
118
+ // Audit F-A1-07: previously-missing dimensions that DO affect the
119
+ // backend verdict. Without these, two requests with different
120
+ // durations (e.g. 60s vs 86400s) collided on the same cache key and
121
+ // the shorter-duration allow served the longer-duration request.
122
+ request.durationRequired ?? "",
123
+ request.invocationProtocol || "",
124
+ request.enableRuntimeChallenge ? "1" : "0",
125
+ // callerMetadata fields contribute to risk model; include the ones
126
+ // backend reads. sourceIp/userAgent/forwardedFor change per-request
127
+ // so their inclusion effectively forces a re-check for any varying
128
+ // client (the right behavior — IP-driven anomaly scoring shouldn't
129
+ // be cached across IPs).
130
+ request.callerMetadata?.sourceIp || "",
131
+ request.callerMetadata?.userAgent || "",
132
+ request.callerMetadata?.forwardedFor || "",
133
+ request.callerMetadata?.agentCardUrl || ""
113
134
  ].join("|");
114
135
  }
115
136
  function getCachedResult(request) {
@@ -135,9 +156,13 @@ function cacheResult(request, result, configuredTtl) {
135
156
  }
136
157
  function extractCredentials(headers, query) {
137
158
  const credentials = {};
159
+ const ASTRA_ID_PATTERN = /^ASTRAE?-[A-Za-z0-9_-]{1,64}$/;
138
160
  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"];
139
161
  if (astraIdHeader) {
140
- credentials.astraId = Array.isArray(astraIdHeader) ? astraIdHeader[0] : astraIdHeader;
162
+ const raw = Array.isArray(astraIdHeader) ? astraIdHeader[0] : typeof astraIdHeader === "string" ? astraIdHeader : void 0;
163
+ if (typeof raw === "string" && ASTRA_ID_PATTERN.test(raw)) {
164
+ credentials.astraId = raw;
165
+ }
141
166
  }
142
167
  const apiKeyHeader = headers["x-api-key"] || headers["X-Api-Key"] || headers["X-API-KEY"];
143
168
  if (apiKeyHeader) {
@@ -146,9 +171,11 @@ function extractCredentials(headers, query) {
146
171
  const authHeader = headers["authorization"] || headers["Authorization"];
147
172
  if (authHeader) {
148
173
  const authValue = Array.isArray(authHeader) ? authHeader[0] : authHeader;
149
- credentials.authorizationHeader = authValue;
150
- if (authValue.startsWith("Bearer ")) {
151
- credentials.jwt = authValue.slice(7);
174
+ if (typeof authValue === "string") {
175
+ credentials.authorizationHeader = authValue;
176
+ if (authValue.startsWith("Bearer ")) {
177
+ credentials.jwt = authValue.slice(7);
178
+ }
152
179
  }
153
180
  }
154
181
  if (query) {
@@ -166,7 +193,7 @@ function createGuidanceResponse(config, reason, options = {}) {
166
193
  const isApiError = source === "api_error";
167
194
  const guidance = isApiError ? {
168
195
  message: "Verification is temporarily unavailable. Retry with exponential backoff; if the issue persists, contact support with the correlationId.",
169
- registrationUrl: `${config.apiBaseUrl.replace("/api", "")}/register`,
196
+ registrationUrl: `${config.apiBaseUrl.replace("/api", "")}/agents/register`,
170
197
  documentationUrl: `${config.apiBaseUrl.replace("/api", "")}/docs/agent-access`,
171
198
  steps: [
172
199
  "Retry the request with exponential backoff",
@@ -174,7 +201,7 @@ function createGuidanceResponse(config, reason, options = {}) {
174
201
  ]
175
202
  } : {
176
203
  message: "This service verifies AI agents before granting access. Please register your agent with AstraSync.",
177
- registrationUrl: `${config.apiBaseUrl.replace("/api", "")}/register`,
204
+ registrationUrl: `${config.apiBaseUrl.replace("/api", "")}/agents/register`,
178
205
  documentationUrl: `${config.apiBaseUrl.replace("/api", "")}/docs/agent-access`,
179
206
  steps: [
180
207
  "Register for an AstraSync account",
@@ -251,12 +278,8 @@ async function callVerifyAccessAPI(config, request) {
251
278
  "Content-Type": "application/json",
252
279
  ...config.customHeaders
253
280
  };
254
- if (credentials.authorizationHeader) {
255
- headers["Authorization"] = credentials.authorizationHeader;
256
- } else if (config.apiKey) {
257
- headers["Authorization"] = `Bearer ${config.apiKey}`;
258
- }
259
281
  if (config.apiKey) {
282
+ headers["Authorization"] = `Bearer ${config.apiKey}`;
260
283
  headers["X-API-Key"] = config.apiKey;
261
284
  }
262
285
  try {
@@ -302,7 +325,11 @@ async function callVerifyAccessAPI(config, request) {
302
325
  async function verify(config, request) {
303
326
  const mergedConfig = { ...DEFAULT_CONFIG, ...config };
304
327
  if (!initCheckPerformed && !mergedConfig.disableInitChecks && mergedConfig.apiBaseUrl) {
305
- void performInitCheck(mergedConfig.apiBaseUrl, mergedConfig.debug);
328
+ if (mergedConfig.strictInit) {
329
+ await performInitCheck(mergedConfig.apiBaseUrl, mergedConfig.debug, true);
330
+ } else {
331
+ void performInitCheck(mergedConfig.apiBaseUrl, mergedConfig.debug, false);
332
+ }
306
333
  }
307
334
  if (!deprecationWarningShown && (config.minTrustScore !== void 0 || config.minTrustScoreForFull !== void 0)) {
308
335
  deprecationWarningShown = true;
@@ -356,7 +383,7 @@ async function verify(config, request) {
356
383
  requiresApproval: apiResponse.access?.requiresApproval,
357
384
  guidance: {
358
385
  message: apiResponse.access?.reason || "Access denied by PDLSS policy",
359
- registrationUrl: `${mergedConfig.apiBaseUrl?.replace("/api", "")}/register`,
386
+ registrationUrl: `${mergedConfig.apiBaseUrl?.replace("/api", "")}/agents/register`,
360
387
  documentationUrl: `${mergedConfig.apiBaseUrl?.replace("/api", "")}/docs/pdlss`
361
388
  },
362
389
  verifiedAt: /* @__PURE__ */ new Date(),
@@ -426,13 +453,15 @@ async function verify(config, request) {
426
453
  result.denialReasons = result.recommendationReasons || [
427
454
  "Access denied by AstraSync recommendation"
428
455
  ];
429
- if (result.runtimeChallenge) {
430
- result.guidance = {
431
- message: `Verification failed: ${result.runtimeChallenge.reason || "runtime challenge failed"}`,
432
- registrationUrl: `${mergedConfig.apiBaseUrl?.replace("/api", "")}/register`,
433
- documentationUrl: `${mergedConfig.apiBaseUrl?.replace("/api", "")}/docs/runtime-challenge`
434
- };
435
- }
456
+ result.guidance = result.runtimeChallenge ? {
457
+ message: `Verification failed: ${result.runtimeChallenge.reason || "runtime challenge failed"}`,
458
+ registrationUrl: `${mergedConfig.apiBaseUrl?.replace("/api", "")}/agents/register`,
459
+ documentationUrl: `${mergedConfig.apiBaseUrl?.replace("/api", "")}/docs/runtime-challenge`
460
+ } : {
461
+ message: result.recommendationReasons?.[0] || "Access denied by AstraSync recommendation",
462
+ registrationUrl: `${mergedConfig.apiBaseUrl?.replace("/api", "")}/agents/register`,
463
+ documentationUrl: `${mergedConfig.apiBaseUrl?.replace("/api", "")}/docs/pdlss`
464
+ };
436
465
  } else if (result.recommendation === "step_up_required") {
437
466
  result.requiresStepUp = true;
438
467
  if (ACCESS_LEVEL_HIERARCHY[result.accessLevel] > ACCESS_LEVEL_HIERARCHY["read-only"]) {
@@ -590,7 +619,7 @@ function mcpToPdlss(parsed, headerPurpose, headerAction) {
590
619
  action = parsed.actionFromBody;
591
620
  actionSource = parsed.actionSourceFromBody;
592
621
  } else {
593
- action = parsed.toolName ? `${parsed.method}:${parsed.toolName}` : parsed.method;
622
+ action = parsed.toolName ? parsed.method === "tools/call" ? parsed.toolName : `${parsed.method}:${parsed.toolName}` : parsed.method;
594
623
  actionSource = "transport_layer";
595
624
  }
596
625
  return { purpose, action, resource, purposeSource, actionSource };
@@ -609,6 +638,17 @@ function readSingleHeader(value) {
609
638
  if (Array.isArray(value)) return value[0];
610
639
  return void 0;
611
640
  }
641
+ function dedupeFailures(result) {
642
+ if (result.failures && result.failures.length > 1) {
643
+ const seen = /* @__PURE__ */ new Set();
644
+ result.failures = result.failures.filter((f) => {
645
+ const key = `${f.dimension}|${f.message}|${f.guidance ?? ""}`;
646
+ if (seen.has(key)) return false;
647
+ seen.add(key);
648
+ return true;
649
+ });
650
+ }
651
+ }
612
652
  function defaultMcpDenied(result, req, res) {
613
653
  const id = req.body?.id ?? null;
614
654
  const status = !result.identityVerified ? 401 : 403;
@@ -648,10 +688,11 @@ function createMcpMiddleware(options) {
648
688
  onAgentIdMismatch = "reject",
649
689
  skip = false,
650
690
  onDenied = defaultMcpDenied,
651
- trustVerifiedHop = true,
691
+ trustVerifiedHop = false,
652
692
  verifiedHopMaxAgeMs,
653
693
  recordDecisions,
654
694
  enableRuntimeChallenge = true,
695
+ failOnError = "open",
655
696
  ...config
656
697
  } = options;
657
698
  return async (req, res, next) => {
@@ -759,6 +800,7 @@ function createMcpMiddleware(options) {
759
800
  recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
760
801
  });
761
802
  }
803
+ dedupeFailures(result);
762
804
  onDenied(result, req, res);
763
805
  return;
764
806
  }
@@ -804,6 +846,7 @@ function createMcpMiddleware(options) {
804
846
  });
805
847
  }
806
848
  }
849
+ dedupeFailures(result);
807
850
  onDenied(result, req, res);
808
851
  return;
809
852
  }
@@ -827,7 +870,30 @@ function createMcpMiddleware(options) {
827
870
  }
828
871
  next();
829
872
  } catch (error) {
873
+ const errorClass = error instanceof Error ? error.constructor.name : typeof error;
874
+ const correlationId = req.headers["x-request-id"] || req.headers["x-correlation-id"] || `gen-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
830
875
  console.error("[VerificationGateway/MCP] Middleware error:", error);
876
+ console.warn(
877
+ `[SHADOW] would-have-denied: errorClass=${errorClass} route=${req.method}:${req.path} merchantId=${config.counterpartyId ?? "unknown"} correlationId=${correlationId}`
878
+ );
879
+ if (failOnError === "closed") {
880
+ const result = {
881
+ identityVerified: false,
882
+ policyAllowed: false,
883
+ accessLevel: "none",
884
+ denialReasons: [`MCP middleware internal error: ${errorClass}`],
885
+ failures: [
886
+ {
887
+ dimension: "middleware.internal_error",
888
+ message: `Middleware threw ${errorClass} \u2014 failing closed`
889
+ }
890
+ ],
891
+ verifiedAt: /* @__PURE__ */ new Date(),
892
+ correlationId
893
+ };
894
+ dedupeFailures(result);
895
+ return onDenied(result, req, res);
896
+ }
831
897
  next();
832
898
  }
833
899
  };