@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/README.md +4 -2
- package/dist/index.d.mts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +60 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +58 -4
- package/dist/index.mjs.map +1 -1
- package/dist/types.d.mts +9 -1
- package/dist/types.d.ts +9 -1
- package/dist/types.js.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/package.json +1 -1
- package/src/delegation-window.test.ts +82 -0
- package/src/federation.test.ts +21 -1
- package/src/federation.ts +3 -1
- package/src/index.ts +4 -0
- package/src/live-time.test.ts +151 -0
- package/src/private-scope.test.ts +4 -2
- package/src/revocation-authority.test.ts +15 -13
- package/src/revocation-failclosed.test.ts +1 -1
- package/src/scope.test.ts +20 -0
- package/src/test-vectors.test.ts +64 -8
- package/src/types.ts +9 -0
- package/src/verify.ts +114 -1
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
|