@fedify/fedify 2.0.22-dev.1565 → 2.0.23-dev.1698

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 (53) hide show
  1. package/dist/{builder-DiCVIEdq.mjs → builder-DKjedYwv.mjs} +3 -3
  2. package/dist/{client-A1UrnX6I.mjs → client-HLLGHiTU.mjs} +63 -5
  3. package/dist/compat/transformers.test.mjs +1 -1
  4. package/dist/{deno-C0hao78M.mjs → deno-CG1anKuN.mjs} +1 -1
  5. package/dist/{docloader-CgohFTEx.mjs → docloader-BVcVx4gq.mjs} +2 -2
  6. package/dist/federation/builder.test.mjs +1 -1
  7. package/dist/federation/handler.test.mjs +4 -4
  8. package/dist/federation/idempotency.test.mjs +2 -2
  9. package/dist/federation/inbox.test.mjs +1 -1
  10. package/dist/federation/middleware.test.mjs +6 -6
  11. package/dist/federation/mod.cjs +1 -1
  12. package/dist/federation/mod.js +1 -1
  13. package/dist/federation/send.test.mjs +3 -3
  14. package/dist/federation/temporal.test.mjs +1 -1
  15. package/dist/federation/webfinger.test.mjs +1 -1
  16. package/dist/{http-CpvgjWiv.cjs → http-CvzGfoof.cjs} +1 -1
  17. package/dist/{http-DzEJOwGD.js → http-Dv8MHNrd.js} +1 -1
  18. package/dist/{http-fK66BkJj.mjs → http-ltv-ZF8s.mjs} +2 -2
  19. package/dist/{inbox-C91k13ar.mjs → inbox-DNUb90Xl.mjs} +1 -1
  20. package/dist/{key-CiI2c4fV.mjs → key-DuoihVCJ.mjs} +1 -1
  21. package/dist/{kv-cache-COhJ7YkU.js → kv-cache-Bxqm7aJ5.js} +1 -1
  22. package/dist/{kv-cache-DCQ3iAoA.cjs → kv-cache-oZ6X1avI.cjs} +1 -1
  23. package/dist/{ld-BtgF_U3o.mjs → ld-DNde5rVk.mjs} +2 -2
  24. package/dist/{middleware-BmLb_YyB.cjs → middleware-BgE6PmPc.cjs} +5 -5
  25. package/dist/{middleware-yNqmDL8w.mjs → middleware-BsfT9SAW.mjs} +12 -12
  26. package/dist/{middleware-CwajVapC.cjs → middleware-CNrBu2zf.cjs} +1 -1
  27. package/dist/{middleware-BGFHmA-f.js → middleware-CO7DuKTs.js} +4 -4
  28. package/dist/{middleware-Bg7Ua9YZ.mjs → middleware-Cr4KyIfv.mjs} +1 -1
  29. package/dist/mod.cjs +5 -5
  30. package/dist/mod.js +5 -5
  31. package/dist/nodeinfo/client.test.mjs +61 -1
  32. package/dist/nodeinfo/handler.test.mjs +1 -1
  33. package/dist/nodeinfo/mod.cjs +1 -1
  34. package/dist/nodeinfo/mod.js +1 -1
  35. package/dist/{owner-sWtcq_uW.mjs → owner-ZK8vkBHE.mjs} +2 -2
  36. package/dist/{proof-B9liQ9K1.mjs → proof-2jNUR7mr.mjs} +2 -2
  37. package/dist/{proof-p6KRdV2F.cjs → proof-BvhXJo8H.cjs} +1 -1
  38. package/dist/{proof-p2BryEGX.js → proof-nkNyzNWj.js} +1 -1
  39. package/dist/{send-B84OJGoX.mjs → send-CnE9KCxn.mjs} +2 -2
  40. package/dist/sig/http.test.mjs +2 -2
  41. package/dist/sig/key.test.mjs +1 -1
  42. package/dist/sig/ld.test.mjs +2 -2
  43. package/dist/sig/mod.cjs +2 -2
  44. package/dist/sig/mod.js +2 -2
  45. package/dist/sig/owner.test.mjs +1 -1
  46. package/dist/sig/proof.test.mjs +1 -1
  47. package/dist/{temporal-D_P8UNzl.mjs → temporal-YipGAiAA.mjs} +1 -1
  48. package/dist/{types-CAY3OdLq.js → types-DTONwNqL.js} +63 -5
  49. package/dist/{types-KC4QAoxe.cjs → types-m8u9L86X.cjs} +62 -4
  50. package/dist/utils/docloader.test.mjs +2 -2
  51. package/dist/utils/mod.cjs +1 -1
  52. package/dist/utils/mod.js +1 -1
  53. package/package.json +6 -6
@@ -2,8 +2,8 @@ import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { n as RouterError, t as Router } from "./router-CrMLXoOr.mjs";
5
- import { n as version, t as name } from "./deno-C0hao78M.mjs";
6
- import { t as InboxListenerSet } from "./inbox-C91k13ar.mjs";
5
+ import { n as version, t as name } from "./deno-CG1anKuN.mjs";
6
+ import { t as InboxListenerSet } from "./inbox-DNUb90Xl.mjs";
7
7
  import { getTypeId } from "@fedify/vocab";
8
8
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
9
9
  import { getLogger } from "@logtape/logtape";
@@ -42,7 +42,7 @@ var FederationBuilderImpl = class {
42
42
  this.collectionTypeIds = {};
43
43
  }
44
44
  async build(options) {
45
- const { FederationImpl } = await import("./middleware-Bg7Ua9YZ.mjs");
45
+ const { FederationImpl } = await import("./middleware-Cr4KyIfv.mjs");
46
46
  const f = new FederationImpl(options);
47
47
  const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
48
48
  f.router = this.router.clone();
@@ -1,7 +1,7 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { getUserAgent } from "@fedify/vocab-runtime";
4
+ import { UrlError, getUserAgent, validatePublicUrl } from "@fedify/vocab-runtime";
5
5
  import { getLogger } from "@logtape/logtape";
6
6
  //#region src/nodeinfo/client.ts
7
7
  const logger = getLogger([
@@ -9,15 +9,72 @@ const logger = getLogger([
9
9
  "nodeinfo",
10
10
  "client"
11
11
  ]);
12
+ const DEFAULT_MAX_REDIRECTION = 5;
13
+ /**
14
+ * Fetches a URL while guarding against SSRF. Before every request—including
15
+ * each redirect hop—the destination is validated with
16
+ * {@link validatePublicUrl}, so that loopback, link-local, and private
17
+ * addresses, as well as non-HTTP(S) schemes such as `data:`, are rejected.
18
+ * Redirects are followed manually with a cap, so that a public URL cannot
19
+ * bounce the request to an internal address.
20
+ * @param url The URL to fetch.
21
+ * @param headers The request headers.
22
+ * @returns The final response, or `null` if the URL is disallowed or there
23
+ * are too many redirects.
24
+ */
25
+ async function fetchPublicUrl(url, headers) {
26
+ let current = url instanceof URL ? url : new URL(url);
27
+ let redirected = 0;
28
+ while (true) {
29
+ try {
30
+ await validatePublicUrl(current.href);
31
+ } catch (e) {
32
+ if (e instanceof UrlError) {
33
+ logger.error("Refused to fetch a private or invalid URL {url}: {error}", {
34
+ url: current.href,
35
+ error: e
36
+ });
37
+ return null;
38
+ }
39
+ throw e;
40
+ }
41
+ const response = await fetch(current, {
42
+ headers,
43
+ redirect: "manual"
44
+ });
45
+ if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
46
+ redirected++;
47
+ if (redirected >= DEFAULT_MAX_REDIRECTION) {
48
+ logger.error("Too many redirections ({redirections}) while fetching {url}.", {
49
+ redirections: redirected,
50
+ url: current.href
51
+ });
52
+ return null;
53
+ }
54
+ const next = new URL(response.headers.get("Location"), response.url == null || response.url === "" ? current : response.url);
55
+ if (next.protocol !== current.protocol) {
56
+ logger.error("Refused a redirect to a different protocol ({protocol} to {redirectedProtocol}) while fetching NodeInfo.", {
57
+ protocol: current.protocol,
58
+ redirectedProtocol: next.protocol
59
+ });
60
+ return null;
61
+ }
62
+ current = next;
63
+ continue;
64
+ }
65
+ return response;
66
+ }
67
+ }
12
68
  async function getNodeInfo(url, options = {}) {
13
69
  try {
14
70
  let nodeInfoUrl = url;
15
71
  if (!options.direct) {
16
72
  const wellKnownUrl = new URL("/.well-known/nodeinfo", url);
17
- const wellKnownResponse = await fetch(wellKnownUrl, { headers: {
73
+ const wellKnownResponse = await fetchPublicUrl(wellKnownUrl, {
18
74
  Accept: "application/json",
19
75
  "User-Agent": typeof options.userAgent === "string" ? options.userAgent : getUserAgent(options.userAgent)
20
- } });
76
+ });
77
+ if (wellKnownResponse == null) return void 0;
21
78
  if (!wellKnownResponse.ok) {
22
79
  logger.error("Failed to fetch {url}: {status} {statusText}", {
23
80
  url: wellKnownUrl.href,
@@ -37,10 +94,11 @@ async function getNodeInfo(url, options = {}) {
37
94
  }
38
95
  nodeInfoUrl = link.href;
39
96
  }
40
- const response = await fetch(nodeInfoUrl, { headers: {
97
+ const response = await fetchPublicUrl(nodeInfoUrl, {
41
98
  Accept: "application/json",
42
99
  "User-Agent": typeof options.userAgent === "string" ? options.userAgent : getUserAgent(options.userAgent)
43
- } });
100
+ });
101
+ if (response == null) return void 0;
44
102
  if (!response.ok) {
45
103
  logger.error("Failed to fetch NodeInfo document from {url}: {status} {statusText}", {
46
104
  url: nodeInfoUrl.toString(),
@@ -5,7 +5,7 @@ import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import { t as assertInstanceOf } from "../assert_instance_of-C4Ri6VuN.mjs";
6
6
  import { t as assert } from "../assert-ddO5KLpe.mjs";
7
7
  import { t as MemoryKvStore } from "../kv-BrZHNugx.mjs";
8
- import { _ as autoIdAssigner, g as actorDehydrator, n as FederationImpl } from "../middleware-yNqmDL8w.mjs";
8
+ import { _ as autoIdAssigner, g as actorDehydrator, n as FederationImpl } from "../middleware-BsfT9SAW.mjs";
9
9
  import { test } from "@fedify/fixture";
10
10
  import { Follow, Person } from "@fedify/vocab";
11
11
  //#region src/compat/transformers.test.ts
@@ -3,6 +3,6 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  //#region deno.json
5
5
  var name = "@fedify/fedify";
6
- var version = "2.0.22-dev.1565+b5a902fa";
6
+ var version = "2.0.23-dev.1698+43673cbc";
7
7
  //#endregion
8
8
  export { version as n, name as t };
@@ -1,8 +1,8 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { a as validateCryptoKey } from "./key-CiI2c4fV.mjs";
5
- import { n as doubleKnock } from "./http-fK66BkJj.mjs";
4
+ import { a as validateCryptoKey } from "./key-DuoihVCJ.mjs";
5
+ import { n as doubleKnock } from "./http-ltv-ZF8s.mjs";
6
6
  import { curry } from "es-toolkit";
7
7
  import { UrlError, createActivityPubRequest, getRemoteDocument, logRequest, validatePublicUrl } from "@fedify/vocab-runtime";
8
8
  import { getLogger } from "@logtape/logtape";
@@ -5,7 +5,7 @@ import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import { a as assertExists } from "../std__assert-Duiq_YC9.mjs";
6
6
  import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
7
7
  import { t as MemoryKvStore } from "../kv-BrZHNugx.mjs";
8
- import { n as createFederationBuilder } from "../builder-DiCVIEdq.mjs";
8
+ import { n as createFederationBuilder } from "../builder-DKjedYwv.mjs";
9
9
  import { test } from "@fedify/fixture";
10
10
  import { Activity, Note, Person } from "@fedify/vocab";
11
11
  //#region src/federation/builder.test.ts
@@ -6,12 +6,12 @@ import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
6
6
  import "../std__assert-Duiq_YC9.mjs";
7
7
  import { t as assertRejects } from "../assert_rejects-B-qJtC9Z.mjs";
8
8
  import { t as assert } from "../assert-ddO5KLpe.mjs";
9
- import { s as signRequest } from "../http-fK66BkJj.mjs";
9
+ import { s as signRequest } from "../http-ltv-ZF8s.mjs";
10
10
  import { a as rsaPrivateKey3, c as rsaPublicKey3, s as rsaPublicKey2 } from "../keys-CtZLJq76.mjs";
11
- import { a as compactJsonLd, f as signJsonLd } from "../ld-BtgF_U3o.mjs";
11
+ import { a as compactJsonLd, f as signJsonLd } from "../ld-DNde5rVk.mjs";
12
12
  import { t as MemoryKvStore } from "../kv-BrZHNugx.mjs";
13
- import { a as createFederation, c as handleCollection, d as handleObject, f as respondWithObject, l as handleCustomCollection, p as respondWithObjectIfAcceptable, s as handleActor, u as handleInbox } from "../middleware-yNqmDL8w.mjs";
14
- import { t as InboxListenerSet } from "../inbox-C91k13ar.mjs";
13
+ import { a as createFederation, c as handleCollection, d as handleObject, f as respondWithObject, l as handleCustomCollection, p as respondWithObjectIfAcceptable, s as handleActor, u as handleInbox } from "../middleware-BsfT9SAW.mjs";
14
+ import { t as InboxListenerSet } from "../inbox-DNUb90Xl.mjs";
15
15
  import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
16
16
  import { Create, Note, Person } from "@fedify/vocab";
17
17
  //#region src/federation/handler.test.ts
@@ -4,9 +4,9 @@ globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import "../std__assert-Duiq_YC9.mjs";
6
6
  import { n as ed25519PrivateKey, r as ed25519PublicKey, t as ed25519Multikey } from "../keys-CtZLJq76.mjs";
7
- import { n as signObject } from "../proof-B9liQ9K1.mjs";
7
+ import { n as signObject } from "../proof-2jNUR7mr.mjs";
8
8
  import { t as MemoryKvStore } from "../kv-BrZHNugx.mjs";
9
- import { a as createFederation } from "../middleware-yNqmDL8w.mjs";
9
+ import { a as createFederation } from "../middleware-BsfT9SAW.mjs";
10
10
  import { mockDocumentLoader, test } from "@fedify/fixture";
11
11
  import { Create, Follow, Person } from "@fedify/vocab";
12
12
  //#region src/federation/idempotency.test.ts
@@ -3,7 +3,7 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
6
- import { t as InboxListenerSet } from "../inbox-C91k13ar.mjs";
6
+ import { t as InboxListenerSet } from "../inbox-DNUb90Xl.mjs";
7
7
  import { test } from "@fedify/fixture";
8
8
  import { Activity, Create, Invite, Offer, Update } from "@fedify/vocab";
9
9
  //#region src/federation/inbox.test.ts
@@ -10,14 +10,14 @@ import { t as assertInstanceOf } from "../assert_instance_of-C4Ri6VuN.mjs";
10
10
  import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
11
11
  import { t as assertNotEquals } from "../assert_not_equals--wG9hV7u.mjs";
12
12
  import { t as assert } from "../assert-ddO5KLpe.mjs";
13
- import { l as verifyRequest, s as signRequest } from "../http-fK66BkJj.mjs";
13
+ import { l as verifyRequest, s as signRequest } from "../http-ltv-ZF8s.mjs";
14
14
  import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-CtZLJq76.mjs";
15
- import { t as getAuthenticatedDocumentLoader } from "../docloader-CgohFTEx.mjs";
16
- import { a as compactJsonLd, f as signJsonLd, m as verifyJsonLd, s as detachSignature } from "../ld-BtgF_U3o.mjs";
17
- import { t as doesActorOwnKey } from "../owner-sWtcq_uW.mjs";
18
- import { n as signObject, r as verifyObject } from "../proof-B9liQ9K1.mjs";
15
+ import { t as getAuthenticatedDocumentLoader } from "../docloader-BVcVx4gq.mjs";
16
+ import { a as compactJsonLd, f as signJsonLd, m as verifyJsonLd, s as detachSignature } from "../ld-DNde5rVk.mjs";
17
+ import { t as doesActorOwnKey } from "../owner-ZK8vkBHE.mjs";
18
+ import { n as signObject, r as verifyObject } from "../proof-2jNUR7mr.mjs";
19
19
  import { t as MemoryKvStore } from "../kv-BrZHNugx.mjs";
20
- import { a as createFederation, i as KvSpecDeterminer, n as FederationImpl, r as InboxContextImpl, t as ContextImpl } from "../middleware-yNqmDL8w.mjs";
20
+ import { a as createFederation, i as KvSpecDeterminer, n as FederationImpl, r as InboxContextImpl, t as ContextImpl } from "../middleware-BsfT9SAW.mjs";
21
21
  import { mockDocumentLoader, test } from "@fedify/fixture";
22
22
  import * as vocab from "@fedify/vocab";
23
23
  import { Create, Offer, Person, getTypeId, lookupObject } from "@fedify/vocab";
@@ -2,7 +2,7 @@ const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
4
  require("../chunk-DDcVe30Y.cjs");
5
- const require_middleware = require("../middleware-BmLb_YyB.cjs");
5
+ const require_middleware = require("../middleware-BgE6PmPc.cjs");
6
6
  let es_toolkit = require("es-toolkit");
7
7
  //#region src/federation/kv.ts
8
8
  /**
@@ -1,6 +1,6 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { a as createExponentialBackoffPolicy, c as buildCollectionSynchronizationHeader, d as Router, f as RouterError, i as SendActivityError, l as digest, o as respondWithObject, r as handleWebFinger, s as respondWithObjectIfAcceptable, t as createFederation, u as createFederationBuilder } from "../middleware-BGFHmA-f.js";
3
+ import { a as createExponentialBackoffPolicy, c as buildCollectionSynchronizationHeader, d as Router, f as RouterError, i as SendActivityError, l as digest, o as respondWithObject, r as handleWebFinger, s as respondWithObjectIfAcceptable, t as createFederation, u as createFederationBuilder } from "../middleware-CO7DuKTs.js";
4
4
  import { isEqual } from "es-toolkit";
5
5
  //#region src/federation/kv.ts
6
6
  /**
@@ -8,10 +8,10 @@ import { n as assertFalse, t as assertRejects } from "../assert_rejects-B-qJtC9Z
8
8
  import { t as assertInstanceOf } from "../assert_instance_of-C4Ri6VuN.mjs";
9
9
  import { t as assertNotEquals } from "../assert_not_equals--wG9hV7u.mjs";
10
10
  import { t as assert } from "../assert-ddO5KLpe.mjs";
11
- import { l as verifyRequest } from "../http-fK66BkJj.mjs";
11
+ import { l as verifyRequest } from "../http-ltv-ZF8s.mjs";
12
12
  import { i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-CtZLJq76.mjs";
13
- import { t as doesActorOwnKey } from "../owner-sWtcq_uW.mjs";
14
- import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-B84OJGoX.mjs";
13
+ import { t as doesActorOwnKey } from "../owner-ZK8vkBHE.mjs";
14
+ import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-CnE9KCxn.mjs";
15
15
  import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
16
16
  import { Activity, Application, Endpoints, Group, Person, Service } from "@fedify/vocab";
17
17
  //#region src/federation/send.test.ts
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
4
4
  import "../std__assert-Duiq_YC9.mjs";
5
5
  import { n as assertFalse } from "../assert_rejects-B-qJtC9Z.mjs";
6
6
  import { t as assert } from "../assert-ddO5KLpe.mjs";
7
- import { t as hasMalformedKnownTemporalLiteral } from "../temporal-D_P8UNzl.mjs";
7
+ import { t as hasMalformedKnownTemporalLiteral } from "../temporal-YipGAiAA.mjs";
8
8
  import { test } from "@fedify/fixture";
9
9
  //#region src/federation/temporal.test.ts
10
10
  test("hasMalformedKnownTemporalLiteral() detects expanded proof timestamps", async () => {
@@ -5,7 +5,7 @@ import { n as createRequestContext } from "../context-Juj6bdHC.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
6
6
  import "../std__assert-Duiq_YC9.mjs";
7
7
  import { t as MemoryKvStore } from "../kv-BrZHNugx.mjs";
8
- import { a as createFederation, o as handleWebFinger } from "../middleware-yNqmDL8w.mjs";
8
+ import { a as createFederation, o as handleWebFinger } from "../middleware-BsfT9SAW.mjs";
9
9
  import { test } from "@fedify/fixture";
10
10
  import { Image, Link, Person } from "@fedify/vocab";
11
11
  //#region src/federation/webfinger.test.ts
@@ -11,7 +11,7 @@ let byte_encodings_base64 = require("byte-encodings/base64");
11
11
  let structured_field_values = require("structured-field-values");
12
12
  //#region deno.json
13
13
  var name = "@fedify/fedify";
14
- var version = "2.0.22-dev.1565+b5a902fa";
14
+ var version = "2.0.23-dev.1698+43673cbc";
15
15
  //#endregion
16
16
  //#region src/sig/key.ts
17
17
  /**
@@ -10,7 +10,7 @@ import { decodeBase64, encodeBase64 } from "byte-encodings/base64";
10
10
  import { Item, decodeDict, encodeItem } from "structured-field-values";
11
11
  //#region deno.json
12
12
  var name = "@fedify/fedify";
13
- var version = "2.0.22-dev.1565+b5a902fa";
13
+ var version = "2.0.23-dev.1698+43673cbc";
14
14
  //#endregion
15
15
  //#region src/sig/key.ts
16
16
  /**
@@ -1,8 +1,8 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-C0hao78M.mjs";
5
- import { a as validateCryptoKey, n as fetchKey } from "./key-CiI2c4fV.mjs";
4
+ import { n as version, t as name } from "./deno-CG1anKuN.mjs";
5
+ import { a as validateCryptoKey, n as fetchKey } from "./key-DuoihVCJ.mjs";
6
6
  import { CryptographicKey } from "@fedify/vocab";
7
7
  import { SpanStatusCode, trace } from "@opentelemetry/api";
8
8
  import { FetchError } from "@fedify/vocab-runtime";
@@ -1,7 +1,7 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-C0hao78M.mjs";
4
+ import { n as version, t as name } from "./deno-CG1anKuN.mjs";
5
5
  import { Activity, getTypeId } from "@fedify/vocab";
6
6
  import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
7
7
  import { getLogger } from "@logtape/logtape";
@@ -1,7 +1,7 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-C0hao78M.mjs";
4
+ import { n as version, t as name } from "./deno-CG1anKuN.mjs";
5
5
  import { CryptographicKey, Object as Object$1, isActor } from "@fedify/vocab";
6
6
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
7
7
  import { getDocumentLoader } from "@fedify/vocab-runtime";
@@ -1,6 +1,6 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { c as validateCryptoKey, t as doubleKnock } from "./http-DzEJOwGD.js";
3
+ import { c as validateCryptoKey, t as doubleKnock } from "./http-Dv8MHNrd.js";
4
4
  import { getLogger } from "@logtape/logtape";
5
5
  import { curry } from "es-toolkit";
6
6
  import { UrlError, createActivityPubRequest, getRemoteDocument, logRequest, preloadedContexts, validatePublicUrl } from "@fedify/vocab-runtime";
@@ -1,7 +1,7 @@
1
1
  const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
3
  require("./chunk-DDcVe30Y.cjs");
4
- const require_http = require("./http-CpvgjWiv.cjs");
4
+ const require_http = require("./http-CvzGfoof.cjs");
5
5
  let _logtape_logtape = require("@logtape/logtape");
6
6
  let es_toolkit = require("es-toolkit");
7
7
  let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
@@ -1,8 +1,8 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-C0hao78M.mjs";
5
- import { a as validateCryptoKey, n as fetchKey } from "./key-CiI2c4fV.mjs";
4
+ import { n as version, t as name } from "./deno-CG1anKuN.mjs";
5
+ import { a as validateCryptoKey, n as fetchKey } from "./key-DuoihVCJ.mjs";
6
6
  import { Activity, CryptographicKey, Object as Object$1, getTypeId } from "@fedify/vocab";
7
7
  import { SpanStatusCode, trace } from "@opentelemetry/api";
8
8
  import { getDocumentLoader } from "@fedify/vocab-runtime";
@@ -2,10 +2,10 @@ const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
3
  const require_chunk = require("./chunk-DDcVe30Y.cjs");
4
4
  const require_transformers = require("./transformers-NeAONrAq.cjs");
5
- const require_http = require("./http-CpvgjWiv.cjs");
6
- const require_proof = require("./proof-p6KRdV2F.cjs");
7
- const require_types = require("./types-KC4QAoxe.cjs");
8
- const require_kv_cache = require("./kv-cache-DCQ3iAoA.cjs");
5
+ const require_http = require("./http-CvzGfoof.cjs");
6
+ const require_proof = require("./proof-BvhXJo8H.cjs");
7
+ const require_types = require("./types-m8u9L86X.cjs");
8
+ const require_kv_cache = require("./kv-cache-oZ6X1avI.cjs");
9
9
  let _logtape_logtape = require("@logtape/logtape");
10
10
  let _fedify_vocab = require("@fedify/vocab");
11
11
  let _opentelemetry_api = require("@opentelemetry/api");
@@ -335,7 +335,7 @@ var FederationBuilderImpl = class {
335
335
  this.collectionTypeIds = {};
336
336
  }
337
337
  async build(options) {
338
- const { FederationImpl } = await Promise.resolve().then(() => require("./middleware-CwajVapC.cjs"));
338
+ const { FederationImpl } = await Promise.resolve().then(() => require("./middleware-CNrBu2zf.cjs"));
339
339
  const f = new FederationImpl(options);
340
340
  const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
341
341
  f.router = this.router.clone();
@@ -2,24 +2,24 @@ import { Temporal } from "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { n as RouterError } from "./router-CrMLXoOr.mjs";
5
- import { n as version, t as name } from "./deno-C0hao78M.mjs";
6
- import { a as validateCryptoKey, i as importJwk, t as exportJwk } from "./key-CiI2c4fV.mjs";
7
- import { l as verifyRequest } from "./http-fK66BkJj.mjs";
8
- import { t as getAuthenticatedDocumentLoader } from "./docloader-CgohFTEx.mjs";
5
+ import { n as version, t as name } from "./deno-CG1anKuN.mjs";
6
+ import { a as validateCryptoKey, i as importJwk, t as exportJwk } from "./key-DuoihVCJ.mjs";
7
+ import { l as verifyRequest } from "./http-ltv-ZF8s.mjs";
8
+ import { t as getAuthenticatedDocumentLoader } from "./docloader-BVcVx4gq.mjs";
9
9
  import { n as kvCache } from "./kv-cache-DqEKe5E0.mjs";
10
- import { a as compactJsonLd, c as getNormalizationContextLoader, d as isInvalidUrlTypeError, f as signJsonLd, g as wrapContextLoaderForJsonLd, l as hasSignature, p as verifyCompactJsonLd, r as assertSafeJsonLd, s as detachSignature, t as InvalidContextReferenceError, u as isClearlyMalformedContextReference } from "./ld-BtgF_U3o.mjs";
11
- import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-sWtcq_uW.mjs";
12
- import { n as signObject, r as verifyObject } from "./proof-B9liQ9K1.mjs";
13
- import { t as getNodeInfo } from "./client-A1UrnX6I.mjs";
10
+ import { a as compactJsonLd, c as getNormalizationContextLoader, d as isInvalidUrlTypeError, f as signJsonLd, g as wrapContextLoaderForJsonLd, l as hasSignature, p as verifyCompactJsonLd, r as assertSafeJsonLd, s as detachSignature, t as InvalidContextReferenceError, u as isClearlyMalformedContextReference } from "./ld-DNde5rVk.mjs";
11
+ import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-ZK8vkBHE.mjs";
12
+ import { n as signObject, r as verifyObject } from "./proof-2jNUR7mr.mjs";
13
+ import { t as getNodeInfo } from "./client-HLLGHiTU.mjs";
14
14
  import { t as nodeInfoToJson } from "./types-C37hquWI.mjs";
15
- import { n as routeActivity } from "./inbox-C91k13ar.mjs";
16
- import { t as FederationBuilderImpl } from "./builder-DiCVIEdq.mjs";
15
+ import { n as routeActivity } from "./inbox-DNUb90Xl.mjs";
16
+ import { t as FederationBuilderImpl } from "./builder-DKjedYwv.mjs";
17
17
  import { t as buildCollectionSynchronizationHeader } from "./collection-ChgDTHLz.mjs";
18
18
  import { t as KvKeyCache } from "./keycache-DaQ3ndaJ.mjs";
19
19
  import { t as acceptsJsonLd } from "./negotiation-BehA2uul.mjs";
20
- import { t as hasMalformedKnownTemporalLiteral } from "./temporal-D_P8UNzl.mjs";
20
+ import { t as hasMalformedKnownTemporalLiteral } from "./temporal-YipGAiAA.mjs";
21
21
  import { t as createExponentialBackoffPolicy } from "./retry-B_E3V_Dx.mjs";
22
- import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-B84OJGoX.mjs";
22
+ import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-CnE9KCxn.mjs";
23
23
  import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
24
24
  import { lookupWebFinger } from "@fedify/webfinger";
25
25
  import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
@@ -1,4 +1,4 @@
1
1
  const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
- const require_middleware = require("./middleware-BmLb_YyB.cjs");
3
+ const require_middleware = require("./middleware-BgE6PmPc.cjs");
4
4
  exports.FederationImpl = require_middleware.FederationImpl;
@@ -2,10 +2,10 @@ import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
3
  import { t as __exportAll } from "./chunk-CRNNMoPX.js";
4
4
  import { r as getDefaultActivityTransformers } from "./transformers-BGMIq1cs.js";
5
- import { c as validateCryptoKey, i as exportJwk, l as name, r as verifyRequest, s as importJwk, t as doubleKnock, u as version } from "./http-DzEJOwGD.js";
6
- import { _ as signJsonLd, a as doesActorOwnKey, c as assertSafeJsonLd, f as detachSignature, g as isInvalidUrlTypeError, h as isClearlyMalformedContextReference, m as hasSignature, n as signObject, o as getKeyOwner, p as getNormalizationContextLoader, r as verifyObject, s as InvalidContextReferenceError, u as compactJsonLd, v as verifyCompactJsonLd, x as wrapContextLoaderForJsonLd } from "./proof-p2BryEGX.js";
7
- import { n as getNodeInfo, t as nodeInfoToJson } from "./types-CAY3OdLq.js";
8
- import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-COhJ7YkU.js";
5
+ import { c as validateCryptoKey, i as exportJwk, l as name, r as verifyRequest, s as importJwk, t as doubleKnock, u as version } from "./http-Dv8MHNrd.js";
6
+ import { _ as signJsonLd, a as doesActorOwnKey, c as assertSafeJsonLd, f as detachSignature, g as isInvalidUrlTypeError, h as isClearlyMalformedContextReference, m as hasSignature, n as signObject, o as getKeyOwner, p as getNormalizationContextLoader, r as verifyObject, s as InvalidContextReferenceError, u as compactJsonLd, v as verifyCompactJsonLd, x as wrapContextLoaderForJsonLd } from "./proof-nkNyzNWj.js";
7
+ import { n as getNodeInfo, t as nodeInfoToJson } from "./types-DTONwNqL.js";
8
+ import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-Bxqm7aJ5.js";
9
9
  import { getLogger, withContext } from "@logtape/logtape";
10
10
  import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
11
11
  import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
@@ -1,5 +1,5 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as FederationImpl } from "./middleware-yNqmDL8w.mjs";
4
+ import { n as FederationImpl } from "./middleware-BsfT9SAW.mjs";
5
5
  export { FederationImpl };
package/dist/mod.cjs CHANGED
@@ -4,11 +4,11 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
4
  require("./chunk-DDcVe30Y.cjs");
5
5
  const require_transformers = require("./transformers-NeAONrAq.cjs");
6
6
  require("./compat/mod.cjs");
7
- const require_http = require("./http-CpvgjWiv.cjs");
8
- const require_middleware = require("./middleware-BmLb_YyB.cjs");
9
- const require_proof = require("./proof-p6KRdV2F.cjs");
10
- const require_types = require("./types-KC4QAoxe.cjs");
11
- const require_kv_cache = require("./kv-cache-DCQ3iAoA.cjs");
7
+ const require_http = require("./http-CvzGfoof.cjs");
8
+ const require_middleware = require("./middleware-BgE6PmPc.cjs");
9
+ const require_proof = require("./proof-BvhXJo8H.cjs");
10
+ const require_types = require("./types-m8u9L86X.cjs");
11
+ const require_kv_cache = require("./kv-cache-oZ6X1avI.cjs");
12
12
  const require_federation_mod = require("./federation/mod.cjs");
13
13
  require("./nodeinfo/mod.cjs");
14
14
  require("./runtime/mod.cjs");
package/dist/mod.js CHANGED
@@ -3,11 +3,11 @@ import { URLPattern } from "urlpattern-polyfill";
3
3
  import "./chunk-CRNNMoPX.js";
4
4
  import { n as autoIdAssigner, r as getDefaultActivityTransformers, t as actorDehydrator } from "./transformers-BGMIq1cs.js";
5
5
  import "./compat/mod.js";
6
- import { a as fetchKey, i as exportJwk, n as signRequest, o as generateCryptoKeyPair, r as verifyRequest, s as importJwk } from "./http-DzEJOwGD.js";
7
- import { a as createExponentialBackoffPolicy, c as buildCollectionSynchronizationHeader, d as Router, f as RouterError, i as SendActivityError, l as digest, o as respondWithObject, r as handleWebFinger, s as respondWithObjectIfAcceptable, t as createFederation, u as createFederationBuilder } from "./middleware-BGFHmA-f.js";
8
- import { _ as signJsonLd, a as doesActorOwnKey, b as verifySignature, d as createSignature, f as detachSignature, i as verifyProof, l as attachSignature, n as signObject, o as getKeyOwner, r as verifyObject, t as createProof, y as verifyJsonLd } from "./proof-p2BryEGX.js";
9
- import { n as getNodeInfo, r as parseNodeInfo, t as nodeInfoToJson } from "./types-CAY3OdLq.js";
10
- import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-COhJ7YkU.js";
6
+ import { a as fetchKey, i as exportJwk, n as signRequest, o as generateCryptoKeyPair, r as verifyRequest, s as importJwk } from "./http-Dv8MHNrd.js";
7
+ import { a as createExponentialBackoffPolicy, c as buildCollectionSynchronizationHeader, d as Router, f as RouterError, i as SendActivityError, l as digest, o as respondWithObject, r as handleWebFinger, s as respondWithObjectIfAcceptable, t as createFederation, u as createFederationBuilder } from "./middleware-CO7DuKTs.js";
8
+ import { _ as signJsonLd, a as doesActorOwnKey, b as verifySignature, d as createSignature, f as detachSignature, i as verifyProof, l as attachSignature, n as signObject, o as getKeyOwner, r as verifyObject, t as createProof, y as verifyJsonLd } from "./proof-nkNyzNWj.js";
9
+ import { n as getNodeInfo, r as parseNodeInfo, t as nodeInfoToJson } from "./types-DTONwNqL.js";
10
+ import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-Bxqm7aJ5.js";
11
11
  import { InProcessMessageQueue, MemoryKvStore, ParallelMessageQueue } from "./federation/mod.js";
12
12
  import "./nodeinfo/mod.js";
13
13
  import "./runtime/mod.js";
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
4
4
  import { t as esm_default } from "../esm-DVILvP5e.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
6
6
  import "../std__assert-Duiq_YC9.mjs";
7
- import { a as parseProtocol, c as parseUsage, i as parseOutboundService, n as parseInboundService, o as parseServices, r as parseNodeInfo, s as parseSoftware, t as getNodeInfo } from "../client-A1UrnX6I.mjs";
7
+ import { a as parseProtocol, c as parseUsage, i as parseOutboundService, n as parseInboundService, o as parseServices, r as parseNodeInfo, s as parseSoftware, t as getNodeInfo } from "../client-HLLGHiTU.mjs";
8
8
  import { test } from "@fedify/fixture";
9
9
  //#region src/nodeinfo/client.test.ts
10
10
  test("getNodeInfo()", async (t) => {
@@ -62,6 +62,66 @@ test("getNodeInfo()", async (t) => {
62
62
  });
63
63
  esm_default.hardReset();
64
64
  });
65
+ test("getNodeInfo() blocks SSRF", async (t) => {
66
+ esm_default.spyGlobal();
67
+ await t.step("link href pointing to a loopback address", async () => {
68
+ esm_default.removeRoutes();
69
+ esm_default.get("https://example.com/.well-known/nodeinfo", { body: { links: [{
70
+ rel: "http://nodeinfo.diaspora.software/ns/schema/2.1",
71
+ href: "http://127.0.0.1:8080/internal"
72
+ }] } });
73
+ let internalFetched = false;
74
+ esm_default.get("http://127.0.0.1:8080/internal", () => {
75
+ internalFetched = true;
76
+ return { body: { secret: "INTERNAL_DATA_LEAKED" } };
77
+ });
78
+ assertEquals(await getNodeInfo("https://example.com/", { parse: "none" }), void 0);
79
+ assertEquals(internalFetched, false);
80
+ });
81
+ await t.step("link href using a data: URL", async () => {
82
+ esm_default.removeRoutes();
83
+ esm_default.get("https://example.com/.well-known/nodeinfo", { body: { links: [{
84
+ rel: "http://nodeinfo.diaspora.software/ns/schema/2.0",
85
+ href: "data:application/json,{\"software\":{\"name\":\"x\"}}"
86
+ }] } });
87
+ assertEquals(await getNodeInfo("https://example.com/", { parse: "none" }), void 0);
88
+ });
89
+ await t.step("redirect to a private address", async () => {
90
+ esm_default.removeRoutes();
91
+ esm_default.get("https://example.com/.well-known/nodeinfo", { body: { links: [{
92
+ rel: "http://nodeinfo.diaspora.software/ns/schema/2.1",
93
+ href: "https://example.com/nodeinfo/2.1"
94
+ }] } });
95
+ esm_default.get("https://example.com/nodeinfo/2.1", {
96
+ status: 302,
97
+ headers: { Location: "https://127.0.0.1:8080/internal" }
98
+ });
99
+ let internalFetched = false;
100
+ esm_default.get("https://127.0.0.1:8080/internal", () => {
101
+ internalFetched = true;
102
+ return { body: { secret: "INTERNAL_DATA_LEAKED" } };
103
+ });
104
+ assertEquals(await getNodeInfo("https://example.com/", { parse: "none" }), void 0);
105
+ assertEquals(internalFetched, false);
106
+ });
107
+ await t.step("direct fetch of a private address", async () => {
108
+ esm_default.removeRoutes();
109
+ let internalFetched = false;
110
+ esm_default.get("http://127.0.0.1:8080/nodeinfo", () => {
111
+ internalFetched = true;
112
+ return { body: { software: {
113
+ name: "x",
114
+ version: "1.0.0"
115
+ } } };
116
+ });
117
+ assertEquals(await getNodeInfo("http://127.0.0.1:8080/nodeinfo", {
118
+ direct: true,
119
+ parse: "none"
120
+ }), void 0);
121
+ assertEquals(internalFetched, false);
122
+ });
123
+ esm_default.hardReset();
124
+ });
65
125
  test("parseNodeInfo()", () => {
66
126
  const input = {
67
127
  software: {
@@ -5,7 +5,7 @@ import { n as createRequestContext } from "../context-Juj6bdHC.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
6
6
  import "../std__assert-Duiq_YC9.mjs";
7
7
  import { t as MemoryKvStore } from "../kv-BrZHNugx.mjs";
8
- import { a as createFederation, h as handleNodeInfoJrd, m as handleNodeInfo } from "../middleware-yNqmDL8w.mjs";
8
+ import { a as createFederation, h as handleNodeInfoJrd, m as handleNodeInfo } from "../middleware-BsfT9SAW.mjs";
9
9
  import { test } from "@fedify/fixture";
10
10
  //#region src/nodeinfo/handler.test.ts
11
11
  test("handleNodeInfo()", async () => {
@@ -1,7 +1,7 @@
1
1
  const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
- const require_types = require("../types-KC4QAoxe.cjs");
4
+ const require_types = require("../types-m8u9L86X.cjs");
5
5
  exports.getNodeInfo = require_types.getNodeInfo;
6
6
  exports.nodeInfoToJson = require_types.nodeInfoToJson;
7
7
  exports.parseNodeInfo = require_types.parseNodeInfo;
@@ -1,4 +1,4 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { n as getNodeInfo, r as parseNodeInfo, t as nodeInfoToJson } from "../types-CAY3OdLq.js";
3
+ import { n as getNodeInfo, r as parseNodeInfo, t as nodeInfoToJson } from "../types-DTONwNqL.js";
4
4
  export { getNodeInfo, nodeInfoToJson, parseNodeInfo };
@@ -1,8 +1,8 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-C0hao78M.mjs";
5
- import "./key-CiI2c4fV.mjs";
4
+ import { n as version, t as name } from "./deno-CG1anKuN.mjs";
5
+ import "./key-DuoihVCJ.mjs";
6
6
  import { CryptographicKey, Object as Object$1, isActor } from "@fedify/vocab";
7
7
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
8
8
  import { getDocumentLoader } from "@fedify/vocab-runtime";
@@ -1,8 +1,8 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-C0hao78M.mjs";
5
- import { a as validateCryptoKey, n as fetchKey } from "./key-CiI2c4fV.mjs";
4
+ import { n as version, t as name } from "./deno-CG1anKuN.mjs";
5
+ import { a as validateCryptoKey, n as fetchKey } from "./key-DuoihVCJ.mjs";
6
6
  import { Activity, DataIntegrityProof, Multikey, getTypeId } from "@fedify/vocab";
7
7
  import { SpanStatusCode, trace } from "@opentelemetry/api";
8
8
  import { getLogger } from "@logtape/logtape";
@@ -1,7 +1,7 @@
1
1
  const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
3
  const require_chunk = require("./chunk-DDcVe30Y.cjs");
4
- const require_http = require("./http-CpvgjWiv.cjs");
4
+ const require_http = require("./http-CvzGfoof.cjs");
5
5
  let _logtape_logtape = require("@logtape/logtape");
6
6
  let _fedify_vocab = require("@fedify/vocab");
7
7
  let _opentelemetry_api = require("@opentelemetry/api");
@@ -1,6 +1,6 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { a as fetchKey, c as validateCryptoKey, l as name, u as version } from "./http-DzEJOwGD.js";
3
+ import { a as fetchKey, c as validateCryptoKey, l as name, u as version } from "./http-Dv8MHNrd.js";
4
4
  import { getLogger } from "@logtape/logtape";
5
5
  import { Activity, CryptographicKey, DataIntegrityProof, Multikey, Object as Object$1, getTypeId, isActor } from "@fedify/vocab";
6
6
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
@@ -1,8 +1,8 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-C0hao78M.mjs";
5
- import { n as doubleKnock } from "./http-fK66BkJj.mjs";
4
+ import { n as version, t as name } from "./deno-CG1anKuN.mjs";
5
+ import { n as doubleKnock } from "./http-ltv-ZF8s.mjs";
6
6
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
7
7
  import { getLogger } from "@logtape/logtape";
8
8
  //#region src/federation/send.ts
@@ -7,8 +7,8 @@ import { a as assertExists, t as assertStringIncludes } from "../std__assert-Dui
7
7
  import { n as assertFalse, t as assertRejects } from "../assert_rejects-B-qJtC9Z.mjs";
8
8
  import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
9
9
  import { t as assert } from "../assert-ddO5KLpe.mjs";
10
- import { t as exportJwk } from "../key-CiI2c4fV.mjs";
11
- import { a as parseRfc9421Signature, c as timingSafeEqual, i as formatRfc9421SignatureParameters, l as verifyRequest, n as doubleKnock, o as parseRfc9421SignatureInput, r as formatRfc9421Signature, s as signRequest, t as createRfc9421SignatureBase } from "../http-fK66BkJj.mjs";
10
+ import { t as exportJwk } from "../key-DuoihVCJ.mjs";
11
+ import { a as parseRfc9421Signature, c as timingSafeEqual, i as formatRfc9421SignatureParameters, l as verifyRequest, n as doubleKnock, o as parseRfc9421SignatureInput, r as formatRfc9421Signature, s as signRequest, t as createRfc9421SignatureBase } from "../http-ltv-ZF8s.mjs";
12
12
  import { i as rsaPrivateKey2, l as rsaPublicKey5, o as rsaPublicKey1, s as rsaPublicKey2 } from "../keys-CtZLJq76.mjs";
13
13
  import { mockDocumentLoader, test } from "@fedify/fixture";
14
14
  import { FetchError, exportSpki } from "@fedify/vocab-runtime";
@@ -5,7 +5,7 @@ import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import "../std__assert-Duiq_YC9.mjs";
6
6
  import { t as assertRejects } from "../assert_rejects-B-qJtC9Z.mjs";
7
7
  import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
8
- import { a as validateCryptoKey, i as importJwk, n as fetchKey, r as generateCryptoKeyPair, t as exportJwk } from "../key-CiI2c4fV.mjs";
8
+ import { a as validateCryptoKey, i as importJwk, n as fetchKey, r as generateCryptoKeyPair, t as exportJwk } from "../key-DuoihVCJ.mjs";
9
9
  import { c as rsaPublicKey3, i as rsaPrivateKey2, o as rsaPublicKey1, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-CtZLJq76.mjs";
10
10
  import { mockDocumentLoader, test } from "@fedify/fixture";
11
11
  import { CryptographicKey, Multikey } from "@fedify/vocab";
@@ -5,9 +5,9 @@ import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import { n as assertFalse, t as assertRejects } from "../assert_rejects-B-qJtC9Z.mjs";
6
6
  import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
7
7
  import { t as assert } from "../assert-ddO5KLpe.mjs";
8
- import { r as generateCryptoKeyPair } from "../key-CiI2c4fV.mjs";
8
+ import { r as generateCryptoKeyPair } from "../key-DuoihVCJ.mjs";
9
9
  import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-CtZLJq76.mjs";
10
- import { a as compactJsonLd, d as isInvalidUrlTypeError, f as signJsonLd, h as verifySignature, i as attachSignature, m as verifyJsonLd, n as UnsafeJsonLdError, o as createSignature, s as detachSignature } from "../ld-BtgF_U3o.mjs";
10
+ import { a as compactJsonLd, d as isInvalidUrlTypeError, f as signJsonLd, h as verifySignature, i as attachSignature, m as verifyJsonLd, n as UnsafeJsonLdError, o as createSignature, s as detachSignature } from "../ld-DNde5rVk.mjs";
11
11
  import { mockDocumentLoader, test } from "@fedify/fixture";
12
12
  import { CryptographicKey } from "@fedify/vocab";
13
13
  import { encodeBase64 } from "byte-encodings/base64";
package/dist/sig/mod.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
- const require_http = require("../http-CpvgjWiv.cjs");
5
- const require_proof = require("../proof-p6KRdV2F.cjs");
4
+ const require_http = require("../http-CvzGfoof.cjs");
5
+ const require_proof = require("../proof-BvhXJo8H.cjs");
6
6
  exports.attachSignature = require_proof.attachSignature;
7
7
  exports.createProof = require_proof.createProof;
8
8
  exports.createSignature = require_proof.createSignature;
package/dist/sig/mod.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { a as fetchKey, i as exportJwk, n as signRequest, o as generateCryptoKeyPair, r as verifyRequest, s as importJwk } from "../http-DzEJOwGD.js";
4
- import { _ as signJsonLd, a as doesActorOwnKey, b as verifySignature, d as createSignature, f as detachSignature, i as verifyProof, l as attachSignature, n as signObject, o as getKeyOwner, r as verifyObject, t as createProof, y as verifyJsonLd } from "../proof-p2BryEGX.js";
3
+ import { a as fetchKey, i as exportJwk, n as signRequest, o as generateCryptoKeyPair, r as verifyRequest, s as importJwk } from "../http-Dv8MHNrd.js";
4
+ import { _ as signJsonLd, a as doesActorOwnKey, b as verifySignature, d as createSignature, f as detachSignature, i as verifyProof, l as attachSignature, n as signObject, o as getKeyOwner, r as verifyObject, t as createProof, y as verifyJsonLd } from "../proof-nkNyzNWj.js";
5
5
  export { attachSignature, createProof, createSignature, detachSignature, doesActorOwnKey, exportJwk, fetchKey, generateCryptoKeyPair, getKeyOwner, importJwk, signJsonLd, signObject, signRequest, verifyJsonLd, verifyObject, verifyProof, verifyRequest, verifySignature };
@@ -6,7 +6,7 @@ import "../std__assert-Duiq_YC9.mjs";
6
6
  import { n as assertFalse } from "../assert_rejects-B-qJtC9Z.mjs";
7
7
  import { t as assert } from "../assert-ddO5KLpe.mjs";
8
8
  import { o as rsaPublicKey1, s as rsaPublicKey2 } from "../keys-CtZLJq76.mjs";
9
- import { n as getKeyOwner, t as doesActorOwnKey } from "../owner-sWtcq_uW.mjs";
9
+ import { n as getKeyOwner, t as doesActorOwnKey } from "../owner-ZK8vkBHE.mjs";
10
10
  import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
11
11
  import { Create, CryptographicKey, lookupObject } from "@fedify/vocab";
12
12
  //#region src/sig/owner.test.ts
@@ -6,7 +6,7 @@ import "../std__assert-Duiq_YC9.mjs";
6
6
  import { t as assertRejects } from "../assert_rejects-B-qJtC9Z.mjs";
7
7
  import { t as assertInstanceOf } from "../assert_instance_of-C4Ri6VuN.mjs";
8
8
  import { i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-CtZLJq76.mjs";
9
- import { i as verifyProof, n as signObject, r as verifyObject, t as createProof } from "../proof-B9liQ9K1.mjs";
9
+ import { i as verifyProof, n as signObject, r as verifyObject, t as createProof } from "../proof-2jNUR7mr.mjs";
10
10
  import { mockDocumentLoader, test } from "@fedify/fixture";
11
11
  import { Create, DataIntegrityProof, Multikey, Note, Place } from "@fedify/vocab";
12
12
  import { decodeMultibase, importMultibaseKey } from "@fedify/vocab-runtime";
@@ -1,7 +1,7 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { c as getNormalizationContextLoader } from "./ld-BtgF_U3o.mjs";
4
+ import { c as getNormalizationContextLoader } from "./ld-DNde5rVk.mjs";
5
5
  import jsonld from "@fedify/vocab-runtime/jsonld";
6
6
  //#region src/federation/temporal.ts
7
7
  function isPlainObject(value) {
@@ -1,22 +1,79 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
3
  import { getLogger } from "@logtape/logtape";
4
- import { getUserAgent } from "@fedify/vocab-runtime";
4
+ import { UrlError, getUserAgent, validatePublicUrl } from "@fedify/vocab-runtime";
5
5
  //#region src/nodeinfo/client.ts
6
6
  const logger = getLogger([
7
7
  "fedify",
8
8
  "nodeinfo",
9
9
  "client"
10
10
  ]);
11
+ const DEFAULT_MAX_REDIRECTION = 5;
12
+ /**
13
+ * Fetches a URL while guarding against SSRF. Before every request—including
14
+ * each redirect hop—the destination is validated with
15
+ * {@link validatePublicUrl}, so that loopback, link-local, and private
16
+ * addresses, as well as non-HTTP(S) schemes such as `data:`, are rejected.
17
+ * Redirects are followed manually with a cap, so that a public URL cannot
18
+ * bounce the request to an internal address.
19
+ * @param url The URL to fetch.
20
+ * @param headers The request headers.
21
+ * @returns The final response, or `null` if the URL is disallowed or there
22
+ * are too many redirects.
23
+ */
24
+ async function fetchPublicUrl(url, headers) {
25
+ let current = url instanceof URL ? url : new URL(url);
26
+ let redirected = 0;
27
+ while (true) {
28
+ try {
29
+ await validatePublicUrl(current.href);
30
+ } catch (e) {
31
+ if (e instanceof UrlError) {
32
+ logger.error("Refused to fetch a private or invalid URL {url}: {error}", {
33
+ url: current.href,
34
+ error: e
35
+ });
36
+ return null;
37
+ }
38
+ throw e;
39
+ }
40
+ const response = await fetch(current, {
41
+ headers,
42
+ redirect: "manual"
43
+ });
44
+ if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
45
+ redirected++;
46
+ if (redirected >= DEFAULT_MAX_REDIRECTION) {
47
+ logger.error("Too many redirections ({redirections}) while fetching {url}.", {
48
+ redirections: redirected,
49
+ url: current.href
50
+ });
51
+ return null;
52
+ }
53
+ const next = new URL(response.headers.get("Location"), response.url == null || response.url === "" ? current : response.url);
54
+ if (next.protocol !== current.protocol) {
55
+ logger.error("Refused a redirect to a different protocol ({protocol} to {redirectedProtocol}) while fetching NodeInfo.", {
56
+ protocol: current.protocol,
57
+ redirectedProtocol: next.protocol
58
+ });
59
+ return null;
60
+ }
61
+ current = next;
62
+ continue;
63
+ }
64
+ return response;
65
+ }
66
+ }
11
67
  async function getNodeInfo(url, options = {}) {
12
68
  try {
13
69
  let nodeInfoUrl = url;
14
70
  if (!options.direct) {
15
71
  const wellKnownUrl = new URL("/.well-known/nodeinfo", url);
16
- const wellKnownResponse = await fetch(wellKnownUrl, { headers: {
72
+ const wellKnownResponse = await fetchPublicUrl(wellKnownUrl, {
17
73
  Accept: "application/json",
18
74
  "User-Agent": typeof options.userAgent === "string" ? options.userAgent : getUserAgent(options.userAgent)
19
- } });
75
+ });
76
+ if (wellKnownResponse == null) return void 0;
20
77
  if (!wellKnownResponse.ok) {
21
78
  logger.error("Failed to fetch {url}: {status} {statusText}", {
22
79
  url: wellKnownUrl.href,
@@ -36,10 +93,11 @@ async function getNodeInfo(url, options = {}) {
36
93
  }
37
94
  nodeInfoUrl = link.href;
38
95
  }
39
- const response = await fetch(nodeInfoUrl, { headers: {
96
+ const response = await fetchPublicUrl(nodeInfoUrl, {
40
97
  Accept: "application/json",
41
98
  "User-Agent": typeof options.userAgent === "string" ? options.userAgent : getUserAgent(options.userAgent)
42
- } });
99
+ });
100
+ if (response == null) return void 0;
43
101
  if (!response.ok) {
44
102
  logger.error("Failed to fetch NodeInfo document from {url}: {status} {statusText}", {
45
103
  url: nodeInfoUrl.toString(),
@@ -9,15 +9,72 @@ const logger = (0, _logtape_logtape.getLogger)([
9
9
  "nodeinfo",
10
10
  "client"
11
11
  ]);
12
+ const DEFAULT_MAX_REDIRECTION = 5;
13
+ /**
14
+ * Fetches a URL while guarding against SSRF. Before every request—including
15
+ * each redirect hop—the destination is validated with
16
+ * {@link validatePublicUrl}, so that loopback, link-local, and private
17
+ * addresses, as well as non-HTTP(S) schemes such as `data:`, are rejected.
18
+ * Redirects are followed manually with a cap, so that a public URL cannot
19
+ * bounce the request to an internal address.
20
+ * @param url The URL to fetch.
21
+ * @param headers The request headers.
22
+ * @returns The final response, or `null` if the URL is disallowed or there
23
+ * are too many redirects.
24
+ */
25
+ async function fetchPublicUrl(url, headers) {
26
+ let current = url instanceof URL ? url : new URL(url);
27
+ let redirected = 0;
28
+ while (true) {
29
+ try {
30
+ await (0, _fedify_vocab_runtime.validatePublicUrl)(current.href);
31
+ } catch (e) {
32
+ if (e instanceof _fedify_vocab_runtime.UrlError) {
33
+ logger.error("Refused to fetch a private or invalid URL {url}: {error}", {
34
+ url: current.href,
35
+ error: e
36
+ });
37
+ return null;
38
+ }
39
+ throw e;
40
+ }
41
+ const response = await fetch(current, {
42
+ headers,
43
+ redirect: "manual"
44
+ });
45
+ if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
46
+ redirected++;
47
+ if (redirected >= DEFAULT_MAX_REDIRECTION) {
48
+ logger.error("Too many redirections ({redirections}) while fetching {url}.", {
49
+ redirections: redirected,
50
+ url: current.href
51
+ });
52
+ return null;
53
+ }
54
+ const next = new URL(response.headers.get("Location"), response.url == null || response.url === "" ? current : response.url);
55
+ if (next.protocol !== current.protocol) {
56
+ logger.error("Refused a redirect to a different protocol ({protocol} to {redirectedProtocol}) while fetching NodeInfo.", {
57
+ protocol: current.protocol,
58
+ redirectedProtocol: next.protocol
59
+ });
60
+ return null;
61
+ }
62
+ current = next;
63
+ continue;
64
+ }
65
+ return response;
66
+ }
67
+ }
12
68
  async function getNodeInfo(url, options = {}) {
13
69
  try {
14
70
  let nodeInfoUrl = url;
15
71
  if (!options.direct) {
16
72
  const wellKnownUrl = new URL("/.well-known/nodeinfo", url);
17
- const wellKnownResponse = await fetch(wellKnownUrl, { headers: {
73
+ const wellKnownResponse = await fetchPublicUrl(wellKnownUrl, {
18
74
  Accept: "application/json",
19
75
  "User-Agent": typeof options.userAgent === "string" ? options.userAgent : (0, _fedify_vocab_runtime.getUserAgent)(options.userAgent)
20
- } });
76
+ });
77
+ if (wellKnownResponse == null) return void 0;
21
78
  if (!wellKnownResponse.ok) {
22
79
  logger.error("Failed to fetch {url}: {status} {statusText}", {
23
80
  url: wellKnownUrl.href,
@@ -37,10 +94,11 @@ async function getNodeInfo(url, options = {}) {
37
94
  }
38
95
  nodeInfoUrl = link.href;
39
96
  }
40
- const response = await fetch(nodeInfoUrl, { headers: {
97
+ const response = await fetchPublicUrl(nodeInfoUrl, {
41
98
  Accept: "application/json",
42
99
  "User-Agent": typeof options.userAgent === "string" ? options.userAgent : (0, _fedify_vocab_runtime.getUserAgent)(options.userAgent)
43
- } });
100
+ });
101
+ if (response == null) return void 0;
44
102
  if (!response.ok) {
45
103
  logger.error("Failed to fetch NodeInfo document from {url}: {status} {statusText}", {
46
104
  url: nodeInfoUrl.toString(),
@@ -5,9 +5,9 @@ import { t as esm_default } from "../esm-DVILvP5e.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
6
6
  import "../std__assert-Duiq_YC9.mjs";
7
7
  import { t as assertRejects } from "../assert_rejects-B-qJtC9Z.mjs";
8
- import { l as verifyRequest } from "../http-fK66BkJj.mjs";
8
+ import { l as verifyRequest } from "../http-ltv-ZF8s.mjs";
9
9
  import { i as rsaPrivateKey2 } from "../keys-CtZLJq76.mjs";
10
- import { t as getAuthenticatedDocumentLoader } from "../docloader-CgohFTEx.mjs";
10
+ import { t as getAuthenticatedDocumentLoader } from "../docloader-BVcVx4gq.mjs";
11
11
  import { mockDocumentLoader, test } from "@fedify/fixture";
12
12
  import { UrlError } from "@fedify/vocab-runtime";
13
13
  //#region src/utils/docloader.test.ts
@@ -1,6 +1,6 @@
1
1
  const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
- const require_kv_cache = require("../kv-cache-DCQ3iAoA.cjs");
4
+ const require_kv_cache = require("../kv-cache-oZ6X1avI.cjs");
5
5
  exports.getAuthenticatedDocumentLoader = require_kv_cache.getAuthenticatedDocumentLoader;
6
6
  exports.kvCache = require_kv_cache.kvCache;
package/dist/utils/mod.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../kv-cache-COhJ7YkU.js";
3
+ import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../kv-cache-Bxqm7aJ5.js";
4
4
  export { getAuthenticatedDocumentLoader, kvCache };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/fedify",
3
- "version": "2.0.22-dev.1565+b5a902fa",
3
+ "version": "2.0.23-dev.1698+43673cbc",
4
4
  "description": "An ActivityPub server framework",
5
5
  "keywords": [
6
6
  "ActivityPub",
@@ -144,9 +144,9 @@
144
144
  "uri-template-router": "^1.0.0",
145
145
  "url-template": "^3.1.1",
146
146
  "urlpattern-polyfill": "^10.1.0",
147
- "@fedify/vocab": "2.0.22-dev.1565+b5a902fa",
148
- "@fedify/vocab-runtime": "2.0.22-dev.1565+b5a902fa",
149
- "@fedify/webfinger": "2.0.22-dev.1565+b5a902fa"
147
+ "@fedify/vocab": "2.0.23-dev.1698+43673cbc",
148
+ "@fedify/webfinger": "2.0.23-dev.1698+43673cbc",
149
+ "@fedify/vocab-runtime": "2.0.23-dev.1698+43673cbc"
150
150
  },
151
151
  "devDependencies": {
152
152
  "@std/assert": "npm:@jsr/std__assert@^0.226.0",
@@ -158,8 +158,8 @@
158
158
  "tsx": "^4.19.4",
159
159
  "typescript": "^6.0.0",
160
160
  "wrangler": "^4.17.0",
161
- "@fedify/fixture": "2.0.0",
162
- "@fedify/vocab-tools": "^2.0.22-dev.1565+b5a902fa"
161
+ "@fedify/vocab-tools": "^2.0.23-dev.1698+43673cbc",
162
+ "@fedify/fixture": "2.0.0"
163
163
  },
164
164
  "scripts": {
165
165
  "build:self": "tsdown",