@orangecheck/agent-core 2.0.0 → 2.2.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
@@ -223,10 +223,15 @@ function rangeSubset(ex, g) {
223
223
  if (!exRange || !gRange) return false;
224
224
  return gRange.lo <= exRange.lo && exRange.hi <= gRange.hi;
225
225
  }
226
+ function parseDecimalInteger(value) {
227
+ if (!/^-?\d+$/.test(value)) return null;
228
+ const n = Number(value);
229
+ return Number.isSafeInteger(n) ? n : null;
230
+ }
226
231
  function opToRange(c) {
227
232
  if (c.value === void 0) return null;
228
- const n = Number(c.value);
229
- if (!Number.isFinite(n)) return null;
233
+ const n = parseDecimalInteger(c.value);
234
+ if (n === null) return null;
230
235
  switch (c.op) {
231
236
  case "=":
232
237
  return { lo: n, hi: n };
@@ -630,6 +635,12 @@ async function verifyAction(input) {
630
635
  "verifyAction requires `revocations` (fetch kind-30085 by #delegation for every link in the chain) or an explicit skipRevocationCheck:true"
631
636
  );
632
637
  }
638
+ let actionAnchor;
639
+ try {
640
+ actionAnchor = await verifiedAnchorHeight(a, input);
641
+ } catch (e) {
642
+ return err("E_MALFORMED", `anchor verifier threw: ${e.message}`);
643
+ }
633
644
  if (input.revocations && input.revocations.length > 0) {
634
645
  const allLinks = [rootHydrated, ...hydratedChain];
635
646
  for (const link of allLinks) {
@@ -642,9 +653,13 @@ async function verifyAction(input) {
642
653
  skipSignatureVerification: input.skipSignatureVerification
643
654
  });
644
655
  if (!rr.ok) continue;
645
- const effective = effectiveRevocationTime(rev, input.resolveAnchorBlockHeight);
646
- const actionTime = actionEffectiveTime(a, input.resolveAnchorBlockHeight);
647
- if (compareTimes(effective, actionTime) <= 0) {
656
+ let revAnchor;
657
+ try {
658
+ revAnchor = await verifiedAnchorHeight(rev, input);
659
+ } catch (e) {
660
+ return err("E_MALFORMED", `anchor verifier threw: ${e.message}`);
661
+ }
662
+ if (revocationPrecedes(rev, revAnchor, a, actionAnchor)) {
648
663
  return err("E_REVOKED", `chain link ${link.id} was revoked by ${rev.id} before action was signed`);
649
664
  }
650
665
  }
@@ -667,14 +682,7 @@ async function verifyAction(input) {
667
682
  if (h === null || hash === null) {
668
683
  return err("E_MALFORMED", "confirmed OTS proof missing block_height or block_hash");
669
684
  }
670
- let verified = false;
671
- if (input.verifyOtsAnchor) {
672
- try {
673
- verified = await input.verifyOtsAnchor(a.ots.proof, h, hash);
674
- } catch (e) {
675
- return err("E_MALFORMED", `anchor verifier threw: ${e.message}`);
676
- }
677
- }
685
+ const verified = actionAnchor !== null;
678
686
  anchor = { status: "confirmed", blockHeight: h, blockHash: hash, verified };
679
687
  }
680
688
  return {
@@ -744,9 +752,8 @@ async function verifyRevocation(input) {
744
752
  return err("E_DELEGATION_MISMATCH", `revocation.delegation_id (${env.delegation_id}) != delegation.id (${d.id})`);
745
753
  }
746
754
  const holders = d.revocation?.holders ?? ["principal"];
747
- const holderAddrs = /* @__PURE__ */ new Set();
748
- if (holders.includes("principal")) holderAddrs.add(d.principal.address);
749
- if (holders.includes("agent")) holderAddrs.add(d.agent.address);
755
+ const holderAddrs = /* @__PURE__ */ new Set([d.principal.address]);
756
+ if (Array.isArray(holders) && holders.includes("agent")) holderAddrs.add(d.agent.address);
750
757
  if (!holderAddrs.has(env.signer.address)) {
751
758
  return err("E_REVOKER_UNAUTHORIZED", `revocation signer ${env.signer.address} not in delegation holders`);
752
759
  }
@@ -946,25 +953,17 @@ async function verifySubdelegation(input) {
946
953
  }
947
954
  return r;
948
955
  }
949
- function actionEffectiveTime(a, resolve) {
950
- if (a.ots?.status === "confirmed") {
951
- const h = resolve ? resolve(a) : a.ots.block_height;
952
- if (h !== null && h !== void 0) return { kind: "anchor", blockHeight: h };
953
- }
954
- return { kind: "signed", ms: new Date(a.signed_at).getTime() };
955
- }
956
- function effectiveRevocationTime(r, resolve) {
957
- if (r.ots?.status === "confirmed") {
958
- const h = resolve ? resolve(r) : r.ots.block_height;
959
- if (h !== null && h !== void 0) return { kind: "anchor", blockHeight: h };
960
- }
961
- return { kind: "signed", ms: new Date(r.signed_at).getTime() };
956
+ async function verifiedAnchorHeight(env, input) {
957
+ if (env.ots?.status !== "confirmed" || !input.verifyOtsAnchor) return null;
958
+ const h = input.resolveAnchorBlockHeight ? input.resolveAnchorBlockHeight(env) : env.ots.block_height;
959
+ const hash = env.ots.block_hash;
960
+ if (h === null || h === void 0 || !Number.isInteger(h) || !hash) return null;
961
+ return await input.verifyOtsAnchor(env.ots.proof, h, hash, env.id) ? h : null;
962
962
  }
963
- function compareTimes(a, b) {
964
- if (a.kind === "anchor" && b.kind === "anchor") return a.blockHeight - b.blockHeight;
965
- if (a.kind === "anchor") return -1;
966
- if (b.kind === "anchor") return 1;
967
- return a.ms - b.ms;
963
+ function revocationPrecedes(rev, revAnchor, action, actionAnchor) {
964
+ if (revAnchor !== null && actionAnchor !== null) return revAnchor <= actionAnchor;
965
+ if (actionAnchor === null && rev.ots?.status === "confirmed") return true;
966
+ return new Date(rev.signed_at).getTime() <= new Date(action.signed_at).getTime();
968
967
  }
969
968
  function err(code, message) {
970
969
  return { ok: false, code, message };
@@ -1114,6 +1113,17 @@ async function verifyFederationRevocation(input) {
1114
1113
  return fail("E_MALFORMED", "reason must be a string \u2264128 bytes");
1115
1114
  }
1116
1115
  if (!ISO_UTC.test(env.signed_at)) return fail("E_MALFORMED", "signed_at must be ISO 8601 UTC");
1116
+ const d = input.delegation;
1117
+ if (!d || !HEX64.test(d.id ?? "")) return fail("E_MALFORMED", "the revoked delegation is required");
1118
+ if (env.delegation_id !== d.id) {
1119
+ return fail("E_DELEGATION_MISMATCH", `revocation.delegation_id (${env.delegation_id}) != delegation.id (${d.id})`);
1120
+ }
1121
+ if (d.principal?.alg !== "federation" || env.signer?.descriptor_id !== d.principal.descriptor_id) {
1122
+ return fail(
1123
+ "E_REVOKER_UNAUTHORIZED",
1124
+ `revoking federation (${env.signer?.descriptor_id}) is not the delegation's principal`
1125
+ );
1126
+ }
1117
1127
  const canonInput = {
1118
1128
  address: `federation:${env.signer?.descriptor_id ?? ""}`,
1119
1129
  delegation_id: env.delegation_id,