@orangecheck/agent-core 2.5.1 → 2.7.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.
package/dist/index.mjs CHANGED
@@ -410,6 +410,7 @@ function hasPrivateScopes(envelope) {
410
410
 
411
411
  // src/verify.ts
412
412
  var DEFAULT_MAX_CHAIN_DEPTH = 5;
413
+ var DEFAULT_MAX_SKEW_MS = 12e4;
413
414
  var AgentError = class extends Error {
414
415
  constructor(code, message) {
415
416
  super(message);
@@ -532,7 +533,6 @@ async function verifyDelegation(input) {
532
533
  const now = input.now ?? /* @__PURE__ */ new Date();
533
534
  const issued = new Date(env.issued_at);
534
535
  const expires = new Date(env.expires_at);
535
- if (expires <= issued) return err("E_MALFORMED", "expires_at <= issued_at");
536
536
  if (now < issued) return err("E_NOT_YET_VALID", `delegation not valid until ${env.issued_at}`);
537
537
  if (now >= expires) return err("E_EXPIRED", `delegation expired at ${env.expires_at}`);
538
538
  }
@@ -545,6 +545,12 @@ async function verifyDelegation(input) {
545
545
  };
546
546
  }
547
547
  async function verifyAction(input) {
548
+ return verifyActionAt(input, input.now ?? /* @__PURE__ */ new Date());
549
+ }
550
+ async function verifyActionAsOfSignedAt(input) {
551
+ return verifyActionAt(input, null);
552
+ }
553
+ async function verifyActionAt(input, now) {
548
554
  const a = input.action;
549
555
  const d = input.delegation;
550
556
  const chain = input.subdelegationChain ?? [];
@@ -622,6 +628,17 @@ async function verifyAction(input) {
622
628
  if (signed < issued || signed >= expires) {
623
629
  return err("E_OUT_OF_WINDOW", `action.signed_at ${a.signed_at} is outside leaf window [${leaf.issued_at}, ${leaf.expires_at})`);
624
630
  }
631
+ if (now) {
632
+ const t = now.getTime();
633
+ for (const link of [rootHydrated, ...hydratedChain]) {
634
+ if (t < new Date(link.issued_at).getTime()) {
635
+ return err("E_NOT_YET_VALID", `${link.id} is not valid until ${link.issued_at}`);
636
+ }
637
+ if (t >= new Date(link.expires_at).getTime()) {
638
+ return err("E_EXPIRED", `${link.id} expired at ${link.expires_at}`);
639
+ }
640
+ }
641
+ }
625
642
  if (!leaf.scopes) {
626
643
  return err("E_SCOPES_NEITHER_PROVIDED", "leaf has no plaintext scopes after hydration");
627
644
  }
@@ -670,9 +687,21 @@ async function verifyAction(input) {
670
687
  if (revocationPrecedes(rev, revAnchor, a, actionAnchor)) {
671
688
  return err("E_REVOKED", `chain link ${link.id} was revoked by ${rev.id} before action was signed`);
672
689
  }
690
+ if (now && (revAnchor !== null || new Date(rev.signed_at).getTime() <= now.getTime())) {
691
+ return err("E_REVOKED", `chain link ${link.id} was revoked by ${rev.id}`);
692
+ }
673
693
  }
674
694
  }
675
695
  }
696
+ if (now) {
697
+ const maxSkew = input.maxSkewMs ?? DEFAULT_MAX_SKEW_MS;
698
+ if (Math.abs(now.getTime() - signed) > maxSkew) {
699
+ return err(
700
+ "E_STALE",
701
+ `action.signed_at ${a.signed_at} is more than ${maxSkew} ms from the time of verification ${now.toISOString()}`
702
+ );
703
+ }
704
+ }
676
705
  if (input.content) {
677
706
  const actualHash = "sha256:" + hexEncode(sha256(input.content));
678
707
  if (actualHash !== a.content.hash) {
@@ -700,7 +729,20 @@ async function verifyAction(input) {
700
729
  delegation: rootHydrated,
701
730
  chain: hydratedChain,
702
731
  scopeExercised: exercised,
703
- anchor
732
+ anchor,
733
+ asOf: now ? { basis: "now", now: now.toISOString() } : { basis: "signed_at", signedAt: a.signed_at, anchored: actionAnchor !== null }
734
+ };
735
+ }
736
+ function createReplayGuard(maxSkewMs = DEFAULT_MAX_SKEW_MS) {
737
+ const keepUntil = /* @__PURE__ */ new Map();
738
+ return {
739
+ firstUse(action, now = /* @__PURE__ */ new Date()) {
740
+ const t = now.getTime();
741
+ for (const [id, until] of keepUntil) if (until <= t) keepUntil.delete(id);
742
+ if (keepUntil.has(action.id)) return false;
743
+ keepUntil.set(action.id, new Date(action.signed_at).getTime() + maxSkewMs);
744
+ return true;
745
+ }
704
746
  };
705
747
  }
706
748
  async function hydrateSubdelegationScopes(sub, input) {
@@ -797,11 +839,22 @@ function checkDelegationShape(env) {
797
839
  }
798
840
  if (!isIsoUtc(env.issued_at)) return err("E_MALFORMED", "issued_at must be ISO 8601 UTC");
799
841
  if (!isIsoUtc(env.expires_at)) return err("E_MALFORMED", "expires_at must be ISO 8601 UTC");
842
+ const windowError = delegationWindowError(env.issued_at, env.expires_at);
843
+ if (windowError) return err("E_MALFORMED", windowError);
800
844
  if (!/^[0-9a-f]{32}$/.test(env.nonce)) return err("E_MALFORMED", "nonce must be 32 lowercase hex chars");
801
845
  if (env.sig?.alg !== "bip322" || typeof env.sig.value !== "string") return err("E_MALFORMED", "sig invalid");
802
846
  if (env.sig.pubkey !== env.principal.address) return err("E_MALFORMED", "sig.pubkey must equal principal.address");
803
847
  return null;
804
848
  }
849
+ var MAX_DELEGATION_MS = 365 * 864e5;
850
+ function delegationWindowError(issuedAt, expiresAt) {
851
+ const issued = Date.parse(issuedAt);
852
+ const expires = Date.parse(expiresAt);
853
+ if (Number.isNaN(issued) || Number.isNaN(expires)) return "issued_at / expires_at must be real UTC times";
854
+ if (expires <= issued) return "expires_at must be after issued_at";
855
+ if (expires - issued > MAX_DELEGATION_MS) return "expires_at is more than 365 days after issued_at (SPEC \xA74.4)";
856
+ return null;
857
+ }
805
858
  function checkActionShape(a) {
806
859
  if (a.kind !== "agent-action") return err("E_MALFORMED", 'kind must be "agent-action"');
807
860
  if (!isHex64(a.id)) return err("E_MALFORMED", "id must be 64 lowercase hex chars");
@@ -1073,6 +1126,8 @@ async function verifyFederationDelegation(input) {
1073
1126
  if (!ISO_UTC.test(env.issued_at) || !ISO_UTC.test(env.expires_at)) {
1074
1127
  return fail("E_MALFORMED", "issued_at / expires_at must be ISO 8601 UTC");
1075
1128
  }
1129
+ const windowError = delegationWindowError(env.issued_at, env.expires_at);
1130
+ if (windowError) return fail("E_MALFORMED", windowError);
1076
1131
  if (!/^[0-9a-f]{32}$/.test(env.nonce)) return fail("E_MALFORMED", "nonce must be 32 hex chars");
1077
1132
  let canonicalScopes;
1078
1133
  try {
@@ -1117,7 +1172,6 @@ async function verifyFederationDelegation(input) {
1117
1172
  const now = input.now ?? /* @__PURE__ */ new Date();
1118
1173
  const issued = new Date(env.issued_at);
1119
1174
  const expires = new Date(env.expires_at);
1120
- if (expires <= issued) return fail("E_MALFORMED", "expires_at <= issued_at");
1121
1175
  if (now < issued) return fail("E_NOT_YET_VALID", `delegation not valid until ${env.issued_at}`);
1122
1176
  if (now >= expires) return fail("E_EXPIRED", `delegation expired at ${env.expires_at}`);
1123
1177
  }
@@ -1228,6 +1282,6 @@ function assertScopeGranted(delegation, scopeExercised, fnName) {
1228
1282
  }
1229
1283
  }
1230
1284
 
1231
- export { AgentError, DEFAULT_MAX_CHAIN_DEPTH, ENVELOPE_VERSION, REGISTERED_SCOPES, ScopeNotGrantedError, ScopeParseError, actionCanonicalBytes, actionCanonicalMessage, assertScopeGranted, canonicalActionBytes, canonicalDelegationBytes, canonicalRevocationBytes, canonicalizeAction, canonicalizeDelegation, canonicalizeRevocation, canonicalizeScope, canonicalizeScopeString, canonicalizeScopes, computeActionId, computeDelegationId, computeFederationDescriptorId, computeRevocationId, computeSubdelegationId, decodeScopesPayload, delegationCanonicalBytes, delegationCanonicalMessage, encodeScopesPayload, federationDescriptorCanonicalMessage, hasPrivateScopes, isSubScope, parseAndCanonicalizeScopes, parseScope, revocationCanonicalBytes, revocationCanonicalMessage, sealScopes, sha256Hex, subdelegationCanonicalBytes, subdelegationCanonicalMessage, unsealScopes, validateScope, verifyAction, verifyDelegation, verifyFederationDelegation, verifyFederationRevocation, verifyRevocation, verifySubdelegation };
1285
+ export { AgentError, DEFAULT_MAX_CHAIN_DEPTH, DEFAULT_MAX_SKEW_MS, ENVELOPE_VERSION, REGISTERED_SCOPES, ScopeNotGrantedError, ScopeParseError, actionCanonicalBytes, actionCanonicalMessage, assertScopeGranted, canonicalActionBytes, canonicalDelegationBytes, canonicalRevocationBytes, canonicalizeAction, canonicalizeDelegation, canonicalizeRevocation, canonicalizeScope, canonicalizeScopeString, canonicalizeScopes, computeActionId, computeDelegationId, computeFederationDescriptorId, computeRevocationId, computeSubdelegationId, createReplayGuard, decodeScopesPayload, delegationCanonicalBytes, delegationCanonicalMessage, encodeScopesPayload, federationDescriptorCanonicalMessage, hasPrivateScopes, isSubScope, parseAndCanonicalizeScopes, parseScope, revocationCanonicalBytes, revocationCanonicalMessage, sealScopes, sha256Hex, subdelegationCanonicalBytes, subdelegationCanonicalMessage, unsealScopes, validateScope, verifyAction, verifyActionAsOfSignedAt, verifyDelegation, verifyFederationDelegation, verifyFederationRevocation, verifyRevocation, verifySubdelegation };
1232
1286
  //# sourceMappingURL=index.mjs.map
1233
1287
  //# sourceMappingURL=index.mjs.map