@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
@@ -410,12 +410,45 @@ function bufferToBase64(bytes) {
410
410
 
411
411
  // src/transport/rfc9421-verify.ts
412
412
  import { httpbis } from "http-message-signatures";
413
+
414
+ // src/transport/nonce-store.ts
415
+ var InMemoryNonceStore = class {
416
+ constructor(capacity = 1e4) {
417
+ this.entries = /* @__PURE__ */ new Map();
418
+ this.lastSweepMs = 0;
419
+ this.capacity = capacity;
420
+ }
421
+ seen(key, expiresAtMs) {
422
+ const nowMs = Date.now();
423
+ if (nowMs - this.lastSweepMs > 1e3) {
424
+ for (const [k, exp] of this.entries) {
425
+ if (exp <= nowMs) this.entries.delete(k);
426
+ }
427
+ this.lastSweepMs = nowMs;
428
+ }
429
+ const existing = this.entries.get(key);
430
+ if (existing !== void 0 && existing > nowMs) {
431
+ return true;
432
+ }
433
+ if (this.entries.size >= this.capacity) {
434
+ const oldest = this.entries.keys().next().value;
435
+ if (oldest !== void 0) this.entries.delete(oldest);
436
+ }
437
+ this.entries.set(key, expiresAtMs);
438
+ return false;
439
+ }
440
+ };
441
+ var defaultNonceStore = new InMemoryNonceStore();
442
+
443
+ // src/transport/rfc9421-verify.ts
413
444
  async function verifyRFC9421(request, options) {
414
445
  const { resolver } = options;
415
- const tolerance = options.clockSkewSec ?? 300;
446
+ const tolerance = options.clockSkewSec ?? 60;
416
447
  const nowSec = options.now ? options.now() : Math.floor(Date.now() / 1e3);
448
+ const nonceStore = options.nonceStore ?? defaultNonceStore;
417
449
  let resolvedKid;
418
450
  let resolvedAlg;
451
+ let replayDetected = false;
419
452
  const keyLookup = async (parameters) => {
420
453
  const kid = typeof parameters.keyid === "string" ? parameters.keyid : void 0;
421
454
  if (!kid) return null;
@@ -429,6 +462,14 @@ async function verifyRFC9421(request, options) {
429
462
  const expires = toUnixSeconds(parameters.expires);
430
463
  if (created !== void 0 && Math.abs(nowSec - created) > tolerance) return null;
431
464
  if (expires !== void 0 && nowSec > expires + tolerance) return null;
465
+ const nonce = typeof parameters.nonce === "string" ? parameters.nonce : void 0;
466
+ if (nonce) {
467
+ const expiresAtMs = (expires !== void 0 ? expires + tolerance : nowSec + tolerance) * 1e3;
468
+ if (nonceStore.seen(`rfc9421:${kid}:${nonce}`, expiresAtMs)) {
469
+ replayDetected = true;
470
+ return null;
471
+ }
472
+ }
432
473
  return jwkToVerifyingKey(kid, jwk, alg);
433
474
  };
434
475
  try {
@@ -451,7 +492,7 @@ async function verifyRFC9421(request, options) {
451
492
  kid: resolvedKid,
452
493
  registry: resolver.name,
453
494
  algorithm: resolvedAlg,
454
- error: result === false ? "signature invalid" : "no signature found"
495
+ error: replayDetected ? "RFC9421 signature replay \u2014 already seen within tolerance window" : result === false ? "signature invalid" : "no signature found"
455
496
  };
456
497
  } catch (err) {
457
498
  return {
@@ -1276,14 +1317,26 @@ function sha256Sync2(data) {
1276
1317
  function verifyAP2Chain(input) {
1277
1318
  const { triple } = input;
1278
1319
  const errors = [];
1320
+ const toleranceSec = input.clockSkewSec ?? 60;
1321
+ const nonceStore = input.nonceStore ?? defaultNonceStore;
1279
1322
  const intentPresent = triple.intent !== void 0;
1280
1323
  const cartRefOk = checkCartRef(triple, errors);
1281
1324
  const paymentRefOk = checkPaymentRef(triple, errors);
1282
1325
  const { ok: agentIdContinuity, agentId } = checkAgentContinuity(triple, errors);
1283
1326
  const paymentMethodAllowed = checkPaymentMethod(triple, errors);
1284
1327
  const totalsConsistent = checkTotals(triple, errors);
1285
- const expiryOk = checkExpiries(triple, input.clockSkewSec ?? 300, input.now, errors);
1286
- const ok = cartRefOk && paymentRefOk && agentIdContinuity && paymentMethodAllowed && totalsConsistent && expiryOk;
1328
+ const expiryOk = checkExpiries(triple, toleranceSec, input.now, errors);
1329
+ let replayOk = true;
1330
+ const replayId = triple.payment?.raw?.id ?? triple.cart?.raw?.id;
1331
+ if (typeof replayId === "string" && replayId.length > 0) {
1332
+ const now = input.now ? input.now() : Math.floor(Date.now() / 1e3);
1333
+ const expiresAt = (now + toleranceSec) * 1e3;
1334
+ if (nonceStore.seen(`ap2:${replayId}`, expiresAt)) {
1335
+ errors.push(`AP2 chain replay \u2014 mandate ${replayId} already seen within tolerance window`);
1336
+ replayOk = false;
1337
+ }
1338
+ }
1339
+ const ok = cartRefOk && paymentRefOk && agentIdContinuity && paymentMethodAllowed && totalsConsistent && expiryOk && replayOk;
1287
1340
  return {
1288
1341
  ok,
1289
1342
  checks: {
@@ -1329,7 +1382,10 @@ function checkAgentContinuity(triple, errors) {
1329
1382
  const ids = [triple.intent?.agent_id, triple.cart?.agent_id, triple.payment?.agent_id].filter(
1330
1383
  (id) => typeof id === "string" && id.length > 0
1331
1384
  );
1332
- if (ids.length === 0) return { ok: true };
1385
+ if (ids.length === 0) {
1386
+ errors.push("agent_id missing across all three mandates (intent/cart/payment)");
1387
+ return { ok: false };
1388
+ }
1333
1389
  const unique = new Set(ids);
1334
1390
  if (unique.size > 1) {
1335
1391
  errors.push(`agent_id mismatch across mandates: ${Array.from(unique).join(", ")}`);
@@ -1338,9 +1394,16 @@ function checkAgentContinuity(triple, errors) {
1338
1394
  return { ok: true, agentId: ids[0] };
1339
1395
  }
1340
1396
  function checkPaymentMethod(triple, errors) {
1341
- const paymentMethod = triple.payment?.payment_method;
1342
1397
  const allowed = triple.intent?.paymentMethods;
1343
- if (!paymentMethod || !allowed || allowed.length === 0) return true;
1398
+ if (!allowed || allowed.length === 0) return true;
1399
+ if (!triple.payment) return true;
1400
+ const paymentMethod = triple.payment.payment_method;
1401
+ if (!paymentMethod) {
1402
+ errors.push(
1403
+ `payment.payment_method missing but intent declares allowlist [${allowed.join(", ")}]`
1404
+ );
1405
+ return false;
1406
+ }
1344
1407
  if (!allowed.includes(paymentMethod)) {
1345
1408
  errors.push(
1346
1409
  `payment_method "${paymentMethod}" not in intent.paymentMethods [${allowed.join(", ")}]`
@@ -1374,19 +1437,24 @@ function checkTotals(triple, errors) {
1374
1437
  function checkExpiries(triple, toleranceSec, nowFn, errors) {
1375
1438
  const now = nowFn ? nowFn() : Math.floor(Date.now() / 1e3);
1376
1439
  let ok = true;
1377
- for (const [name, mandate] of [
1378
- ["intent", triple.intent],
1379
- ["cart", triple.cart]
1380
- ]) {
1381
- if (!mandate?.expires) continue;
1382
- const parsed = parseExpiry(mandate.expires);
1440
+ const layers = [
1441
+ ["intent", triple.intent?.expires],
1442
+ ["cart", triple.cart?.expires],
1443
+ [
1444
+ "payment",
1445
+ typeof triple.payment?.raw?.expires === "string" ? triple.payment.raw.expires : typeof triple.payment?.raw?.exp === "string" ? triple.payment.raw.exp : void 0
1446
+ ]
1447
+ ];
1448
+ for (const [name, expires] of layers) {
1449
+ if (!expires) continue;
1450
+ const parsed = parseExpiry(expires);
1383
1451
  if (parsed === null) {
1384
1452
  errors.push(`${name}.expires unparseable`);
1385
1453
  ok = false;
1386
1454
  continue;
1387
1455
  }
1388
1456
  if (now > parsed + toleranceSec) {
1389
- errors.push(`${name} mandate expired at ${mandate.expires}`);
1457
+ errors.push(`${name} mandate expired at ${expires}`);
1390
1458
  ok = false;
1391
1459
  }
1392
1460
  }
@@ -1413,10 +1481,21 @@ async function verifyACPSignature(input) {
1413
1481
  if (!input.signatureHeader) {
1414
1482
  return { ok: false, error: "missing Signature header" };
1415
1483
  }
1416
- const freshness = checkTimestamp(input.timestampHeader, input.clockSkewSec ?? 300, input.now);
1484
+ const tolerance = input.clockSkewSec ?? 60;
1485
+ const nonceStore = input.nonceStore ?? defaultNonceStore;
1486
+ const freshness = checkTimestamp(input.timestampHeader, tolerance, input.now);
1417
1487
  if (!freshness.ok) {
1418
1488
  return { ok: false, error: freshness.error, timestampStale: true };
1419
1489
  }
1490
+ const nowSec = input.now ? input.now() : Math.floor(Date.now() / 1e3);
1491
+ const expiresAtMs = (nowSec + tolerance) * 1e3;
1492
+ const replayKey = `acp:${input.signatureHeader}:${input.timestampHeader ?? ""}`;
1493
+ if (nonceStore.seen(replayKey, expiresAtMs)) {
1494
+ return {
1495
+ ok: false,
1496
+ error: "ACP signature replay \u2014 already seen within tolerance window"
1497
+ };
1498
+ }
1420
1499
  const signatureBytes = decodeBase64(input.signatureHeader);
1421
1500
  if (!signatureBytes) {
1422
1501
  return { ok: false, error: "signature header is not valid base64" };
@@ -1634,8 +1713,9 @@ function coerceString6(v) {
1634
1713
  import { BodyDigest } from "mppx";
1635
1714
  function verifyMPP(input) {
1636
1715
  const { context } = input;
1637
- const tolerance = input.clockSkewSec ?? 300;
1716
+ const tolerance = input.clockSkewSec ?? 60;
1638
1717
  const nowSec = input.now ? input.now() : Math.floor(Date.now() / 1e3);
1718
+ const nonceStore = input.nonceStore ?? defaultNonceStore;
1639
1719
  const challenge = context.credential?.challenge ?? (context.challenges && context.challenges[0]);
1640
1720
  const source = context.credential?.source;
1641
1721
  const method = challenge?.method;
@@ -1658,21 +1738,38 @@ function verifyMPP(input) {
1658
1738
  }
1659
1739
  }
1660
1740
  let bodyDigestOk = null;
1661
- if (challenge?.digest && input.rawBody !== void 0) {
1662
- try {
1663
- if (!/^sha-256=/.test(challenge.digest)) {
1741
+ if (input.rawBody !== void 0) {
1742
+ if (!challenge?.digest) {
1743
+ bodyDigestOk = false;
1744
+ } else {
1745
+ try {
1746
+ if (!/^sha-256=/.test(challenge.digest)) {
1747
+ bodyDigestOk = false;
1748
+ } else {
1749
+ bodyDigestOk = BodyDigest.verify(challenge.digest, input.rawBody);
1750
+ }
1751
+ } catch {
1664
1752
  bodyDigestOk = false;
1665
- } else {
1666
- bodyDigestOk = BodyDigest.verify(challenge.digest, input.rawBody);
1667
1753
  }
1668
- } catch {
1669
- bodyDigestOk = false;
1670
1754
  }
1671
1755
  }
1672
- const ok = expiryOk && (bodyDigestOk === null || bodyDigestOk === true);
1756
+ let replayOk = true;
1757
+ if (challenge?.digest && expiryOk) {
1758
+ const replayKey = `mpp:${challenge.digest}:${challenge.nonce ?? ""}`;
1759
+ const expiresAt = (nowSec + tolerance) * 1e3;
1760
+ if (nonceStore.seen(replayKey, expiresAt)) {
1761
+ replayOk = false;
1762
+ }
1763
+ }
1764
+ const ok = expiryOk && (bodyDigestOk === null || bodyDigestOk === true) && replayOk;
1673
1765
  const errors = [];
1674
1766
  if (!expiryOk) errors.push("challenge expired");
1675
- if (bodyDigestOk === false) errors.push("body digest mismatch");
1767
+ if (bodyDigestOk === false) {
1768
+ errors.push(
1769
+ input.rawBody !== void 0 && !challenge?.digest ? "body digest required when rawBody present" : "body digest mismatch"
1770
+ );
1771
+ }
1772
+ if (!replayOk) errors.push("MPP challenge replay \u2014 already seen within tolerance window");
1676
1773
  return {
1677
1774
  ok,
1678
1775
  expiryOk,
@@ -1836,14 +1933,32 @@ function readHeader4(headers, name) {
1836
1933
  import { createHash as createHash3, webcrypto } from "crypto";
1837
1934
  async function verifyVIChain(input) {
1838
1935
  const errors = [];
1839
- const tolerance = input.clockSkewSec ?? 300;
1936
+ const tolerance = input.clockSkewSec ?? 60;
1840
1937
  const now = input.now ? input.now() : Math.floor(Date.now() / 1e3);
1841
1938
  const { l1, l2, l3a, l3b } = input.layers;
1939
+ const nonceStore = input.nonceStore ?? defaultNonceStore;
1940
+ if (!l1) {
1941
+ if (!input.allowUnboundChain) {
1942
+ errors.push(
1943
+ "L1 missing \u2014 chain root unbound (set allowUnboundChain + expectedL2Key to override)"
1944
+ );
1945
+ } else if (!input.expectedL2Key) {
1946
+ errors.push("allowUnboundChain set but expectedL2Key missing");
1947
+ }
1948
+ }
1842
1949
  const l1SigOk = l1 ? await input.verifySignature(l1, null) : null;
1843
1950
  if (l1 && !l1SigOk) errors.push("L1 signature invalid");
1844
1951
  const l1Cnf = extractCnfJwk(l1?.payload);
1845
- const l2SigOk = await input.verifySignature(l2, l1Cnf ?? null);
1952
+ const l2ExpectedKey = l1Cnf ?? input.expectedL2Key ?? null;
1953
+ const l2SigOk = await input.verifySignature(l2, l2ExpectedKey);
1846
1954
  if (!l2SigOk) errors.push("L2 signature invalid");
1955
+ if (l2SigOk) {
1956
+ const replayKey = `vi:l2:${l2.compact}`;
1957
+ const expiresAt = now * 1e3 + tolerance * 1e3;
1958
+ if (nonceStore.seen(replayKey, expiresAt)) {
1959
+ errors.push("L2 signature replay \u2014 already seen within tolerance window");
1960
+ }
1961
+ }
1847
1962
  const l2Cnf = extractCnfJwk(l2.payload);
1848
1963
  const l3aSigOk = l3a ? await input.verifySignature(l3a, l2Cnf ?? null) : null;
1849
1964
  if (l3a && !l3aSigOk) errors.push("L3a signature invalid");
@@ -1887,7 +2002,10 @@ async function verifyVIChain(input) {
1887
2002
  }
1888
2003
  }
1889
2004
  const expiryOk = checkExpiryAcross([l1, l2, l3a, l3b], tolerance, now, errors);
1890
- const ok = l1SigOk !== false && l2SigOk && l3aSigOk !== false && l3bSigOk !== false && l1BindsL2 && l2BindsL3 && l3aL3bTxnIdMatch !== false && checkoutHashOk !== false && expiryOk;
2005
+ const noUnboundChainOrReplayErrors = !errors.some(
2006
+ (e) => e.startsWith("L1 missing") || e.startsWith("allowUnboundChain set") || e.startsWith("L2 signature replay")
2007
+ );
2008
+ const ok = l1SigOk !== false && l2SigOk && l3aSigOk !== false && l3bSigOk !== false && l1BindsL2 && l2BindsL3 && l3aL3bTxnIdMatch !== false && checkoutHashOk !== false && expiryOk && noUnboundChainOrReplayErrors;
1891
2009
  return {
1892
2010
  ok,
1893
2011
  checks: {