@fedify/fedify 2.4.0-dev.1964 → 2.4.0-dev.1988

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.
Files changed (83) hide show
  1. package/dist/{builder-B4zpKIFU.mjs → builder-Cts78G9N.mjs} +2 -2
  2. package/dist/{client-CtxvmazV.mjs → client-DpnpgjyX.mjs} +78 -4
  3. package/dist/compat/mod.d.cts +1 -1
  4. package/dist/compat/mod.d.ts +1 -1
  5. package/dist/compat/transformers.test.mjs +1 -1
  6. package/dist/{context-COLwHogJ.d.cts → context-BeDnDuF3.d.cts} +4 -3
  7. package/dist/{context-DiiCt4S7.d.ts → context-uyRUWoZQ.d.ts} +4 -3
  8. package/dist/{deno-CtG93ohj.mjs → deno-BgMEefEr.mjs} +1 -1
  9. package/dist/{docloader-dk_k3S8d.mjs → docloader-DEYs99FZ.mjs} +2 -2
  10. package/dist/federation/builder.test.mjs +1 -1
  11. package/dist/federation/handler.test.mjs +122 -4
  12. package/dist/federation/idempotency.test.mjs +2 -2
  13. package/dist/federation/keycache.test.mjs +52 -9
  14. package/dist/federation/metrics.test.mjs +1 -1
  15. package/dist/federation/middleware.test.mjs +111 -15
  16. package/dist/federation/mod.cjs +1 -1
  17. package/dist/federation/mod.d.cts +2 -2
  18. package/dist/federation/mod.d.ts +2 -2
  19. package/dist/federation/mod.js +1 -1
  20. package/dist/federation/send.test.mjs +139 -4
  21. package/dist/federation/tasks/enqueue.test.mjs +1 -1
  22. package/dist/federation/tasks/tasks.test.mjs +2 -2
  23. package/dist/federation/temporal.test.mjs +1 -1
  24. package/dist/federation/webfinger.test.mjs +1 -1
  25. package/dist/{federation-Drjlrrax.cjs → federation-DzPUG_AP.cjs} +93 -17
  26. package/dist/{http-6TI0tI03.d.cts → http-65PYi9jY.d.ts} +34 -2
  27. package/dist/{http-CdrSn2ip.d.ts → http-CP-7OdSq.d.cts} +34 -2
  28. package/dist/{http-Dj4s1UQ0.js → http-CgwqtxAy.js} +169 -7
  29. package/dist/{http-D7LX-BL9.mjs → http-CtidZS_t.mjs} +3 -3
  30. package/dist/{http-B3skRvGo.cjs → http-Z3anvCd-.cjs} +180 -6
  31. package/dist/{key-BVjgbs_9.mjs → key-Cwbg5ggV.mjs} +169 -7
  32. package/dist/{keycache-ZLqIfrkL.mjs → keycache-BUDHB5rY.mjs} +12 -4
  33. package/dist/{kv-cache-DtsjH_bY.cjs → kv-cache-BM2-w-Wq.cjs} +2 -2
  34. package/dist/{kv-cache-tGNbvDCF.js → kv-cache-CykuL0ya.js} +2 -2
  35. package/dist/{kv-cache-xRcC_xxP.mjs → kv-cache-DHiR8yTf.mjs} +1 -1
  36. package/dist/{ld-OuCeQFny.mjs → ld-CzhdWyTt.mjs} +3 -3
  37. package/dist/{metrics-B9N27cCB.mjs → metrics-CrqsHP0l.mjs} +1 -1
  38. package/dist/{middleware-Cd_vPqDH.mjs → middleware-CZpYxSKD.mjs} +1 -1
  39. package/dist/{middleware-CPTDFMmJ.js → middleware-CeCj5caM.js} +101 -25
  40. package/dist/{middleware-TeLjzoxn.mjs → middleware-DCpiAsZi.mjs} +34 -23
  41. package/dist/{mod-DypxPS9H.d.ts → mod-C0BYc0OU.d.ts} +1 -1
  42. package/dist/{mod-CnGLNoVw.d.cts → mod-CbKDOnCt.d.cts} +1 -1
  43. package/dist/{mod-CLHkCwLm.d.cts → mod-CyiSjRkk.d.cts} +1 -1
  44. package/dist/{mod-CSH1Ee-I.d.ts → mod-D882_Xsg.d.ts} +1 -1
  45. package/dist/{mod-DzRXACM5.d.ts → mod-UDwMjuHM.d.ts} +1 -1
  46. package/dist/{mod-BZldCEU0.d.cts → mod-x9W0d0Pk.d.cts} +1 -1
  47. package/dist/mod.cjs +5 -5
  48. package/dist/mod.d.cts +6 -6
  49. package/dist/mod.d.ts +6 -6
  50. package/dist/mod.js +5 -5
  51. package/dist/nodeinfo/client.test.mjs +28 -1
  52. package/dist/nodeinfo/handler.test.mjs +1 -1
  53. package/dist/nodeinfo/mod.cjs +1 -1
  54. package/dist/nodeinfo/mod.js +1 -1
  55. package/dist/{owner-DGSfntI-.d.ts → owner-BF0L9cMt.d.cts} +12 -1
  56. package/dist/{owner-r_waTiQj.d.cts → owner-BSzr5-I8.d.ts} +12 -1
  57. package/dist/owner-BqVXJLW8.mjs +175 -0
  58. package/dist/{proof-BO_esQii.js → proof-CUghqhAf.js} +122 -65
  59. package/dist/{proof-Dua0d906.cjs → proof-DHu5zI0Z.cjs} +122 -65
  60. package/dist/{proof-D5Ajk4DJ.mjs → proof-DL8Iid3a.mjs} +14 -11
  61. package/dist/{send-DVXyUqhO.mjs → send-6jS4xFsX.mjs} +68 -11
  62. package/dist/sig/http.test.mjs +2 -2
  63. package/dist/sig/key.test.mjs +169 -2
  64. package/dist/sig/ld.test.mjs +55 -2
  65. package/dist/sig/mod.cjs +2 -2
  66. package/dist/sig/mod.d.cts +3 -3
  67. package/dist/sig/mod.d.ts +3 -3
  68. package/dist/sig/mod.js +2 -2
  69. package/dist/sig/owner.test.mjs +103 -4
  70. package/dist/sig/proof.test.mjs +120 -1
  71. package/dist/{temporal-BoDw51A-.mjs → temporal-DcyzeytP.mjs} +1 -1
  72. package/dist/testing/kv.test.mjs +1 -1
  73. package/dist/testing/mod.d.mts +2 -1
  74. package/dist/{types-RXm5YxpC.cjs → types-Be2zbyeG.cjs} +94 -2
  75. package/dist/{types-Dtyf8lVj.js → types-C_cNlNVK.js} +78 -4
  76. package/dist/utils/docloader.test.mjs +27 -3
  77. package/dist/utils/kv-cache.test.mjs +1 -1
  78. package/dist/utils/mod.cjs +1 -1
  79. package/dist/utils/mod.d.cts +1 -1
  80. package/dist/utils/mod.d.ts +1 -1
  81. package/dist/utils/mod.js +1 -1
  82. package/package.json +6 -6
  83. package/dist/owner-DTEqTzSr.mjs +0 -120
@@ -1,6 +1,80 @@
1
1
  const { URLPattern } = require("urlpattern-polyfill");
2
2
  let _logtape_logtape = require("@logtape/logtape");
3
3
  let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
4
+ //#region src/utils/body.ts
5
+ /** Decoded JSON body limit: 16 MiB. @internal */
6
+ const MAX_BODY_SIZE = 16 * 1024 * 1024;
7
+ /** A body exceeded the byte limit. @internal */
8
+ var BodyTooLargeError = class extends _fedify_vocab_runtime.FetchError {
9
+ /** Creates an error for a body exceeding the given limit. */
10
+ constructor(url, maxBytes) {
11
+ super(url, `Body exceeds the limit of ${maxBytes} bytes`);
12
+ this.name = "BodyTooLargeError";
13
+ }
14
+ };
15
+ /** Validates a finite, positive byte limit. @internal */
16
+ function validateBodySizeLimit(maxBytes) {
17
+ if (!Number.isSafeInteger(maxBytes) || maxBytes <= 0) throw new RangeError("The body size limit must be a positive safe integer.");
18
+ }
19
+ /**
20
+ * Reads UTF-8 text while limiting bytes received from the body stream.
21
+ * Fetch responses are already decompressed, so the stream count is authoritative.
22
+ * @param message The response or request to read.
23
+ * @param maxBytes The maximum number of decoded bytes to read.
24
+ * @param url The URL to include in errors and logs.
25
+ * @returns The decoded body text.
26
+ * @throws {BodyTooLargeError} If the body exceeds the limit.
27
+ * @internal
28
+ */
29
+ async function readBoundedText(message, maxBytes, url) {
30
+ validateBodySizeLimit(maxBytes);
31
+ const reader = message.body?.getReader();
32
+ const tooLarge = () => {
33
+ (0, _logtape_logtape.getLogger)([
34
+ "fedify",
35
+ "runtime",
36
+ "body"
37
+ ]).warn("Body from {url} exceeds the limit of {maxBytes} bytes.", {
38
+ url: url.toString(),
39
+ maxBytes
40
+ });
41
+ throw new BodyTooLargeError(url, maxBytes);
42
+ };
43
+ try {
44
+ const length = message.headers.get("Content-Length");
45
+ const encoding = message.headers.get("Content-Encoding");
46
+ if ((encoding == null || encoding.toLowerCase() === "identity") && length != null && /^\d+$/.test(length) && Number(length) > maxBytes) tooLarge();
47
+ if (reader == null) return "";
48
+ let size = 0;
49
+ const textReader = new ReadableStream({ async pull(controller) {
50
+ const { done, value } = await reader.read();
51
+ if (done) {
52
+ controller.close();
53
+ return;
54
+ }
55
+ size += value.byteLength;
56
+ if (size > maxBytes) tooLarge();
57
+ controller.enqueue(value);
58
+ } }, { highWaterMark: 0 }).pipeThrough(new TextDecoderStream()).getReader();
59
+ try {
60
+ const chunks = [];
61
+ while (true) {
62
+ const { done, value } = await textReader.read();
63
+ if (done) break;
64
+ chunks.push(value);
65
+ }
66
+ return chunks.join("");
67
+ } finally {
68
+ textReader.releaseLock();
69
+ }
70
+ } catch (error) {
71
+ if (reader != null) reader.cancel(error).catch(() => {});
72
+ throw error;
73
+ } finally {
74
+ reader?.releaseLock();
75
+ }
76
+ }
77
+ //#endregion
4
78
  //#region src/nodeinfo/client.ts
5
79
  const logger = (0, _logtape_logtape.getLogger)([
6
80
  "fedify",
@@ -81,7 +155,7 @@ async function getNodeInfo(url, options = {}) {
81
155
  });
82
156
  return;
83
157
  }
84
- const wellKnownRd = await wellKnownResponse.json();
158
+ const wellKnownRd = JSON.parse(await readBoundedText(wellKnownResponse, MAX_BODY_SIZE, wellKnownUrl));
85
159
  const link = wellKnownRd?.links?.find((link) => link != null && "rel" in link && (link.rel === "http://nodeinfo.diaspora.software/ns/schema/2.0" || link.rel === "http://nodeinfo.diaspora.software/ns/schema/2.1") && "href" in link && link.href != null);
86
160
  if (link == null || link.href == null) {
87
161
  logger.error("Failed to find a NodeInfo document link from {url}: {resourceDescriptor}", {
@@ -105,7 +179,7 @@ async function getNodeInfo(url, options = {}) {
105
179
  });
106
180
  return;
107
181
  }
108
- const data = await response.json();
182
+ const data = JSON.parse(await readBoundedText(response, MAX_BODY_SIZE, nodeInfoUrl));
109
183
  if (options.parse === "none") return data;
110
184
  return parseNodeInfo(data, { tryBestEffort: options.parse === "best-effort" }) ?? void 0;
111
185
  } catch (error) {
@@ -351,6 +425,18 @@ function nodeInfoToJson(nodeInfo) {
351
425
  };
352
426
  }
353
427
  //#endregion
428
+ Object.defineProperty(exports, "BodyTooLargeError", {
429
+ enumerable: true,
430
+ get: function() {
431
+ return BodyTooLargeError;
432
+ }
433
+ });
434
+ Object.defineProperty(exports, "MAX_BODY_SIZE", {
435
+ enumerable: true,
436
+ get: function() {
437
+ return MAX_BODY_SIZE;
438
+ }
439
+ });
354
440
  Object.defineProperty(exports, "getNodeInfo", {
355
441
  enumerable: true,
356
442
  get: function() {
@@ -369,3 +455,9 @@ Object.defineProperty(exports, "parseNodeInfo", {
369
455
  return parseNodeInfo;
370
456
  }
371
457
  });
458
+ Object.defineProperty(exports, "readBoundedText", {
459
+ enumerable: true,
460
+ get: function() {
461
+ return readBoundedText;
462
+ }
463
+ });
@@ -1,7 +1,81 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
3
  import { getLogger } from "@logtape/logtape";
4
- import { UrlError, getUserAgent, validatePublicUrl } from "@fedify/vocab-runtime";
4
+ import { FetchError, UrlError, getUserAgent, validatePublicUrl } from "@fedify/vocab-runtime";
5
+ //#region src/utils/body.ts
6
+ /** Decoded JSON body limit: 16 MiB. @internal */
7
+ const MAX_BODY_SIZE = 16 * 1024 * 1024;
8
+ /** A body exceeded the byte limit. @internal */
9
+ var BodyTooLargeError = class extends FetchError {
10
+ /** Creates an error for a body exceeding the given limit. */
11
+ constructor(url, maxBytes) {
12
+ super(url, `Body exceeds the limit of ${maxBytes} bytes`);
13
+ this.name = "BodyTooLargeError";
14
+ }
15
+ };
16
+ /** Validates a finite, positive byte limit. @internal */
17
+ function validateBodySizeLimit(maxBytes) {
18
+ if (!Number.isSafeInteger(maxBytes) || maxBytes <= 0) throw new RangeError("The body size limit must be a positive safe integer.");
19
+ }
20
+ /**
21
+ * Reads UTF-8 text while limiting bytes received from the body stream.
22
+ * Fetch responses are already decompressed, so the stream count is authoritative.
23
+ * @param message The response or request to read.
24
+ * @param maxBytes The maximum number of decoded bytes to read.
25
+ * @param url The URL to include in errors and logs.
26
+ * @returns The decoded body text.
27
+ * @throws {BodyTooLargeError} If the body exceeds the limit.
28
+ * @internal
29
+ */
30
+ async function readBoundedText(message, maxBytes, url) {
31
+ validateBodySizeLimit(maxBytes);
32
+ const reader = message.body?.getReader();
33
+ const tooLarge = () => {
34
+ getLogger([
35
+ "fedify",
36
+ "runtime",
37
+ "body"
38
+ ]).warn("Body from {url} exceeds the limit of {maxBytes} bytes.", {
39
+ url: url.toString(),
40
+ maxBytes
41
+ });
42
+ throw new BodyTooLargeError(url, maxBytes);
43
+ };
44
+ try {
45
+ const length = message.headers.get("Content-Length");
46
+ const encoding = message.headers.get("Content-Encoding");
47
+ if ((encoding == null || encoding.toLowerCase() === "identity") && length != null && /^\d+$/.test(length) && Number(length) > maxBytes) tooLarge();
48
+ if (reader == null) return "";
49
+ let size = 0;
50
+ const textReader = new ReadableStream({ async pull(controller) {
51
+ const { done, value } = await reader.read();
52
+ if (done) {
53
+ controller.close();
54
+ return;
55
+ }
56
+ size += value.byteLength;
57
+ if (size > maxBytes) tooLarge();
58
+ controller.enqueue(value);
59
+ } }, { highWaterMark: 0 }).pipeThrough(new TextDecoderStream()).getReader();
60
+ try {
61
+ const chunks = [];
62
+ while (true) {
63
+ const { done, value } = await textReader.read();
64
+ if (done) break;
65
+ chunks.push(value);
66
+ }
67
+ return chunks.join("");
68
+ } finally {
69
+ textReader.releaseLock();
70
+ }
71
+ } catch (error) {
72
+ if (reader != null) reader.cancel(error).catch(() => {});
73
+ throw error;
74
+ } finally {
75
+ reader?.releaseLock();
76
+ }
77
+ }
78
+ //#endregion
5
79
  //#region src/nodeinfo/client.ts
6
80
  const logger = getLogger([
7
81
  "fedify",
@@ -82,7 +156,7 @@ async function getNodeInfo(url, options = {}) {
82
156
  });
83
157
  return;
84
158
  }
85
- const wellKnownRd = await wellKnownResponse.json();
159
+ const wellKnownRd = JSON.parse(await readBoundedText(wellKnownResponse, MAX_BODY_SIZE, wellKnownUrl));
86
160
  const link = wellKnownRd?.links?.find((link) => link != null && "rel" in link && (link.rel === "http://nodeinfo.diaspora.software/ns/schema/2.0" || link.rel === "http://nodeinfo.diaspora.software/ns/schema/2.1") && "href" in link && link.href != null);
87
161
  if (link == null || link.href == null) {
88
162
  logger.error("Failed to find a NodeInfo document link from {url}: {resourceDescriptor}", {
@@ -106,7 +180,7 @@ async function getNodeInfo(url, options = {}) {
106
180
  });
107
181
  return;
108
182
  }
109
- const data = await response.json();
183
+ const data = JSON.parse(await readBoundedText(response, MAX_BODY_SIZE, nodeInfoUrl));
110
184
  if (options.parse === "none") return data;
111
185
  return parseNodeInfo(data, { tryBestEffort: options.parse === "best-effort" }) ?? void 0;
112
186
  } catch (error) {
@@ -352,4 +426,4 @@ function nodeInfoToJson(nodeInfo) {
352
426
  };
353
427
  }
354
428
  //#endregion
355
- export { getNodeInfo as n, parseNodeInfo as r, nodeInfoToJson as t };
429
+ export { MAX_BODY_SIZE as a, BodyTooLargeError as i, getNodeInfo as n, readBoundedText as o, parseNodeInfo as r, nodeInfoToJson as t };
@@ -5,11 +5,11 @@ import { t as esm_default } from "../esm-BHXXx4Wx.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-CBlRfA-_.mjs";
6
6
  import "../std__assert-CxFT0MNt.mjs";
7
7
  import { t as assertRejects } from "../assert_rejects-wFbSPn1O.mjs";
8
- import { l as verifyRequest } from "../http-D7LX-BL9.mjs";
8
+ import { l as verifyRequest } from "../http-CtidZS_t.mjs";
9
9
  import { i as rsaPrivateKey2 } from "../keys-DIJNHW-Y.mjs";
10
- import { t as getAuthenticatedDocumentLoader } from "../docloader-dk_k3S8d.mjs";
10
+ import { t as getAuthenticatedDocumentLoader } from "../docloader-DEYs99FZ.mjs";
11
11
  import { mockDocumentLoader, test } from "@fedify/fixture";
12
- import { UrlError } from "@fedify/vocab-runtime";
12
+ import { FetchError, UrlError } from "@fedify/vocab-runtime";
13
13
  //#region src/utils/docloader.test.ts
14
14
  test("getAuthenticatedDocumentLoader()", async (t) => {
15
15
  esm_default.spyGlobal();
@@ -136,5 +136,29 @@ test("getAuthenticatedDocumentLoader() cancellation", {
136
136
  });
137
137
  esm_default.hardReset();
138
138
  });
139
+ test("getAuthenticatedDocumentLoader() bounds JSON after redirects", async () => {
140
+ esm_default.mockGlobal();
141
+ let oversized = true;
142
+ try {
143
+ const url = "https://example.com/bounded";
144
+ esm_default.get(url, {
145
+ status: 302,
146
+ headers: { Location: `${url}/document` }
147
+ });
148
+ esm_default.get(`${url}/document`, () => new Response("{\"name\":\"hello\"}", { headers: {
149
+ "Content-Type": "application/activity+json",
150
+ ...oversized ? { "Content-Length": String(16777217) } : {}
151
+ } }));
152
+ const loader = getAuthenticatedDocumentLoader({
153
+ privateKey: rsaPrivateKey2,
154
+ keyId: new URL("https://example.com/key2")
155
+ }, { allowPrivateAddress: true });
156
+ await assertRejects(() => loader(url), FetchError);
157
+ oversized = false;
158
+ assertEquals((await loader(url)).document, { name: "hello" });
159
+ } finally {
160
+ esm_default.hardReset();
161
+ }
162
+ });
139
163
  //#endregion
140
164
  export {};
@@ -1,7 +1,7 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as kvCache, t as MockKvStore } from "../kv-cache-xRcC_xxP.mjs";
4
+ import { n as kvCache, t as MockKvStore } from "../kv-cache-DHiR8yTf.mjs";
5
5
  import { deepStrictEqual, throws } from "node:assert";
6
6
  import { createTestMeterProvider, mockDocumentLoader, test } from "@fedify/fixture";
7
7
  import { preloadedContexts } from "@fedify/vocab-runtime";
@@ -1,5 +1,5 @@
1
1
  const { URLPattern } = require("urlpattern-polyfill");
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const require_kv_cache = require("../kv-cache-DtsjH_bY.cjs");
3
+ const require_kv_cache = require("../kv-cache-BM2-w-Wq.cjs");
4
4
  exports.getAuthenticatedDocumentLoader = require_kv_cache.getAuthenticatedDocumentLoader;
5
5
  exports.kvCache = require_kv_cache.kvCache;
@@ -1,3 +1,3 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../mod-CLHkCwLm.cjs";
2
+ import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../mod-CyiSjRkk.cjs";
3
3
  export { getAuthenticatedDocumentLoader, kvCache };
@@ -1,3 +1,3 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../mod-DypxPS9H.js";
2
+ import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../mod-C0BYc0OU.js";
3
3
  export { getAuthenticatedDocumentLoader, kvCache };
package/dist/utils/mod.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../kv-cache-tGNbvDCF.js";
3
+ import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../kv-cache-CykuL0ya.js";
4
4
  export { getAuthenticatedDocumentLoader, kvCache };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/fedify",
3
- "version": "2.4.0-dev.1964+1d28dc03",
3
+ "version": "2.4.0-dev.1988+c5c9d617",
4
4
  "description": "An ActivityPub server framework",
5
5
  "keywords": [
6
6
  "ActivityPub",
@@ -151,10 +151,10 @@
151
151
  "structured-field-values": "^2.0.4",
152
152
  "temporal-polyfill": "^1.0.1",
153
153
  "urlpattern-polyfill": "^10.1.0",
154
- "@fedify/vocab": "2.4.0-dev.1964+1d28dc03",
155
- "@fedify/webfinger": "2.4.0-dev.1964+1d28dc03",
156
- "@fedify/uri-template": "2.4.0-dev.1964+1d28dc03",
157
- "@fedify/vocab-runtime": "2.4.0-dev.1964+1d28dc03"
154
+ "@fedify/uri-template": "2.4.0-dev.1988+c5c9d617",
155
+ "@fedify/vocab": "2.4.0-dev.1988+c5c9d617",
156
+ "@fedify/vocab-runtime": "2.4.0-dev.1988+c5c9d617",
157
+ "@fedify/webfinger": "2.4.0-dev.1988+c5c9d617"
158
158
  },
159
159
  "devDependencies": {
160
160
  "@std/assert": "npm:@jsr/std__assert@^0.226.0",
@@ -167,7 +167,7 @@
167
167
  "typescript": "^6.0.0",
168
168
  "wrangler": "^4.17.0",
169
169
  "@fedify/fixture": "2.0.0",
170
- "@fedify/vocab-tools": "^2.4.0-dev.1964+1d28dc03"
170
+ "@fedify/vocab-tools": "^2.4.0-dev.1988+c5c9d617"
171
171
  },
172
172
  "scripts": {
173
173
  "build:self": "tsdown",
@@ -1,120 +0,0 @@
1
- import "temporal-polyfill";
2
- import "urlpattern-polyfill";
3
- globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-CtG93ohj.mjs";
5
- import "./key-BVjgbs_9.mjs";
6
- import { CryptographicKey, Object as Object$1, isActor } from "@fedify/vocab";
7
- import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
8
- import { getDocumentLoader } from "@fedify/vocab-runtime";
9
- //#region src/sig/owner.ts
10
- /**
11
- * Checks if the actor of the given activity owns the specified key.
12
- * @param activity The activity to check.
13
- * @param key The public key to check.
14
- * @param options Options for checking the key ownership.
15
- * @returns Whether the actor is the owner of the key.
16
- */
17
- async function doesActorOwnKey(activity, key, options) {
18
- return await (options.tracerProvider ?? trace.getTracerProvider()).getTracer(name, version).startActiveSpan("activitypub.verify_key_ownership", {
19
- kind: SpanKind.INTERNAL,
20
- attributes: {
21
- "activitypub.actor.id": activity.actorId?.href ?? "",
22
- "activitypub.key.id": key.id?.href ?? ""
23
- }
24
- }, async (span) => {
25
- try {
26
- if (key.ownerId != null) {
27
- const owns = key.ownerId.href === activity.actorId?.href;
28
- span.setAttribute("activitypub.key_ownership.verified", owns);
29
- span.setAttribute("activitypub.key_ownership.method", "owner_id");
30
- return owns;
31
- }
32
- const actor = await activity.getActor(options);
33
- if (actor == null || !isActor(actor)) {
34
- span.setAttribute("activitypub.key_ownership.verified", false);
35
- span.setAttribute("activitypub.key_ownership.method", "actor_fetch");
36
- return false;
37
- }
38
- for (const publicKeyId of actor.publicKeyIds) if (key.id != null && publicKeyId.href === key.id.href) {
39
- span.setAttribute("activitypub.key_ownership.verified", true);
40
- span.setAttribute("activitypub.key_ownership.method", "actor_fetch");
41
- return true;
42
- }
43
- span.setAttribute("activitypub.key_ownership.verified", false);
44
- span.setAttribute("activitypub.key_ownership.method", "actor_fetch");
45
- return false;
46
- } catch (error) {
47
- span.recordException(error);
48
- span.setStatus({
49
- code: SpanStatusCode.ERROR,
50
- message: String(error)
51
- });
52
- throw error;
53
- } finally {
54
- span.end();
55
- }
56
- });
57
- }
58
- /**
59
- * Gets the actor that owns the specified key. Returns `null` if the key has no
60
- * known owner.
61
- *
62
- * @param keyId The ID of the key to check, or the key itself.
63
- * @param options Options for getting the key owner.
64
- * @returns The actor that owns the key, or `null` if the key has no known
65
- * owner.
66
- * @since 0.7.0
67
- */
68
- async function getKeyOwner(keyId, options) {
69
- const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
70
- const documentLoader = options.documentLoader ?? getDocumentLoader();
71
- const contextLoader = options.contextLoader ?? getDocumentLoader();
72
- let object;
73
- if (keyId instanceof CryptographicKey) {
74
- object = keyId;
75
- if (object.id == null) return null;
76
- keyId = object.id;
77
- } else {
78
- let keyDoc;
79
- try {
80
- const { document } = await documentLoader(keyId.href);
81
- keyDoc = document;
82
- } catch (_) {
83
- return null;
84
- }
85
- try {
86
- object = await Object$1.fromJsonLd(keyDoc, {
87
- documentLoader,
88
- contextLoader,
89
- tracerProvider
90
- });
91
- } catch (e) {
92
- if (!(e instanceof TypeError)) throw e;
93
- try {
94
- object = await CryptographicKey.fromJsonLd(keyDoc, {
95
- documentLoader,
96
- contextLoader,
97
- tracerProvider
98
- });
99
- } catch (e) {
100
- if (e instanceof TypeError) return null;
101
- throw e;
102
- }
103
- }
104
- }
105
- let owner = null;
106
- if (object instanceof CryptographicKey) {
107
- if (object.ownerId == null) return null;
108
- owner = await object.getOwner({
109
- documentLoader,
110
- contextLoader,
111
- tracerProvider
112
- });
113
- } else if (isActor(object)) owner = object;
114
- else return null;
115
- if (owner == null) return null;
116
- for (const kid of owner.publicKeyIds) if (kid.href === keyId.href) return owner;
117
- return null;
118
- }
119
- //#endregion
120
- export { getKeyOwner as n, doesActorOwnKey as t };