@orangecheck/agent-core 2.6.0 → 2.7.1

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) {
@@ -757,11 +800,11 @@ async function verifyRevocation(input) {
757
800
  if (env.delegation_id !== d.id) {
758
801
  return err("E_DELEGATION_MISMATCH", `revocation.delegation_id (${env.delegation_id}) != delegation.id (${d.id})`);
759
802
  }
760
- const holders = d.revocation?.holders ?? ["principal"];
761
- const holderAddrs = /* @__PURE__ */ new Set([d.principal.address]);
762
- if (Array.isArray(holders) && holders.includes("agent")) holderAddrs.add(d.agent.address);
763
- if (!holderAddrs.has(env.signer.address)) {
764
- return err("E_REVOKER_UNAUTHORIZED", `revocation signer ${env.signer.address} not in delegation holders`);
803
+ if (env.signer.address !== d.principal.address && env.signer.address !== d.agent.address) {
804
+ return err(
805
+ "E_REVOKER_UNAUTHORIZED",
806
+ `revocation signer ${env.signer.address} is neither the principal nor the agent`
807
+ );
765
808
  }
766
809
  const canonInput = {
767
810
  address: env.signer.address,
@@ -1144,10 +1187,6 @@ async function verifyRevocationOf(d, rev, input) {
1144
1187
  });
1145
1188
  }
1146
1189
  const v1 = rev;
1147
- const holders = d.revocation?.holders ?? ["principal"];
1148
- if (!Array.isArray(holders) || !holders.includes("agent")) {
1149
- return fail("E_REVOKER_UNAUTHORIZED", "only the federation may revoke this delegation");
1150
- }
1151
1190
  if (v1.kind !== "agent-revocation" || v1.signer?.address !== d.agent.address) {
1152
1191
  return fail("E_REVOKER_UNAUTHORIZED", "a single-address revocation must be signed by the agent");
1153
1192
  }
@@ -1239,6 +1278,6 @@ function assertScopeGranted(delegation, scopeExercised, fnName) {
1239
1278
  }
1240
1279
  }
1241
1280
 
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 };
1281
+ 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
1282
  //# sourceMappingURL=index.mjs.map
1244
1283
  //# sourceMappingURL=index.mjs.map