@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
@@ -503,12 +503,45 @@ function bufferToBase64(bytes) {
503
503
 
504
504
  // src/transport/rfc9421-verify.ts
505
505
  var import_http_message_signatures = require("http-message-signatures");
506
+
507
+ // src/transport/nonce-store.ts
508
+ var InMemoryNonceStore = class {
509
+ constructor(capacity = 1e4) {
510
+ this.entries = /* @__PURE__ */ new Map();
511
+ this.lastSweepMs = 0;
512
+ this.capacity = capacity;
513
+ }
514
+ seen(key, expiresAtMs) {
515
+ const nowMs = Date.now();
516
+ if (nowMs - this.lastSweepMs > 1e3) {
517
+ for (const [k, exp] of this.entries) {
518
+ if (exp <= nowMs) this.entries.delete(k);
519
+ }
520
+ this.lastSweepMs = nowMs;
521
+ }
522
+ const existing = this.entries.get(key);
523
+ if (existing !== void 0 && existing > nowMs) {
524
+ return true;
525
+ }
526
+ if (this.entries.size >= this.capacity) {
527
+ const oldest = this.entries.keys().next().value;
528
+ if (oldest !== void 0) this.entries.delete(oldest);
529
+ }
530
+ this.entries.set(key, expiresAtMs);
531
+ return false;
532
+ }
533
+ };
534
+ var defaultNonceStore = new InMemoryNonceStore();
535
+
536
+ // src/transport/rfc9421-verify.ts
506
537
  async function verifyRFC9421(request, options) {
507
538
  const { resolver } = options;
508
- const tolerance = options.clockSkewSec ?? 300;
539
+ const tolerance = options.clockSkewSec ?? 60;
509
540
  const nowSec = options.now ? options.now() : Math.floor(Date.now() / 1e3);
541
+ const nonceStore = options.nonceStore ?? defaultNonceStore;
510
542
  let resolvedKid;
511
543
  let resolvedAlg;
544
+ let replayDetected = false;
512
545
  const keyLookup = async (parameters) => {
513
546
  const kid = typeof parameters.keyid === "string" ? parameters.keyid : void 0;
514
547
  if (!kid) return null;
@@ -522,6 +555,14 @@ async function verifyRFC9421(request, options) {
522
555
  const expires = toUnixSeconds(parameters.expires);
523
556
  if (created !== void 0 && Math.abs(nowSec - created) > tolerance) return null;
524
557
  if (expires !== void 0 && nowSec > expires + tolerance) return null;
558
+ const nonce = typeof parameters.nonce === "string" ? parameters.nonce : void 0;
559
+ if (nonce) {
560
+ const expiresAtMs = (expires !== void 0 ? expires + tolerance : nowSec + tolerance) * 1e3;
561
+ if (nonceStore.seen(`rfc9421:${kid}:${nonce}`, expiresAtMs)) {
562
+ replayDetected = true;
563
+ return null;
564
+ }
565
+ }
525
566
  return jwkToVerifyingKey(kid, jwk, alg);
526
567
  };
527
568
  try {
@@ -544,7 +585,7 @@ async function verifyRFC9421(request, options) {
544
585
  kid: resolvedKid,
545
586
  registry: resolver.name,
546
587
  algorithm: resolvedAlg,
547
- error: result === false ? "signature invalid" : "no signature found"
588
+ error: replayDetected ? "RFC9421 signature replay \u2014 already seen within tolerance window" : result === false ? "signature invalid" : "no signature found"
548
589
  };
549
590
  } catch (err) {
550
591
  return {
@@ -1369,14 +1410,26 @@ function sha256Sync2(data) {
1369
1410
  function verifyAP2Chain(input) {
1370
1411
  const { triple } = input;
1371
1412
  const errors = [];
1413
+ const toleranceSec = input.clockSkewSec ?? 60;
1414
+ const nonceStore = input.nonceStore ?? defaultNonceStore;
1372
1415
  const intentPresent = triple.intent !== void 0;
1373
1416
  const cartRefOk = checkCartRef(triple, errors);
1374
1417
  const paymentRefOk = checkPaymentRef(triple, errors);
1375
1418
  const { ok: agentIdContinuity, agentId } = checkAgentContinuity(triple, errors);
1376
1419
  const paymentMethodAllowed = checkPaymentMethod(triple, errors);
1377
1420
  const totalsConsistent = checkTotals(triple, errors);
1378
- const expiryOk = checkExpiries(triple, input.clockSkewSec ?? 300, input.now, errors);
1379
- const ok = cartRefOk && paymentRefOk && agentIdContinuity && paymentMethodAllowed && totalsConsistent && expiryOk;
1421
+ const expiryOk = checkExpiries(triple, toleranceSec, input.now, errors);
1422
+ let replayOk = true;
1423
+ const replayId = triple.payment?.raw?.id ?? triple.cart?.raw?.id;
1424
+ if (typeof replayId === "string" && replayId.length > 0) {
1425
+ const now = input.now ? input.now() : Math.floor(Date.now() / 1e3);
1426
+ const expiresAt = (now + toleranceSec) * 1e3;
1427
+ if (nonceStore.seen(`ap2:${replayId}`, expiresAt)) {
1428
+ errors.push(`AP2 chain replay \u2014 mandate ${replayId} already seen within tolerance window`);
1429
+ replayOk = false;
1430
+ }
1431
+ }
1432
+ const ok = cartRefOk && paymentRefOk && agentIdContinuity && paymentMethodAllowed && totalsConsistent && expiryOk && replayOk;
1380
1433
  return {
1381
1434
  ok,
1382
1435
  checks: {
@@ -1422,7 +1475,10 @@ function checkAgentContinuity(triple, errors) {
1422
1475
  const ids = [triple.intent?.agent_id, triple.cart?.agent_id, triple.payment?.agent_id].filter(
1423
1476
  (id) => typeof id === "string" && id.length > 0
1424
1477
  );
1425
- if (ids.length === 0) return { ok: true };
1478
+ if (ids.length === 0) {
1479
+ errors.push("agent_id missing across all three mandates (intent/cart/payment)");
1480
+ return { ok: false };
1481
+ }
1426
1482
  const unique = new Set(ids);
1427
1483
  if (unique.size > 1) {
1428
1484
  errors.push(`agent_id mismatch across mandates: ${Array.from(unique).join(", ")}`);
@@ -1431,9 +1487,16 @@ function checkAgentContinuity(triple, errors) {
1431
1487
  return { ok: true, agentId: ids[0] };
1432
1488
  }
1433
1489
  function checkPaymentMethod(triple, errors) {
1434
- const paymentMethod = triple.payment?.payment_method;
1435
1490
  const allowed = triple.intent?.paymentMethods;
1436
- if (!paymentMethod || !allowed || allowed.length === 0) return true;
1491
+ if (!allowed || allowed.length === 0) return true;
1492
+ if (!triple.payment) return true;
1493
+ const paymentMethod = triple.payment.payment_method;
1494
+ if (!paymentMethod) {
1495
+ errors.push(
1496
+ `payment.payment_method missing but intent declares allowlist [${allowed.join(", ")}]`
1497
+ );
1498
+ return false;
1499
+ }
1437
1500
  if (!allowed.includes(paymentMethod)) {
1438
1501
  errors.push(
1439
1502
  `payment_method "${paymentMethod}" not in intent.paymentMethods [${allowed.join(", ")}]`
@@ -1467,19 +1530,24 @@ function checkTotals(triple, errors) {
1467
1530
  function checkExpiries(triple, toleranceSec, nowFn, errors) {
1468
1531
  const now = nowFn ? nowFn() : Math.floor(Date.now() / 1e3);
1469
1532
  let ok = true;
1470
- for (const [name, mandate] of [
1471
- ["intent", triple.intent],
1472
- ["cart", triple.cart]
1473
- ]) {
1474
- if (!mandate?.expires) continue;
1475
- const parsed = parseExpiry(mandate.expires);
1533
+ const layers = [
1534
+ ["intent", triple.intent?.expires],
1535
+ ["cart", triple.cart?.expires],
1536
+ [
1537
+ "payment",
1538
+ typeof triple.payment?.raw?.expires === "string" ? triple.payment.raw.expires : typeof triple.payment?.raw?.exp === "string" ? triple.payment.raw.exp : void 0
1539
+ ]
1540
+ ];
1541
+ for (const [name, expires] of layers) {
1542
+ if (!expires) continue;
1543
+ const parsed = parseExpiry(expires);
1476
1544
  if (parsed === null) {
1477
1545
  errors.push(`${name}.expires unparseable`);
1478
1546
  ok = false;
1479
1547
  continue;
1480
1548
  }
1481
1549
  if (now > parsed + toleranceSec) {
1482
- errors.push(`${name} mandate expired at ${mandate.expires}`);
1550
+ errors.push(`${name} mandate expired at ${expires}`);
1483
1551
  ok = false;
1484
1552
  }
1485
1553
  }
@@ -1506,10 +1574,21 @@ async function verifyACPSignature(input) {
1506
1574
  if (!input.signatureHeader) {
1507
1575
  return { ok: false, error: "missing Signature header" };
1508
1576
  }
1509
- const freshness = checkTimestamp(input.timestampHeader, input.clockSkewSec ?? 300, input.now);
1577
+ const tolerance = input.clockSkewSec ?? 60;
1578
+ const nonceStore = input.nonceStore ?? defaultNonceStore;
1579
+ const freshness = checkTimestamp(input.timestampHeader, tolerance, input.now);
1510
1580
  if (!freshness.ok) {
1511
1581
  return { ok: false, error: freshness.error, timestampStale: true };
1512
1582
  }
1583
+ const nowSec = input.now ? input.now() : Math.floor(Date.now() / 1e3);
1584
+ const expiresAtMs = (nowSec + tolerance) * 1e3;
1585
+ const replayKey = `acp:${input.signatureHeader}:${input.timestampHeader ?? ""}`;
1586
+ if (nonceStore.seen(replayKey, expiresAtMs)) {
1587
+ return {
1588
+ ok: false,
1589
+ error: "ACP signature replay \u2014 already seen within tolerance window"
1590
+ };
1591
+ }
1513
1592
  const signatureBytes = decodeBase64(input.signatureHeader);
1514
1593
  if (!signatureBytes) {
1515
1594
  return { ok: false, error: "signature header is not valid base64" };
@@ -1727,8 +1806,9 @@ function coerceString6(v) {
1727
1806
  var import_mppx2 = require("mppx");
1728
1807
  function verifyMPP(input) {
1729
1808
  const { context } = input;
1730
- const tolerance = input.clockSkewSec ?? 300;
1809
+ const tolerance = input.clockSkewSec ?? 60;
1731
1810
  const nowSec = input.now ? input.now() : Math.floor(Date.now() / 1e3);
1811
+ const nonceStore = input.nonceStore ?? defaultNonceStore;
1732
1812
  const challenge = context.credential?.challenge ?? (context.challenges && context.challenges[0]);
1733
1813
  const source = context.credential?.source;
1734
1814
  const method = challenge?.method;
@@ -1751,21 +1831,38 @@ function verifyMPP(input) {
1751
1831
  }
1752
1832
  }
1753
1833
  let bodyDigestOk = null;
1754
- if (challenge?.digest && input.rawBody !== void 0) {
1755
- try {
1756
- if (!/^sha-256=/.test(challenge.digest)) {
1834
+ if (input.rawBody !== void 0) {
1835
+ if (!challenge?.digest) {
1836
+ bodyDigestOk = false;
1837
+ } else {
1838
+ try {
1839
+ if (!/^sha-256=/.test(challenge.digest)) {
1840
+ bodyDigestOk = false;
1841
+ } else {
1842
+ bodyDigestOk = import_mppx2.BodyDigest.verify(challenge.digest, input.rawBody);
1843
+ }
1844
+ } catch {
1757
1845
  bodyDigestOk = false;
1758
- } else {
1759
- bodyDigestOk = import_mppx2.BodyDigest.verify(challenge.digest, input.rawBody);
1760
1846
  }
1761
- } catch {
1762
- bodyDigestOk = false;
1763
1847
  }
1764
1848
  }
1765
- const ok = expiryOk && (bodyDigestOk === null || bodyDigestOk === true);
1849
+ let replayOk = true;
1850
+ if (challenge?.digest && expiryOk) {
1851
+ const replayKey = `mpp:${challenge.digest}:${challenge.nonce ?? ""}`;
1852
+ const expiresAt = (nowSec + tolerance) * 1e3;
1853
+ if (nonceStore.seen(replayKey, expiresAt)) {
1854
+ replayOk = false;
1855
+ }
1856
+ }
1857
+ const ok = expiryOk && (bodyDigestOk === null || bodyDigestOk === true) && replayOk;
1766
1858
  const errors = [];
1767
1859
  if (!expiryOk) errors.push("challenge expired");
1768
- if (bodyDigestOk === false) errors.push("body digest mismatch");
1860
+ if (bodyDigestOk === false) {
1861
+ errors.push(
1862
+ input.rawBody !== void 0 && !challenge?.digest ? "body digest required when rawBody present" : "body digest mismatch"
1863
+ );
1864
+ }
1865
+ if (!replayOk) errors.push("MPP challenge replay \u2014 already seen within tolerance window");
1769
1866
  return {
1770
1867
  ok,
1771
1868
  expiryOk,
@@ -1926,14 +2023,32 @@ function readHeader4(headers, name) {
1926
2023
  var import_node_crypto4 = require("crypto");
1927
2024
  async function verifyVIChain(input) {
1928
2025
  const errors = [];
1929
- const tolerance = input.clockSkewSec ?? 300;
2026
+ const tolerance = input.clockSkewSec ?? 60;
1930
2027
  const now = input.now ? input.now() : Math.floor(Date.now() / 1e3);
1931
2028
  const { l1, l2, l3a, l3b } = input.layers;
2029
+ const nonceStore = input.nonceStore ?? defaultNonceStore;
2030
+ if (!l1) {
2031
+ if (!input.allowUnboundChain) {
2032
+ errors.push(
2033
+ "L1 missing \u2014 chain root unbound (set allowUnboundChain + expectedL2Key to override)"
2034
+ );
2035
+ } else if (!input.expectedL2Key) {
2036
+ errors.push("allowUnboundChain set but expectedL2Key missing");
2037
+ }
2038
+ }
1932
2039
  const l1SigOk = l1 ? await input.verifySignature(l1, null) : null;
1933
2040
  if (l1 && !l1SigOk) errors.push("L1 signature invalid");
1934
2041
  const l1Cnf = extractCnfJwk(l1?.payload);
1935
- const l2SigOk = await input.verifySignature(l2, l1Cnf ?? null);
2042
+ const l2ExpectedKey = l1Cnf ?? input.expectedL2Key ?? null;
2043
+ const l2SigOk = await input.verifySignature(l2, l2ExpectedKey);
1936
2044
  if (!l2SigOk) errors.push("L2 signature invalid");
2045
+ if (l2SigOk) {
2046
+ const replayKey = `vi:l2:${l2.compact}`;
2047
+ const expiresAt = now * 1e3 + tolerance * 1e3;
2048
+ if (nonceStore.seen(replayKey, expiresAt)) {
2049
+ errors.push("L2 signature replay \u2014 already seen within tolerance window");
2050
+ }
2051
+ }
1937
2052
  const l2Cnf = extractCnfJwk(l2.payload);
1938
2053
  const l3aSigOk = l3a ? await input.verifySignature(l3a, l2Cnf ?? null) : null;
1939
2054
  if (l3a && !l3aSigOk) errors.push("L3a signature invalid");
@@ -1977,7 +2092,10 @@ async function verifyVIChain(input) {
1977
2092
  }
1978
2093
  }
1979
2094
  const expiryOk = checkExpiryAcross([l1, l2, l3a, l3b], tolerance, now, errors);
1980
- const ok = l1SigOk !== false && l2SigOk && l3aSigOk !== false && l3bSigOk !== false && l1BindsL2 && l2BindsL3 && l3aL3bTxnIdMatch !== false && checkoutHashOk !== false && expiryOk;
2095
+ const noUnboundChainOrReplayErrors = !errors.some(
2096
+ (e) => e.startsWith("L1 missing") || e.startsWith("allowUnboundChain set") || e.startsWith("L2 signature replay")
2097
+ );
2098
+ const ok = l1SigOk !== false && l2SigOk && l3aSigOk !== false && l3bSigOk !== false && l1BindsL2 && l2BindsL3 && l3aL3bTxnIdMatch !== false && checkoutHashOk !== false && expiryOk && noUnboundChainOrReplayErrors;
1981
2099
  return {
1982
2100
  ok,
1983
2101
  checks: {