@tinycloud/sdk-core 3.10.0 → 3.11.0-beta.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.cjs CHANGED
@@ -288,6 +288,7 @@ __export(index_exports, {
288
288
  canonicalizeSecretScope: () => import_sdk_services10.canonicalizeSecretScope,
289
289
  canonicalizeSignedObjectUnsigned: () => canonicalizeSignedObjectUnsigned,
290
290
  canonicalizeUnifiedPolicyCapability: () => canonicalizeUnifiedPolicyCapability,
291
+ checkChangedExpiration: () => checkChangedExpiration,
291
292
  checkDecryptInvocationInput: () => import_sdk_services10.checkDecryptInvocationInput,
292
293
  checkNodeInfo: () => checkNodeInfo,
293
294
  classifyRegistryLookup: () => classifyRegistryLookup,
@@ -16679,7 +16680,7 @@ function diffImmutableSiweFields(original, signed, options2) {
16679
16680
  "requestId",
16680
16681
  "nonRecapResources"
16681
16682
  ];
16682
- const keys = options2?.originalHasRecap ? baseKeys : [...baseKeys, "statement"];
16683
+ const keys = (options2?.originalHasRecap ? baseKeys : [...baseKeys, "statement"]).filter((key) => !(options2?.allowExpirationChange === true && key === "expirationTime"));
16683
16684
  const diffs = [];
16684
16685
  for (const key of keys) {
16685
16686
  const a = original[key] ?? "";
@@ -16690,6 +16691,62 @@ function diffImmutableSiweFields(original, signed, options2) {
16690
16691
  }
16691
16692
  return diffs;
16692
16693
  }
16694
+ var CHANGED_EXPIRY_MIN_REMAINING_MS = 6e4;
16695
+ var ISO_INSTANT = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;
16696
+ function parseInstantMs(value) {
16697
+ const match = value === void 0 ? null : ISO_INSTANT.exec(value);
16698
+ if (!match || value === void 0) return void 0;
16699
+ const [year, month, day] = [Number(match[1]), Number(match[2]), Number(match[3])];
16700
+ if (new Date(Date.UTC(year, month - 1, day)).getUTCDate() !== day) return void 0;
16701
+ const ms2 = Date.parse(value);
16702
+ return Number.isFinite(ms2) ? ms2 : void 0;
16703
+ }
16704
+ var EXPIRATION_LINE = /^Expiration Time:/;
16705
+ function hasCanonicalExpirationLine(siwe) {
16706
+ if (siwe.includes("\r")) return false;
16707
+ const lines = siwe.split("\n");
16708
+ const index = lines.findIndex((line) => EXPIRATION_LINE.test(line));
16709
+ if (index < 0 || !/^Expiration Time: \S+$/.test(lines[index])) return false;
16710
+ const resources = lines.indexOf("Resources:", 4);
16711
+ return resources < 0 || index < resources;
16712
+ }
16713
+ function checkChangedExpiration(input) {
16714
+ const nowMs = input.now instanceof Date ? input.now.getTime() : input.now;
16715
+ if (!Number.isFinite(nowMs)) return "expiry check needs a finite current time";
16716
+ const maxMs = input.maxExpiresAt === void 0 ? void 0 : input.maxExpiresAt instanceof Date ? input.maxExpiresAt.getTime() : input.maxExpiresAt;
16717
+ if (maxMs !== void 0 && !Number.isFinite(new Date(maxMs).getTime())) return "expiry check needs a finite maximum";
16718
+ const originalHasRecap = input.originalHasRecap ?? /(?:^|\n)-\s+urn:recap:/i.test(input.preparedSiwe);
16719
+ const countLines = (siwe) => siwe.split(/\r?\n/).filter((line) => EXPIRATION_LINE.test(line)).length;
16720
+ if (countLines(input.preparedSiwe) !== 1 || countLines(input.signedSiwe) !== 1) {
16721
+ return "expiry check needs exactly one Expiration Time line in both messages";
16722
+ }
16723
+ if (!hasCanonicalExpirationLine(input.signedSiwe)) {
16724
+ return "the Expiration Time line is not in canonical SIWE form";
16725
+ }
16726
+ const prepared = extractImmutableSiweFields(input.preparedSiwe);
16727
+ const signed = extractImmutableSiweFields(input.signedSiwe);
16728
+ const drift = diffImmutableSiweFields(prepared, signed, { originalHasRecap, allowExpirationChange: true });
16729
+ if (drift.length > 0) return `altered immutable SIWE fields: ${drift.join(", ")}`;
16730
+ if (signed.expirationTime === prepared.expirationTime) return null;
16731
+ const expiresAt = parseInstantMs(signed.expirationTime);
16732
+ if (expiresAt === void 0) return "expiry is not a valid timestamp";
16733
+ if (expiresAt <= nowMs + CHANGED_EXPIRY_MIN_REMAINING_MS) {
16734
+ return "expiry must be more than 60 seconds from now; choose a longer expiry";
16735
+ }
16736
+ for (const [label, value] of [["Issued At", prepared.issuedAt], ["Not Before", prepared.notBefore]]) {
16737
+ if (value === void 0 || value === "") continue;
16738
+ const bound = parseInstantMs(value);
16739
+ if (bound === void 0) return `${label} is not a valid timestamp`;
16740
+ if (expiresAt <= bound) return `expiry must be after ${label}; choose a later expiry`;
16741
+ }
16742
+ if (maxMs !== void 0 && expiresAt > maxMs) {
16743
+ return `expiry is later than the maximum for this request (the latest allowed is ${new Date(maxMs).toISOString()}); choose an earlier expiry`;
16744
+ }
16745
+ if (expiresAt > nowMs + EXPIRY.MAX_MS) {
16746
+ return "expiry is more than 10 years away, the longest this SDK accepts; choose a shorter expiry";
16747
+ }
16748
+ return null;
16749
+ }
16693
16750
  function extractRecapAttenuations(siwe) {
16694
16751
  const merged = {};
16695
16752
  const lines = siwe.split(/\r?\n/);