@orangecheck/agent-core 2.6.0 → 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);
@@ -544,6 +545,12 @@ async function verifyDelegation(input) {
544
545
  };
545
546
  }
546
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) {
547
554
  const a = input.action;
548
555
  const d = input.delegation;
549
556
  const chain = input.subdelegationChain ?? [];
@@ -621,6 +628,17 @@ async function verifyAction(input) {
621
628
  if (signed < issued || signed >= expires) {
622
629
  return err("E_OUT_OF_WINDOW", `action.signed_at ${a.signed_at} is outside leaf window [${leaf.issued_at}, ${leaf.expires_at})`);
623
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
+ }
624
642
  if (!leaf.scopes) {
625
643
  return err("E_SCOPES_NEITHER_PROVIDED", "leaf has no plaintext scopes after hydration");
626
644
  }
@@ -669,9 +687,21 @@ async function verifyAction(input) {
669
687
  if (revocationPrecedes(rev, revAnchor, a, actionAnchor)) {
670
688
  return err("E_REVOKED", `chain link ${link.id} was revoked by ${rev.id} before action was signed`);
671
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
+ }
672
693
  }
673
694
  }
674
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
+ }
675
705
  if (input.content) {
676
706
  const actualHash = "sha256:" + hexEncode(sha256(input.content));
677
707
  if (actualHash !== a.content.hash) {
@@ -699,7 +729,20 @@ async function verifyAction(input) {
699
729
  delegation: rootHydrated,
700
730
  chain: hydratedChain,
701
731
  scopeExercised: exercised,
702
- 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
+ }
703
746
  };
704
747
  }
705
748
  async function hydrateSubdelegationScopes(sub, input) {
@@ -1239,6 +1282,6 @@ function assertScopeGranted(delegation, scopeExercised, fnName) {
1239
1282
  }
1240
1283
  }
1241
1284
 
1242
- 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 };
1243
1286
  //# sourceMappingURL=index.mjs.map
1244
1287
  //# sourceMappingURL=index.mjs.map