@fedify/fedify 1.9.12 → 1.9.13-dev.2344

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 (101) hide show
  1. package/dist/{actor-DbMPM7MA.cjs → actor-B2bgsOT5.cjs} +187 -187
  2. package/dist/{actor-Bapf7M7G.mjs → actor-DWfw2nxx.mjs} +1 -1
  3. package/dist/{actor-c1CsMwXN.js → actor-NkVQr224.js} +187 -187
  4. package/dist/{authdocloader-CtifebS7.cjs → authdocloader-BlkJJdEW.cjs} +3 -3
  5. package/dist/{authdocloader-DIKD7bwX.js → authdocloader-CiOaAMij.js} +3 -3
  6. package/dist/{authdocloader-CpMO32zq.mjs → authdocloader-DrnaVEJP.mjs} +3 -3
  7. package/dist/{builder-DC-GkZmh.mjs → builder-Cu8Iy5eZ.mjs} +4 -4
  8. package/dist/{client-CCadsk7N.mjs → client-Ck9Tn5iM.mjs} +63 -5
  9. package/dist/compat/mod.cjs +1 -1
  10. package/dist/compat/transformers.test.mjs +3 -3
  11. package/dist/{docloader-kZyrNwUH.js → docloader-B1OuNGno.js} +1 -1
  12. package/dist/{docloader-D4ctxKPG.cjs → docloader-CF7V0mC7.cjs} +1 -1
  13. package/dist/{esm-BxUjO5zX.mjs → esm-i8KtYbr9.mjs} +1 -1
  14. package/dist/federation/builder.test.mjs +3 -3
  15. package/dist/federation/collection.test.mjs +1 -1
  16. package/dist/federation/handler.test.mjs +7 -7
  17. package/dist/federation/idempotency.test.mjs +5 -5
  18. package/dist/federation/inbox.test.mjs +3 -3
  19. package/dist/federation/keycache.test.mjs +3 -3
  20. package/dist/federation/kv.test.mjs +1 -1
  21. package/dist/federation/middleware.test.mjs +11 -11
  22. package/dist/federation/mod.cjs +2 -2
  23. package/dist/federation/mod.js +1 -1
  24. package/dist/federation/mq.test.mjs +1 -1
  25. package/dist/federation/retry.test.mjs +1 -1
  26. package/dist/federation/router.test.mjs +2 -2
  27. package/dist/federation/send.test.mjs +7 -7
  28. package/dist/federation/temporal.test.mjs +2 -2
  29. package/dist/{http-DHDDqxdt.cjs → http-BEbcy3ax.cjs} +3 -3
  30. package/dist/{http-XbFxcG2g.mjs → http-CQ-6-sWA.mjs} +2 -2
  31. package/dist/{http-DM-MJijX.js → http-HjCIfzJC.js} +3 -3
  32. package/dist/{inbox-Uj6B6OKU.mjs → inbox-k8JQUnr6.mjs} +1 -1
  33. package/dist/{key-CLQ-3DyR.mjs → key-1GoRlbN4.mjs} +1 -1
  34. package/dist/{key-CPiCP_jy.cjs → key-CGuO8qJR.cjs} +1 -1
  35. package/dist/{key-DRbBpHOk.cjs → key-D-Oj5Zp2.cjs} +2 -2
  36. package/dist/{key-BiqmS6JT.mjs → key-DVX4QzuU.mjs} +2 -2
  37. package/dist/{key-Bgwa1_Vg.js → key-G8BkRalB.js} +2 -2
  38. package/dist/{keycache-FgycNH45.mjs → keycache-LnrJEGam.mjs} +1 -1
  39. package/dist/{keys-C9GAGpRb.mjs → keys-0Ff8761e.mjs} +1 -1
  40. package/dist/{ld-CrKje9FG.mjs → ld-Bftin5ZV.mjs} +2 -2
  41. package/dist/{lookup-C6mc3UXC.cjs → lookup-BSzQV-_k.cjs} +1 -1
  42. package/dist/{lookup-DkapKN3J.js → lookup-Ch5cERe_.js} +1 -1
  43. package/dist/{lookup-C4htfQ1_.mjs → lookup-HZr8nYiW.mjs} +1 -1
  44. package/dist/{middleware-ClNY6MUr.cjs → middleware-B58IZiSj.cjs} +1 -1
  45. package/dist/{middleware-CiiWngJ-.mjs → middleware-CDNI_5lz.mjs} +15 -15
  46. package/dist/{middleware--MqHhywy.mjs → middleware-DAMqqhFd.mjs} +1 -1
  47. package/dist/{middleware-Bs8fC5Th.js → middleware-DzaUMfHQ.js} +8 -8
  48. package/dist/{middleware-DjmGmYQz.cjs → middleware-MxfcK4vs.cjs} +10 -10
  49. package/dist/mod.cjs +10 -10
  50. package/dist/mod.js +9 -9
  51. package/dist/nodeinfo/client.test.mjs +63 -3
  52. package/dist/nodeinfo/handler.test.mjs +2 -2
  53. package/dist/nodeinfo/mod.cjs +1 -1
  54. package/dist/nodeinfo/mod.js +1 -1
  55. package/dist/nodeinfo/semver.test.mjs +1 -1
  56. package/dist/nodeinfo/types.test.mjs +1 -1
  57. package/dist/{owner-BxrTJWec.mjs → owner-DTdIjtoj.mjs} +3 -3
  58. package/dist/{proof-C-3TCOgT.cjs → proof-BeaoVMmb.cjs} +3 -3
  59. package/dist/{proof-CDFv0GLm.mjs → proof-DHg_8Rip.mjs} +2 -2
  60. package/dist/{proof-BCTTE8zz.js → proof-Dhn-fh6B.js} +3 -3
  61. package/dist/runtime/authdocloader.test.mjs +6 -6
  62. package/dist/runtime/docloader.test.mjs +3 -3
  63. package/dist/runtime/key.test.mjs +3 -3
  64. package/dist/runtime/langstr.test.mjs +2 -2
  65. package/dist/runtime/link.test.mjs +2 -2
  66. package/dist/runtime/mod.cjs +3 -3
  67. package/dist/runtime/mod.js +3 -3
  68. package/dist/runtime/multibase/multibase.test.mjs +2 -2
  69. package/dist/runtime/url.test.mjs +2 -2
  70. package/dist/{send-D8YQJCKA.mjs → send-5v8rpjgq.mjs} +2 -2
  71. package/dist/sig/http.test.mjs +6 -6
  72. package/dist/sig/key.test.mjs +4 -4
  73. package/dist/sig/ld.test.mjs +5 -5
  74. package/dist/sig/mod.cjs +3 -3
  75. package/dist/sig/mod.js +3 -3
  76. package/dist/sig/owner.test.mjs +5 -5
  77. package/dist/sig/proof.test.mjs +4 -4
  78. package/dist/{temporal-VtumSYrv.mjs → temporal-CJPJeb9T.mjs} +1 -1
  79. package/dist/testing/docloader.test.mjs +1 -1
  80. package/dist/testing/mod.mjs +1 -1
  81. package/dist/{testing-CZLFMsfn.mjs → testing-04hA-7q1.mjs} +2 -2
  82. package/dist/{transformers-CReap-SS.cjs → transformers-BQBtOzZ9.cjs} +1 -1
  83. package/dist/{types-uQdGoORv.cjs → types-Bgnn6Jz6.cjs} +63 -5
  84. package/dist/{types-CNhOmYHh.js → types-Cjuud6i7.js} +63 -5
  85. package/dist/vocab/actor.test.mjs +4 -4
  86. package/dist/vocab/cjs.test.mjs +1 -1
  87. package/dist/vocab/lookup.test.mjs +4 -4
  88. package/dist/vocab/mod.cjs +3 -3
  89. package/dist/vocab/mod.js +3 -3
  90. package/dist/vocab/type.test.mjs +2 -2
  91. package/dist/vocab/vocab.test.mjs +3 -3
  92. package/dist/{vocab-C071xkhv.mjs → vocab-DlRBSs8-.mjs} +186 -186
  93. package/dist/webfinger/handler.test.mjs +3 -3
  94. package/dist/webfinger/lookup.test.mjs +3 -3
  95. package/dist/webfinger/mod.cjs +1 -1
  96. package/dist/webfinger/mod.js +1 -1
  97. package/dist/x/cfworkers.cjs +1 -1
  98. package/dist/x/cfworkers.test.mjs +1 -1
  99. package/dist/x/hono.cjs +1 -1
  100. package/dist/x/sveltekit.cjs +1 -1
  101. package/package.json +1 -1
@@ -1,15 +1,15 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { s as CryptographicKey } from "../vocab-C071xkhv.mjs";
5
- import { a as mockDocumentLoader, t as test } from "../testing-CZLFMsfn.mjs";
4
+ import { s as CryptographicKey } from "../vocab-DlRBSs8-.mjs";
5
+ import { a as mockDocumentLoader, t as test } from "../testing-04hA-7q1.mjs";
6
6
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
7
7
  import { n as assertFalse, t as assertRejects } from "../assert_rejects-CJC9ThS-.mjs";
8
8
  import { t as assertThrows } from "../assert_throws-BIL7gChy.mjs";
9
9
  import { t as assert } from "../assert-DikXweDx.mjs";
10
- import { r as generateCryptoKeyPair } from "../key-BiqmS6JT.mjs";
11
- 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-CrKje9FG.mjs";
12
- import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-C9GAGpRb.mjs";
10
+ import { r as generateCryptoKeyPair } from "../key-DVX4QzuU.mjs";
11
+ 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-Bftin5ZV.mjs";
12
+ import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-0Ff8761e.mjs";
13
13
  import { encodeBase64 } from "byte-encodings/base64";
14
14
  //#region src/sig/ld.test.ts
15
15
  test("isInvalidUrlTypeError()", () => {
package/dist/sig/mod.cjs CHANGED
@@ -1,9 +1,9 @@
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_key = require("../key-DRbBpHOk.cjs");
5
- const require_http = require("../http-DHDDqxdt.cjs");
6
- const require_proof = require("../proof-C-3TCOgT.cjs");
4
+ const require_key = require("../key-D-Oj5Zp2.cjs");
5
+ const require_http = require("../http-BEbcy3ax.cjs");
6
+ const require_proof = require("../proof-BeaoVMmb.cjs");
7
7
  exports.attachSignature = require_proof.attachSignature;
8
8
  exports.createProof = require_proof.createProof;
9
9
  exports.createSignature = require_proof.createSignature;
package/dist/sig/mod.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
- import { i as importJwk, n as fetchKey, r as generateCryptoKeyPair, t as exportJwk } from "../key-Bgwa1_Vg.js";
4
- import { n as signRequest, r as verifyRequest } from "../http-DM-MJijX.js";
5
- 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-BCTTE8zz.js";
3
+ import { i as importJwk, n as fetchKey, r as generateCryptoKeyPair, t as exportJwk } from "../key-G8BkRalB.js";
4
+ import { n as signRequest, r as verifyRequest } from "../http-HjCIfzJC.js";
5
+ 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-Dhn-fh6B.js";
6
6
  export { attachSignature, createProof, createSignature, detachSignature, doesActorOwnKey, exportJwk, fetchKey, generateCryptoKeyPair, getKeyOwner, importJwk, signJsonLd, signObject, signRequest, verifyJsonLd, verifyObject, verifyProof, verifyRequest, verifySignature };
@@ -1,15 +1,15 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { o as Create } from "../vocab-C071xkhv.mjs";
5
- import { t as lookupObject } from "../lookup-C4htfQ1_.mjs";
6
- import { a as mockDocumentLoader, t as test } from "../testing-CZLFMsfn.mjs";
4
+ import { o as Create } from "../vocab-DlRBSs8-.mjs";
5
+ import { t as lookupObject } from "../lookup-HZr8nYiW.mjs";
6
+ import { a as mockDocumentLoader, t as test } from "../testing-04hA-7q1.mjs";
7
7
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
8
8
  import "../std__assert-2v7gYiZp.mjs";
9
9
  import { n as assertFalse } from "../assert_rejects-CJC9ThS-.mjs";
10
10
  import { t as assert } from "../assert-DikXweDx.mjs";
11
- import { n as getKeyOwner, t as doesActorOwnKey } from "../owner-BxrTJWec.mjs";
12
- import { o as rsaPublicKey1, s as rsaPublicKey2 } from "../keys-C9GAGpRb.mjs";
11
+ import { n as getKeyOwner, t as doesActorOwnKey } from "../owner-DTdIjtoj.mjs";
12
+ import { o as rsaPublicKey1, s as rsaPublicKey2 } from "../keys-0Ff8761e.mjs";
13
13
  //#region src/sig/owner.test.ts
14
14
  test("doesActorOwnKey()", async () => {
15
15
  const options = {
@@ -1,14 +1,14 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { L as decode, N as importMultibaseKey, _ as Note, c as DataIntegrityProof, g as Multikey, o as Create, w as Place } from "../vocab-C071xkhv.mjs";
5
- import { a as mockDocumentLoader, t as test } from "../testing-CZLFMsfn.mjs";
4
+ import { L as decode, N as importMultibaseKey, _ as Note, c as DataIntegrityProof, g as Multikey, o as Create, w as Place } from "../vocab-DlRBSs8-.mjs";
5
+ import { a as mockDocumentLoader, t as test } from "../testing-04hA-7q1.mjs";
6
6
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
7
7
  import "../std__assert-2v7gYiZp.mjs";
8
8
  import { t as assertRejects } from "../assert_rejects-CJC9ThS-.mjs";
9
9
  import { t as assertInstanceOf } from "../assert_instance_of-C4Ri6VuN.mjs";
10
- import { i as verifyProof, n as signObject, r as verifyObject, t as createProof } from "../proof-CDFv0GLm.mjs";
11
- import { i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-C9GAGpRb.mjs";
10
+ import { i as verifyProof, n as signObject, r as verifyObject, t as createProof } from "../proof-DHg_8Rip.mjs";
11
+ import { i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-0Ff8761e.mjs";
12
12
  import { decodeHex, encodeHex } from "byte-encodings/hex";
13
13
  //#region src/sig/proof.test.ts
14
14
  const fep8b32TestVectorPrivateKey = await crypto.subtle.importKey("jwk", {
@@ -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-CrKje9FG.mjs";
4
+ import { c as getNormalizationContextLoader } from "./ld-Bftin5ZV.mjs";
5
5
  import jsonld from "jsonld";
6
6
  //#region src/federation/temporal.ts
7
7
  function isPlainObject(value) {
@@ -1,7 +1,7 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { a as mockDocumentLoader, t as test } from "../testing-CZLFMsfn.mjs";
4
+ import { a as mockDocumentLoader, t as test } from "../testing-04hA-7q1.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
6
6
  import "../std__assert-2v7gYiZp.mjs";
7
7
  //#region src/testing/docloader.test.ts
@@ -1,5 +1,5 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { i as createRequestContext, n as testDefinitions, r as createInboxContext, t as test } from "../testing-CZLFMsfn.mjs";
4
+ import { i as createRequestContext, n as testDefinitions, r as createInboxContext, t as test } from "../testing-04hA-7q1.mjs";
5
5
  export { createInboxContext, createRequestContext, test, testDefinitions };
@@ -1,8 +1,8 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { W as lookupWebFinger, ut as __require } from "./vocab-C071xkhv.mjs";
5
- import { i as RouterError, n as traverseCollection, t as lookupObject } from "./lookup-C4htfQ1_.mjs";
4
+ import { W as lookupWebFinger, ut as __require } from "./vocab-DlRBSs8-.mjs";
5
+ import { i as RouterError, n as traverseCollection, t as lookupObject } from "./lookup-HZr8nYiW.mjs";
6
6
  import { configure, getConsoleSink, getLogger, reset } from "@logtape/logtape";
7
7
  import { trace } from "@opentelemetry/api";
8
8
  //#region src/testing/docloader.ts
@@ -1,6 +1,6 @@
1
1
  const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
- require("./docloader-D4ctxKPG.cjs");
3
+ require("./docloader-CF7V0mC7.cjs");
4
4
  //#region src/compat/transformers.ts
5
5
  const logger = (0, require("@logtape/logtape").getLogger)([
6
6
  "fedify",
@@ -1,6 +1,6 @@
1
1
  const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
- const require_docloader = require("./docloader-D4ctxKPG.cjs");
3
+ const require_docloader = require("./docloader-CF7V0mC7.cjs");
4
4
  let _logtape_logtape = require("@logtape/logtape");
5
5
  //#region src/nodeinfo/semver.ts
6
6
  const MAX_LENGTH = 256;
@@ -138,15 +138,72 @@ const logger = (0, _logtape_logtape.getLogger)([
138
138
  "nodeinfo",
139
139
  "client"
140
140
  ]);
141
+ const DEFAULT_MAX_REDIRECTION = 5;
142
+ /**
143
+ * Fetches a URL while guarding against SSRF. Before every request—including
144
+ * each redirect hop—the destination is validated with
145
+ * {@link validatePublicUrl}, so that loopback, link-local, and private
146
+ * addresses, as well as non-HTTP(S) schemes such as `data:`, are rejected.
147
+ * Redirects are followed manually with a cap, so that a public URL cannot
148
+ * bounce the request to an internal address.
149
+ * @param url The URL to fetch.
150
+ * @param headers The request headers.
151
+ * @returns The final response, or `null` if the URL is disallowed or there
152
+ * are too many redirects.
153
+ */
154
+ async function fetchPublicUrl(url, headers) {
155
+ let current = url instanceof URL ? url : new URL(url);
156
+ let redirected = 0;
157
+ while (true) {
158
+ try {
159
+ await require_docloader.validatePublicUrl(current.href);
160
+ } catch (e) {
161
+ if (e instanceof require_docloader.UrlError) {
162
+ logger.error("Refused to fetch a private or invalid URL {url}: {error}", {
163
+ url: current.href,
164
+ error: e
165
+ });
166
+ return null;
167
+ }
168
+ throw e;
169
+ }
170
+ const response = await fetch(current, {
171
+ headers,
172
+ redirect: "manual"
173
+ });
174
+ if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
175
+ redirected++;
176
+ if (redirected >= DEFAULT_MAX_REDIRECTION) {
177
+ logger.error("Too many redirections ({redirections}) while fetching {url}.", {
178
+ redirections: redirected,
179
+ url: current.href
180
+ });
181
+ return null;
182
+ }
183
+ const next = new URL(response.headers.get("Location"), response.url == null || response.url === "" ? current : response.url);
184
+ if (next.protocol !== current.protocol) {
185
+ logger.error("Refused a redirect to a different protocol ({protocol} to {redirectedProtocol}) while fetching NodeInfo.", {
186
+ protocol: current.protocol,
187
+ redirectedProtocol: next.protocol
188
+ });
189
+ return null;
190
+ }
191
+ current = next;
192
+ continue;
193
+ }
194
+ return response;
195
+ }
196
+ }
141
197
  async function getNodeInfo(url, options = {}) {
142
198
  try {
143
199
  let nodeInfoUrl = url;
144
200
  if (!options.direct) {
145
201
  const wellKnownUrl = new URL("/.well-known/nodeinfo", url);
146
- const wellKnownResponse = await fetch(wellKnownUrl, { headers: {
202
+ const wellKnownResponse = await fetchPublicUrl(wellKnownUrl, {
147
203
  Accept: "application/json",
148
204
  "User-Agent": typeof options.userAgent === "string" ? options.userAgent : require_docloader.getUserAgent(options.userAgent)
149
- } });
205
+ });
206
+ if (wellKnownResponse == null) return void 0;
150
207
  if (!wellKnownResponse.ok) {
151
208
  logger.error("Failed to fetch {url}: {status} {statusText}", {
152
209
  url: wellKnownUrl.href,
@@ -166,10 +223,11 @@ async function getNodeInfo(url, options = {}) {
166
223
  }
167
224
  nodeInfoUrl = link.href;
168
225
  }
169
- const response = await fetch(nodeInfoUrl, { headers: {
226
+ const response = await fetchPublicUrl(nodeInfoUrl, {
170
227
  Accept: "application/json",
171
228
  "User-Agent": typeof options.userAgent === "string" ? options.userAgent : require_docloader.getUserAgent(options.userAgent)
172
- } });
229
+ });
230
+ if (response == null) return void 0;
173
231
  if (!response.ok) {
174
232
  logger.error("Failed to fetch NodeInfo document from {url}: {status} {statusText}", {
175
233
  url: nodeInfoUrl.toString(),
@@ -1,6 +1,6 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
- import { o as getUserAgent } from "./docloader-kZyrNwUH.js";
3
+ import { l as UrlError, o as getUserAgent, u as validatePublicUrl } from "./docloader-B1OuNGno.js";
4
4
  import { getLogger } from "@logtape/logtape";
5
5
  //#region src/nodeinfo/semver.ts
6
6
  const MAX_LENGTH = 256;
@@ -138,15 +138,72 @@ const logger = getLogger([
138
138
  "nodeinfo",
139
139
  "client"
140
140
  ]);
141
+ const DEFAULT_MAX_REDIRECTION = 5;
142
+ /**
143
+ * Fetches a URL while guarding against SSRF. Before every request—including
144
+ * each redirect hop—the destination is validated with
145
+ * {@link validatePublicUrl}, so that loopback, link-local, and private
146
+ * addresses, as well as non-HTTP(S) schemes such as `data:`, are rejected.
147
+ * Redirects are followed manually with a cap, so that a public URL cannot
148
+ * bounce the request to an internal address.
149
+ * @param url The URL to fetch.
150
+ * @param headers The request headers.
151
+ * @returns The final response, or `null` if the URL is disallowed or there
152
+ * are too many redirects.
153
+ */
154
+ async function fetchPublicUrl(url, headers) {
155
+ let current = url instanceof URL ? url : new URL(url);
156
+ let redirected = 0;
157
+ while (true) {
158
+ try {
159
+ await validatePublicUrl(current.href);
160
+ } catch (e) {
161
+ if (e instanceof UrlError) {
162
+ logger.error("Refused to fetch a private or invalid URL {url}: {error}", {
163
+ url: current.href,
164
+ error: e
165
+ });
166
+ return null;
167
+ }
168
+ throw e;
169
+ }
170
+ const response = await fetch(current, {
171
+ headers,
172
+ redirect: "manual"
173
+ });
174
+ if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
175
+ redirected++;
176
+ if (redirected >= DEFAULT_MAX_REDIRECTION) {
177
+ logger.error("Too many redirections ({redirections}) while fetching {url}.", {
178
+ redirections: redirected,
179
+ url: current.href
180
+ });
181
+ return null;
182
+ }
183
+ const next = new URL(response.headers.get("Location"), response.url == null || response.url === "" ? current : response.url);
184
+ if (next.protocol !== current.protocol) {
185
+ logger.error("Refused a redirect to a different protocol ({protocol} to {redirectedProtocol}) while fetching NodeInfo.", {
186
+ protocol: current.protocol,
187
+ redirectedProtocol: next.protocol
188
+ });
189
+ return null;
190
+ }
191
+ current = next;
192
+ continue;
193
+ }
194
+ return response;
195
+ }
196
+ }
141
197
  async function getNodeInfo(url, options = {}) {
142
198
  try {
143
199
  let nodeInfoUrl = url;
144
200
  if (!options.direct) {
145
201
  const wellKnownUrl = new URL("/.well-known/nodeinfo", url);
146
- const wellKnownResponse = await fetch(wellKnownUrl, { headers: {
202
+ const wellKnownResponse = await fetchPublicUrl(wellKnownUrl, {
147
203
  Accept: "application/json",
148
204
  "User-Agent": typeof options.userAgent === "string" ? options.userAgent : getUserAgent(options.userAgent)
149
- } });
205
+ });
206
+ if (wellKnownResponse == null) return void 0;
150
207
  if (!wellKnownResponse.ok) {
151
208
  logger.error("Failed to fetch {url}: {status} {statusText}", {
152
209
  url: wellKnownUrl.href,
@@ -166,10 +223,11 @@ async function getNodeInfo(url, options = {}) {
166
223
  }
167
224
  nodeInfoUrl = link.href;
168
225
  }
169
- const response = await fetch(nodeInfoUrl, { headers: {
226
+ const response = await fetchPublicUrl(nodeInfoUrl, {
170
227
  Accept: "application/json",
171
228
  "User-Agent": typeof options.userAgent === "string" ? options.userAgent : getUserAgent(options.userAgent)
172
- } });
229
+ });
230
+ if (response == null) return void 0;
173
231
  if (!response.ok) {
174
232
  logger.error("Failed to fetch NodeInfo document from {url}: {status} {statusText}", {
175
233
  url: nodeInfoUrl.toString(),
@@ -1,15 +1,15 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { C as Person, E as Service, S as Organization, d as Group, lt as __exportAll, r as Application } from "../vocab-C071xkhv.mjs";
5
- import { t as test } from "../testing-CZLFMsfn.mjs";
4
+ import { C as Person, E as Service, S as Organization, d as Group, lt as __exportAll, r as Application } from "../vocab-DlRBSs8-.mjs";
5
+ import { t as test } from "../testing-04hA-7q1.mjs";
6
6
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
7
7
  import { n as assertStrictEquals } from "../std__assert-2v7gYiZp.mjs";
8
8
  import { n as assertFalse, t as assertRejects } from "../assert_rejects-CJC9ThS-.mjs";
9
9
  import { t as assertThrows } from "../assert_throws-BIL7gChy.mjs";
10
10
  import { t as assert$1 } from "../assert-DikXweDx.mjs";
11
- import { a as normalizeActorHandle, i as isActor, n as getActorHandle, r as getActorTypeName, t as getActorClassByTypeName } from "../actor-Bapf7M7G.mjs";
12
- import { t as esm_default } from "../esm-BxUjO5zX.mjs";
11
+ import { a as normalizeActorHandle, i as isActor, n as getActorHandle, r as getActorTypeName, t as getActorClassByTypeName } from "../actor-DWfw2nxx.mjs";
12
+ import { t as esm_default } from "../esm-i8KtYbr9.mjs";
13
13
  //#region ../../node_modules/.pnpm/fast-check@3.23.2/node_modules/fast-check/lib/esm/check/precondition/PreconditionFailure.js
14
14
  var PreconditionFailure = class PreconditionFailure extends Error {
15
15
  constructor(interruptExecution = false) {
@@ -1,7 +1,7 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { t as test } from "../testing-CZLFMsfn.mjs";
4
+ import { t as test } from "../testing-04hA-7q1.mjs";
5
5
  import { createRequire } from "node:module";
6
6
  import { strictEqual } from "node:assert";
7
7
  //#region src/vocab/cjs.test.ts
@@ -1,14 +1,14 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { C as Person, _ as Note, i as Collection, v as Object$1 } from "../vocab-C071xkhv.mjs";
5
- import { n as traverseCollection, t as lookupObject } from "../lookup-C4htfQ1_.mjs";
6
- import { a as mockDocumentLoader, t as test } from "../testing-CZLFMsfn.mjs";
4
+ import { C as Person, _ as Note, i as Collection, v as Object$1 } from "../vocab-DlRBSs8-.mjs";
5
+ import { n as traverseCollection, t as lookupObject } from "../lookup-HZr8nYiW.mjs";
6
+ import { a as mockDocumentLoader, t as test } from "../testing-04hA-7q1.mjs";
7
7
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
8
8
  import "../std__assert-2v7gYiZp.mjs";
9
9
  import { t as assertRejects } from "../assert_rejects-CJC9ThS-.mjs";
10
10
  import { t as assertInstanceOf } from "../assert_instance_of-C4Ri6VuN.mjs";
11
- import { t as esm_default } from "../esm-BxUjO5zX.mjs";
11
+ import { t as esm_default } from "../esm-i8KtYbr9.mjs";
12
12
  import { deepStrictEqual } from "node:assert";
13
13
  //#region src/vocab/lookup.test.ts
14
14
  test("lookupObject()", {
@@ -1,9 +1,9 @@
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_docloader = require("../docloader-D4ctxKPG.cjs");
5
- const require_actor = require("../actor-DbMPM7MA.cjs");
6
- const require_lookup = require("../lookup-C6mc3UXC.cjs");
4
+ const require_docloader = require("../docloader-CF7V0mC7.cjs");
5
+ const require_actor = require("../actor-B2bgsOT5.cjs");
6
+ const require_lookup = require("../lookup-BSzQV-_k.cjs");
7
7
  let _logtape_logtape = require("@logtape/logtape");
8
8
  let _opentelemetry_api = require("@opentelemetry/api");
9
9
  let es_toolkit = require("es-toolkit");
package/dist/vocab/mod.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
- import { d as name, f as version, i as getDocumentLoader } from "../docloader-kZyrNwUH.js";
4
- import { $ as Person, A as Flag, B as Like, C as Dislike, D as Endpoints, E as EmojiReact, F as Image, G as Multikey, H as Listen, I as IntransitiveActivity, J as Offer, K as Note, L as Invite, M as Group, N as Hashtag, O as Event, P as Ignore, Q as Page, R as Join, S as DidService, T as Emoji, Tt as getTypeId, U as Mention, V as Link, W as Move, X as OrderedCollectionPage, Y as OrderedCollection, Z as Organization, _ as CollectionPage, _t as View, a as normalizeActorHandle, at as Reject, b as DataIntegrityProof, c as Add, ct as Service, d as Arrive, dt as TentativeReject, et as Place, f as Article, ft as Tombstone, g as Collection, gt as Video, h as ChatMessage, ht as Update, i as isActor, it as Read, j as Follow, k as Export, l as Announce, lt as Source, m as Block, mt as Undo, n as getActorHandle, nt as PropertyValue, o as Accept, ot as Relationship, p as Audio, pt as Travel, q as Object$1, r as getActorTypeName, rt as Question, s as Activity, st as Remove, t as getActorClassByTypeName, tt as Profile, u as Application, ut as TentativeAccept, v as Create, w as Document, x as Delete, y as CryptographicKey, z as Leave } from "../actor-c1CsMwXN.js";
5
- import { t as lookupWebFinger } from "../lookup-DkapKN3J.js";
3
+ import { d as name, f as version, i as getDocumentLoader } from "../docloader-B1OuNGno.js";
4
+ import { $ as Person, A as Flag, B as Like, C as Dislike, D as Endpoints, E as EmojiReact, F as Image, G as Multikey, H as Listen, I as IntransitiveActivity, J as Offer, K as Note, L as Invite, M as Group, N as Hashtag, O as Event, P as Ignore, Q as Page, R as Join, S as DidService, T as Emoji, Tt as getTypeId, U as Mention, V as Link, W as Move, X as OrderedCollectionPage, Y as OrderedCollection, Z as Organization, _ as CollectionPage, _t as View, a as normalizeActorHandle, at as Reject, b as DataIntegrityProof, c as Add, ct as Service, d as Arrive, dt as TentativeReject, et as Place, f as Article, ft as Tombstone, g as Collection, gt as Video, h as ChatMessage, ht as Update, i as isActor, it as Read, j as Follow, k as Export, l as Announce, lt as Source, m as Block, mt as Undo, n as getActorHandle, nt as PropertyValue, o as Accept, ot as Relationship, p as Audio, pt as Travel, q as Object$1, r as getActorTypeName, rt as Question, s as Activity, st as Remove, t as getActorClassByTypeName, tt as Profile, u as Application, ut as TentativeAccept, v as Create, w as Document, x as Delete, y as CryptographicKey, z as Leave } from "../actor-NkVQr224.js";
5
+ import { t as lookupWebFinger } from "../lookup-Ch5cERe_.js";
6
6
  import { getLogger } from "@logtape/logtape";
7
7
  import { SpanStatusCode, trace } from "@opentelemetry/api";
8
8
  import { delay } from "es-toolkit";
@@ -1,8 +1,8 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { C as Person, U as getTypeId } from "../vocab-C071xkhv.mjs";
5
- import { t as test } from "../testing-CZLFMsfn.mjs";
4
+ import { C as Person, U as getTypeId } from "../vocab-DlRBSs8-.mjs";
5
+ import { t as test } from "../testing-04hA-7q1.mjs";
6
6
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
7
7
  //#region src/vocab/type.test.ts
8
8
  test("getTypeId()", () => {
@@ -1,8 +1,8 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { A as LanguageString, C as Person, D as Source, L as decode, T as Question, _ as Note, f as Hashtag, h as Link, i as Collection, k as vocab_exports, n as Announce, o as Create, s as CryptographicKey, t as Activity, u as Follow, v as Object$1, w as Place, x as OrderedCollectionPage } from "../vocab-C071xkhv.mjs";
5
- import { a as mockDocumentLoader, t as test } from "../testing-CZLFMsfn.mjs";
4
+ import { A as LanguageString, C as Person, D as Source, L as decode, T as Question, _ as Note, f as Hashtag, h as Link, i as Collection, k as vocab_exports, n as Announce, o as Create, s as CryptographicKey, t as Activity, u as Follow, v as Object$1, w as Place, x as OrderedCollectionPage } from "../vocab-DlRBSs8-.mjs";
5
+ import { a as mockDocumentLoader, t as test } from "../testing-04hA-7q1.mjs";
6
6
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
7
7
  import "../std__assert-2v7gYiZp.mjs";
8
8
  import { n as assertFalse, t as assertRejects } from "../assert_rejects-CJC9ThS-.mjs";
@@ -10,7 +10,7 @@ import { t as assertInstanceOf } from "../assert_instance_of-C4Ri6VuN.mjs";
10
10
  import { t as assertNotEquals } from "../assert_not_equals--wG9hV7u.mjs";
11
11
  import { t as assertThrows } from "../assert_throws-BIL7gChy.mjs";
12
12
  import { t as assert } from "../assert-DikXweDx.mjs";
13
- import { o as rsaPublicKey1, r as ed25519PublicKey } from "../keys-C9GAGpRb.mjs";
13
+ import { o as rsaPublicKey1, r as ed25519PublicKey } from "../keys-0Ff8761e.mjs";
14
14
  import { pascalCase } from "es-toolkit";
15
15
  import { parseLanguageTag } from "@phensley/language-tag";
16
16
  import { Validator } from "@cfworker/json-schema";