@fedify/fedify 2.4.0-dev.1668 → 2.4.0-dev.1740
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{assert-CJ1KXcvv.mjs → assert-CebeUf0C.mjs} +1 -1
- package/dist/assert_exists-DtzkXC47.mjs +28 -0
- package/dist/assert_false-3pLLLsO4.mjs +23 -0
- package/dist/assert_greater_or_equal-BApOTh1h.mjs +30 -0
- package/dist/{assert_instance_of-Q9KTBnrf.mjs → assert_instance_of-g3WcyYNR.mjs} +1 -1
- package/dist/{assert_not_equals-EPJPoxuf.mjs → assert_not_equals-BN7yDMWe.mjs} +1 -1
- package/dist/assert_rejects-wFbSPn1O.mjs +39 -0
- package/dist/{assert_strict_equals-Ec20AUwa.mjs → assert_strict_equals-Bpjl4Psw.mjs} +1 -1
- package/dist/{assert_throws-NiFfqbnH.mjs → assert_throws-BJ5wuvR2.mjs} +1 -1
- package/dist/{builder-iXZ5jNVg.mjs → builder-Bw6LVKGw.mjs} +5 -3
- package/dist/{circuit-breaker-CBGyz5F7.mjs → circuit-breaker-BT7CiPbz.mjs} +191 -9
- package/dist/circuit-breaker-test-utils-BYIHoliq.mjs +14 -0
- package/dist/{client-CjF5IYQS.mjs → client-CtxvmazV.mjs} +63 -5
- package/dist/{collection-BIhlldqK.mjs → collection-CnnP1UAv.mjs} +1 -1
- package/dist/compat/mod.cjs +1 -1
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/mod.js +1 -1
- package/dist/compat/outgoing-jsonld.test.mjs +3 -3
- package/dist/compat/public-audience.test.mjs +3 -3
- package/dist/compat/transformers.test.mjs +5 -5
- package/dist/{context-C05Rgxmu.d.cts → context-B-dsBjZ9.d.cts} +281 -7
- package/dist/{context-Cjnr0ig1.d.ts → context-DrLXudbA.d.ts} +281 -7
- package/dist/{deno-DtHcfB0f.mjs → deno-tRdp8MCl.mjs} +1 -1
- package/dist/{docloader-0vAfF6XW.mjs → docloader-Dg_7gMC7.mjs} +2 -2
- package/dist/{esm-yYQfPhIY.mjs → esm-BHXXx4Wx.mjs} +13 -6
- package/dist/federation/builder.test.mjs +18 -5
- package/dist/federation/circuit-breaker.test.mjs +626 -7
- package/dist/federation/collection.test.mjs +3 -3
- package/dist/federation/handler.test.mjs +14 -13
- package/dist/federation/idempotency.test.mjs +6 -6
- package/dist/federation/inbox.test.mjs +3 -3
- package/dist/federation/keycache.test.mjs +5 -5
- package/dist/federation/kv.test.mjs +3 -3
- package/dist/federation/metrics.test.mjs +4 -4
- package/dist/federation/middleware.test.mjs +37 -28
- package/dist/federation/mod.cjs +18 -540
- package/dist/federation/mod.d.cts +3 -4
- package/dist/federation/mod.d.ts +3 -4
- package/dist/federation/mod.js +1 -1
- package/dist/federation/mq.test.mjs +32 -5
- package/dist/federation/negotiation.test.mjs +4 -4
- package/dist/federation/retry.test.mjs +3 -3
- package/dist/federation/router.test.mjs +7 -7
- package/dist/federation/send.test.mjs +28 -26
- package/dist/federation/tasks/codec.test.mjs +4 -4
- package/dist/federation/tasks/enqueue.test.mjs +4 -4
- package/dist/federation/tasks/tasks.test.mjs +6 -6
- package/dist/federation/temporal.test.mjs +4 -4
- package/dist/federation/webfinger.test.mjs +74 -12
- package/dist/{middleware-DzIUFRO2.cjs → federation-BJBpt_Am.cjs} +855 -114
- package/dist/{http-BbnOMQoy.js → http-BIxGa6AG.js} +3 -3
- package/dist/{http-DUmWq5Pw.mjs → http-Day1kcpT.mjs} +5 -5
- package/dist/{temporal-polyfill-BCdTHexd.cjs → http-DggG1scN.cjs} +2801 -104
- package/dist/{key-CM7wDnZG.mjs → key-igh0AMsL.mjs} +2 -2
- package/dist/{kv-cache-ZQ22hm44.js → kv-cache-BIAB0HKx.js} +1 -1
- package/dist/{kv-cache-DEXKemJ9.mjs → kv-cache-Bz6aQOOX.mjs} +1 -1
- package/dist/{kv-cache-zmgG1HUp.cjs → kv-cache-C4U_uBqD.cjs} +2 -4
- package/dist/{ld-BttH_-8K.mjs → ld-DnT9sCWa.mjs} +5 -5
- package/dist/metrics-DVKYY6K1.d.cts +36 -0
- package/dist/{metrics-DT-CnD1g.mjs → metrics-DbdNnWIt.mjs} +2 -2
- package/dist/metrics-DoRtI0qU.d.ts +36 -0
- package/dist/{middleware-ChlYS0Af.mjs → middleware-B6GweVcg.mjs} +1 -1
- package/dist/{middleware-D3IAVMcT.js → middleware-CnsPXG0O.js} +241 -40
- package/dist/{middleware-DrroX3aD.mjs → middleware-DPIS4fsB.mjs} +60 -43
- package/dist/{mod-BrpVsSfY.d.cts → mod-CLHkCwLm.d.cts} +3 -3
- package/dist/{mod-B0hW12_O.d.cts → mod-CnGLNoVw.d.cts} +86 -4
- package/dist/{mod-DdONo-ep.d.cts → mod-D-abvu2C.d.cts} +1 -1
- package/dist/{mod-BsCE5Ihq.d.ts → mod-D6O8C7eN.d.ts} +1 -1
- package/dist/{mod-CNef4in0.d.ts → mod-DypxPS9H.d.ts} +3 -3
- package/dist/{mod-COIAjwRS.d.ts → mod-DzRXACM5.d.ts} +86 -4
- package/dist/mod.cjs +25 -25
- package/dist/mod.d.cts +10 -11
- package/dist/mod.d.ts +10 -11
- package/dist/mod.js +8 -8
- package/dist/nodeinfo/client.test.mjs +64 -4
- package/dist/nodeinfo/handler.test.mjs +5 -5
- package/dist/nodeinfo/mod.cjs +1 -1
- package/dist/nodeinfo/mod.d.cts +1 -1
- package/dist/nodeinfo/mod.d.ts +1 -1
- package/dist/nodeinfo/mod.js +1 -1
- package/dist/nodeinfo/types.test.mjs +4 -4
- package/dist/otel/exporter.test.mjs +3 -3
- package/dist/otel/mod.cjs +0 -1
- package/dist/otel/mod.d.cts +1 -1
- package/dist/otel/mod.d.ts +1 -1
- package/dist/{outgoing-jsonld-URuqsICz.mjs → outgoing-jsonld-BQSHRLhg.mjs} +2 -2
- package/dist/{owner-DZALmhdl.mjs → owner-B3ef8GfJ.mjs} +2 -2
- package/dist/{proof-CqA-2nfW.js → proof-BBLQXRWw.js} +608 -36
- package/dist/{proof-a60oVJ_4.cjs → proof-Cqk2TcLX.cjs} +616 -39
- package/dist/proof-DVoDZ8d7.mjs +890 -0
- package/dist/{public-audience-Cr5MsnMq.mjs → public-audience--9hx7IGx.mjs} +1 -1
- package/dist/runtime/mod.cjs +1 -1
- package/dist/runtime/mod.js +1 -1
- package/dist/{send-DcwZnQWG.mjs → send-PbBSZ7Qk.mjs} +3 -3
- package/dist/sig/accept.test.mjs +1 -1
- package/dist/sig/http.test.mjs +37 -11
- package/dist/sig/key.test.mjs +6 -6
- package/dist/sig/ld.test.mjs +9 -7
- package/dist/sig/mod.cjs +3 -2
- package/dist/sig/mod.d.cts +4 -4
- package/dist/sig/mod.d.ts +4 -4
- package/dist/sig/mod.js +3 -3
- package/dist/sig/owner.test.mjs +6 -6
- package/dist/sig/proof.test.mjs +965 -15
- package/dist/std__assert-CxFT0MNt.mjs +8 -0
- package/dist/{temporal-Bpxpsl2k.mjs → temporal-DLQIjuTn.mjs} +3 -3
- package/dist/testing/mod.d.mts +10 -0
- package/dist/testing/mod.mjs +2 -2
- package/dist/{testing-CO7ftO8A.mjs → testing-BX0Nicyq.mjs} +2 -2
- package/dist/{transformers-DGwl-hD1.cjs → transformers-BalCrSsr.cjs} +0 -1
- package/dist/{types-DC1pNgJA.js → types-Dtyf8lVj.js} +63 -5
- package/dist/{types-CPym83zc.cjs → types-RXm5YxpC.cjs} +62 -5
- package/dist/utils/docloader.test.mjs +7 -7
- package/dist/utils/kv-cache.test.mjs +1 -1
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.d.cts +1 -1
- package/dist/utils/mod.d.ts +1 -1
- package/dist/utils/mod.js +1 -1
- package/package.json +7 -7
- package/skills/fedify/SKILL.md +462 -0
- package/dist/assert_rejects-EgstcQy5.mjs +0 -82
- package/dist/http-BpT5cE6r.cjs +0 -2694
- package/dist/metrics-DN13mCAw.d.cts +0 -294
- package/dist/metrics-v_e0n5GQ.d.ts +0 -294
- package/dist/proof-DiQ0uBal.mjs +0 -316
- package/dist/std__assert-B2NnY9bq.mjs +0 -79
- /package/dist/{accept-8PiolDP7.mjs → accept-CPVqki78.mjs} +0 -0
- /package/dist/{activity-listener-BtTSxZWA.mjs → activity-listener-C-BSbKI7.mjs} +0 -0
- /package/dist/{assert_equals-BV7oLMxm.mjs → assert_equals-CBlRfA-_.mjs} +0 -0
- /package/dist/{client-CAM_bQXx.d.cts → client-D-XP_Ccb.d.cts} +0 -0
- /package/dist/{client-CSddvgWN.d.ts → client-D-XP_Ccb.d.ts} +0 -0
- /package/dist/{codec-BBga-FWI.mjs → codec-peqOdBaE.mjs} +0 -0
- /package/dist/{context-BJXxPElm.mjs → context-Dt36KFEJ.mjs} +0 -0
- /package/dist/{http-VyDTd4G3.d.cts → http-6TI0tI03.d.cts} +0 -0
- /package/dist/{http-lf8Hsd91.d.ts → http-CdrSn2ip.d.ts} +0 -0
- /package/dist/{keycache-_DHqz5z_.mjs → keycache-0Jk78XmB.mjs} +0 -0
- /package/dist/{keys-Dbyd7bhl.mjs → keys-DIJNHW-Y.mjs} +0 -0
- /package/dist/{kv-D6hNiMTK.d.ts → kv-BhNbN8Ed.d.cts} +0 -0
- /package/dist/{kv-gJ8LYbxX.d.cts → kv-BhNbN8Ed.d.ts} +0 -0
- /package/dist/{kv-CXBAok3k.mjs → kv-DCHkaJ9-.mjs} +0 -0
- /package/dist/{mq-Bo7Etgh6.mjs → mq-Dm1Wmtm-.mjs} +0 -0
- /package/dist/{negotiation-RuwrPFqi.mjs → negotiation-CCocWT_N.mjs} +0 -0
- /package/dist/{owner-CnngXDNJ.d.ts → owner-DGSfntI-.d.ts} +0 -0
- /package/dist/{owner-DEvZuyOE.d.cts → owner-r_waTiQj.d.cts} +0 -0
- /package/dist/{retry-DgFqeS5k.mjs → retry-zX5xw5e0.mjs} +0 -0
- /package/dist/{chunk-D5IdezsZ.js → rolldown-runtime-BC-Eo5Mj.js} +0 -0
- /package/dist/{chunk-Czpeue4M.cjs → rolldown-runtime-Dmm3t92W.cjs} +0 -0
- /package/dist/{transformers-DXze6FUB.js → transformers-DAtXl_Kc.js} +0 -0
- /package/dist/{types-CrtdVuDP.mjs → types-DE_LV1JB.mjs} +0 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import "temporal-polyfill";
|
|
2
|
+
import "urlpattern-polyfill";
|
|
3
|
+
globalThis.addEventListener = () => {};
|
|
4
|
+
import "./assert_equals-CBlRfA-_.mjs";
|
|
5
|
+
import "./assert_throws-BJ5wuvR2.mjs";
|
|
6
|
+
import "./assert_rejects-wFbSPn1O.mjs";
|
|
7
|
+
import "./assert_strict_equals-Bpjl4Psw.mjs";
|
|
8
|
+
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { c as getNormalizationContextLoader } from "./ld-
|
|
4
|
+
import { c as getNormalizationContextLoader } from "./ld-DnT9sCWa.mjs";
|
|
5
5
|
import jsonld from "@fedify/vocab-runtime/jsonld";
|
|
6
6
|
//#region src/federation/temporal.ts
|
|
7
7
|
function isPlainObject(value) {
|
|
@@ -28,7 +28,7 @@ function isMalformedDurationLiteral(value) {
|
|
|
28
28
|
return true;
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
-
const TEMPORAL_DATE_TIME_IRIS = new Set([
|
|
31
|
+
const TEMPORAL_DATE_TIME_IRIS = /* @__PURE__ */ new Set([
|
|
32
32
|
"https://www.w3.org/ns/activitystreams#deleted",
|
|
33
33
|
"https://www.w3.org/ns/activitystreams#endTime",
|
|
34
34
|
"https://www.w3.org/ns/activitystreams#published",
|
|
@@ -37,7 +37,7 @@ const TEMPORAL_DATE_TIME_IRIS = new Set([
|
|
|
37
37
|
"http://purl.org/dc/terms/created",
|
|
38
38
|
"https://w3id.org/security#created"
|
|
39
39
|
]);
|
|
40
|
-
const TEMPORAL_DURATION_IRIS = new Set(["https://www.w3.org/ns/activitystreams#duration"]);
|
|
40
|
+
const TEMPORAL_DURATION_IRIS = /* @__PURE__ */ new Set(["https://www.w3.org/ns/activitystreams#duration"]);
|
|
41
41
|
const QUESTION_CLOSED_IRI = "https://www.w3.org/ns/activitystreams#closed";
|
|
42
42
|
const XSD_DATE_TIME_IRI = "http://www.w3.org/2001/XMLSchema#dateTime";
|
|
43
43
|
function hasMalformedExpandedDateTimeLiteral(value) {
|
package/dist/testing/mod.d.mts
CHANGED
|
@@ -782,6 +782,16 @@ type CircuitBreakerOptions = CircuitBreakerFailurePolicy & {
|
|
|
782
782
|
* @default `{ seconds: 1 }`
|
|
783
783
|
*/
|
|
784
784
|
readonly releaseInterval?: Temporal.Duration | Temporal.DurationLike;
|
|
785
|
+
/**
|
|
786
|
+
* How long Fedify keeps circuit breaker state in the configured key-value
|
|
787
|
+
* store.
|
|
788
|
+
*
|
|
789
|
+
* When omitted, Fedify derives this from `failureWindow`, `recoveryDelay`, and
|
|
790
|
+
* `heldActivityTtl` for the default numeric failure policy. Custom `failure`
|
|
791
|
+
* callbacks do not have an inspectable time window, so custom policies do not
|
|
792
|
+
* expire their stored state unless this option is provided.
|
|
793
|
+
*/
|
|
794
|
+
readonly stateTtl?: Temporal.Duration | Temporal.DurationLike;
|
|
785
795
|
/**
|
|
786
796
|
* Called whenever the circuit state changes.
|
|
787
797
|
*/
|
package/dist/testing/mod.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { a as testDefinitions, n as createOutboxContext, r as createRequestContext, t as createInboxContext } from "../context-
|
|
5
|
-
import { a as numberSchema, i as makeSchema, n as baseOptions, o as stringSchema, r as envelopeSchema, t as MockQueue } from "../testing-
|
|
4
|
+
import { a as testDefinitions, n as createOutboxContext, r as createRequestContext, t as createInboxContext } from "../context-Dt36KFEJ.mjs";
|
|
5
|
+
import { a as numberSchema, i as makeSchema, n as baseOptions, o as stringSchema, r as envelopeSchema, t as MockQueue } from "../testing-BX0Nicyq.mjs";
|
|
6
6
|
export { MockQueue, baseOptions, createInboxContext, createOutboxContext, createRequestContext, envelopeSchema, makeSchema, numberSchema, stringSchema, testDefinitions };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { i as mockDocumentLoader } from "./context-
|
|
5
|
-
import { t as MemoryKvStore } from "./kv-
|
|
4
|
+
import { i as mockDocumentLoader } from "./context-Dt36KFEJ.mjs";
|
|
5
|
+
import { t as MemoryKvStore } from "./kv-DCHkaJ9-.mjs";
|
|
6
6
|
import { Note } from "@fedify/vocab";
|
|
7
7
|
//#region src/testing/tasks.ts
|
|
8
8
|
/**
|
|
@@ -1,22 +1,79 @@
|
|
|
1
1
|
import { Temporal } from "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
|
|
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
|
|
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(),
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
2
|
-
require("./chunk-Czpeue4M.cjs");
|
|
3
2
|
let _logtape_logtape = require("@logtape/logtape");
|
|
4
3
|
let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
|
|
5
4
|
//#region src/nodeinfo/client.ts
|
|
@@ -8,15 +7,72 @@ const logger = (0, _logtape_logtape.getLogger)([
|
|
|
8
7
|
"nodeinfo",
|
|
9
8
|
"client"
|
|
10
9
|
]);
|
|
10
|
+
const DEFAULT_MAX_REDIRECTION = 5;
|
|
11
|
+
/**
|
|
12
|
+
* Fetches a URL while guarding against SSRF. Before every request—including
|
|
13
|
+
* each redirect hop—the destination is validated with
|
|
14
|
+
* {@link validatePublicUrl}, so that loopback, link-local, and private
|
|
15
|
+
* addresses, as well as non-HTTP(S) schemes such as `data:`, are rejected.
|
|
16
|
+
* Redirects are followed manually with a cap, so that a public URL cannot
|
|
17
|
+
* bounce the request to an internal address.
|
|
18
|
+
* @param url The URL to fetch.
|
|
19
|
+
* @param headers The request headers.
|
|
20
|
+
* @returns The final response, or `null` if the URL is disallowed or there
|
|
21
|
+
* are too many redirects.
|
|
22
|
+
*/
|
|
23
|
+
async function fetchPublicUrl(url, headers) {
|
|
24
|
+
let current = url instanceof URL ? url : new URL(url);
|
|
25
|
+
let redirected = 0;
|
|
26
|
+
while (true) {
|
|
27
|
+
try {
|
|
28
|
+
await (0, _fedify_vocab_runtime.validatePublicUrl)(current.href);
|
|
29
|
+
} catch (e) {
|
|
30
|
+
if (e instanceof _fedify_vocab_runtime.UrlError) {
|
|
31
|
+
logger.error("Refused to fetch a private or invalid URL {url}: {error}", {
|
|
32
|
+
url: current.href,
|
|
33
|
+
error: e
|
|
34
|
+
});
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
throw e;
|
|
38
|
+
}
|
|
39
|
+
const response = await fetch(current, {
|
|
40
|
+
headers,
|
|
41
|
+
redirect: "manual"
|
|
42
|
+
});
|
|
43
|
+
if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
|
|
44
|
+
redirected++;
|
|
45
|
+
if (redirected >= DEFAULT_MAX_REDIRECTION) {
|
|
46
|
+
logger.error("Too many redirections ({redirections}) while fetching {url}.", {
|
|
47
|
+
redirections: redirected,
|
|
48
|
+
url: current.href
|
|
49
|
+
});
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const next = new URL(response.headers.get("Location"), response.url == null || response.url === "" ? current : response.url);
|
|
53
|
+
if (next.protocol !== current.protocol) {
|
|
54
|
+
logger.error("Refused a redirect to a different protocol ({protocol} to {redirectedProtocol}) while fetching NodeInfo.", {
|
|
55
|
+
protocol: current.protocol,
|
|
56
|
+
redirectedProtocol: next.protocol
|
|
57
|
+
});
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
current = next;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
return response;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
11
66
|
async function getNodeInfo(url, options = {}) {
|
|
12
67
|
try {
|
|
13
68
|
let nodeInfoUrl = url;
|
|
14
69
|
if (!options.direct) {
|
|
15
70
|
const wellKnownUrl = new URL("/.well-known/nodeinfo", url);
|
|
16
|
-
const wellKnownResponse = await
|
|
71
|
+
const wellKnownResponse = await fetchPublicUrl(wellKnownUrl, {
|
|
17
72
|
Accept: "application/json",
|
|
18
73
|
"User-Agent": typeof options.userAgent === "string" ? options.userAgent : (0, _fedify_vocab_runtime.getUserAgent)(options.userAgent)
|
|
19
|
-
}
|
|
74
|
+
});
|
|
75
|
+
if (wellKnownResponse == null) return void 0;
|
|
20
76
|
if (!wellKnownResponse.ok) {
|
|
21
77
|
logger.error("Failed to fetch {url}: {status} {statusText}", {
|
|
22
78
|
url: wellKnownUrl.href,
|
|
@@ -36,10 +92,11 @@ async function getNodeInfo(url, options = {}) {
|
|
|
36
92
|
}
|
|
37
93
|
nodeInfoUrl = link.href;
|
|
38
94
|
}
|
|
39
|
-
const response = await
|
|
95
|
+
const response = await fetchPublicUrl(nodeInfoUrl, {
|
|
40
96
|
Accept: "application/json",
|
|
41
97
|
"User-Agent": typeof options.userAgent === "string" ? options.userAgent : (0, _fedify_vocab_runtime.getUserAgent)(options.userAgent)
|
|
42
|
-
}
|
|
98
|
+
});
|
|
99
|
+
if (response == null) return void 0;
|
|
43
100
|
if (!response.ok) {
|
|
44
101
|
logger.error("Failed to fetch NodeInfo document from {url}: {status} {statusText}", {
|
|
45
102
|
url: nodeInfoUrl.toString(),
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { t as esm_default } from "../esm-
|
|
5
|
-
import { t as assertEquals } from "../assert_equals-
|
|
6
|
-
import "../std__assert-
|
|
7
|
-
import { t as assertRejects } from "../assert_rejects-
|
|
8
|
-
import { l as verifyRequest } from "../http-
|
|
9
|
-
import { i as rsaPrivateKey2 } from "../keys-
|
|
10
|
-
import { t as getAuthenticatedDocumentLoader } from "../docloader-
|
|
4
|
+
import { t as esm_default } from "../esm-BHXXx4Wx.mjs";
|
|
5
|
+
import { t as assertEquals } from "../assert_equals-CBlRfA-_.mjs";
|
|
6
|
+
import "../std__assert-CxFT0MNt.mjs";
|
|
7
|
+
import { t as assertRejects } from "../assert_rejects-wFbSPn1O.mjs";
|
|
8
|
+
import { l as verifyRequest } from "../http-Day1kcpT.mjs";
|
|
9
|
+
import { i as rsaPrivateKey2 } from "../keys-DIJNHW-Y.mjs";
|
|
10
|
+
import { t as getAuthenticatedDocumentLoader } from "../docloader-Dg_7gMC7.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,7 +1,7 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as kvCache, t as MockKvStore } from "../kv-cache-
|
|
4
|
+
import { n as kvCache, t as MockKvStore } from "../kv-cache-Bz6aQOOX.mjs";
|
|
5
5
|
import { deepStrictEqual, throws } from "node:assert";
|
|
6
6
|
import { createTestMeterProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
7
7
|
import { preloadedContexts } from "@fedify/vocab-runtime";
|
package/dist/utils/mod.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const require_kv_cache = require("../kv-cache-
|
|
3
|
+
const require_kv_cache = require("../kv-cache-C4U_uBqD.cjs");
|
|
4
4
|
exports.getAuthenticatedDocumentLoader = require_kv_cache.getAuthenticatedDocumentLoader;
|
|
5
5
|
exports.kvCache = require_kv_cache.kvCache;
|
package/dist/utils/mod.d.cts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
|
-
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../mod-
|
|
2
|
+
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../mod-CLHkCwLm.cjs";
|
|
3
3
|
export { getAuthenticatedDocumentLoader, kvCache };
|
package/dist/utils/mod.d.ts
CHANGED
package/dist/utils/mod.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
|
-
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../kv-cache-
|
|
3
|
+
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../kv-cache-BIAB0HKx.js";
|
|
4
4
|
export { getAuthenticatedDocumentLoader, kvCache };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fedify/fedify",
|
|
3
|
-
"version": "2.4.0-dev.
|
|
3
|
+
"version": "2.4.0-dev.1740+32e9952c",
|
|
4
4
|
"description": "An ActivityPub server framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ActivityPub",
|
|
@@ -151,10 +151,10 @@
|
|
|
151
151
|
"structured-field-values": "^2.0.4",
|
|
152
152
|
"temporal-polyfill": "^1.0.1",
|
|
153
153
|
"urlpattern-polyfill": "^10.1.0",
|
|
154
|
-
"@fedify/uri-template": "2.4.0-dev.
|
|
155
|
-
"@fedify/vocab
|
|
156
|
-
"@fedify/
|
|
157
|
-
"@fedify/
|
|
154
|
+
"@fedify/uri-template": "2.4.0-dev.1740+32e9952c",
|
|
155
|
+
"@fedify/vocab": "2.4.0-dev.1740+32e9952c",
|
|
156
|
+
"@fedify/vocab-runtime": "2.4.0-dev.1740+32e9952c",
|
|
157
|
+
"@fedify/webfinger": "2.4.0-dev.1740+32e9952c"
|
|
158
158
|
},
|
|
159
159
|
"devDependencies": {
|
|
160
160
|
"@std/assert": "npm:@jsr/std__assert@^0.226.0",
|
|
@@ -166,8 +166,8 @@
|
|
|
166
166
|
"tsx": "^4.21.0",
|
|
167
167
|
"typescript": "^6.0.0",
|
|
168
168
|
"wrangler": "^4.17.0",
|
|
169
|
-
"@fedify/
|
|
170
|
-
"@fedify/
|
|
169
|
+
"@fedify/fixture": "2.0.0",
|
|
170
|
+
"@fedify/vocab-tools": "^2.4.0-dev.1740+32e9952c"
|
|
171
171
|
},
|
|
172
172
|
"scripts": {
|
|
173
173
|
"build:self": "tsdown",
|