@fedify/fedify 2.4.5-dev.2462 → 2.4.6-dev.2628
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 +3 -0
- package/dist/{builder-BBUGd9wr.mjs → builder-kj5boG2l.mjs} +2 -2
- package/dist/{client-D-XP_Ccb.d.cts → client-3InDZbba.d.cts} +0 -1
- package/dist/{client-D-XP_Ccb.d.ts → client-3InDZbba.d.ts} +0 -1
- package/dist/{client-CEnNz8O7.mjs → client-5SIFzW3q.mjs} +113 -64
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{compound-proof-CariICma.mjs → compound-proof-BoNAYSHt.mjs} +2 -2
- package/dist/{context-7NyiqIcg.d.ts → context-CVOOd9pD.d.ts} +28 -2
- package/dist/{context-tsYPJ2Ew.d.cts → context-D0gFIgev.d.cts} +28 -2
- package/dist/{deno-BO7-a3hz.mjs → deno-ksnVeCA7.mjs} +1 -1
- package/dist/docloader-BhVNi06k.mjs +136 -0
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/compound-fanout.test.mjs +6 -3
- package/dist/federation/handler-compound-proof.test.mjs +3 -3
- package/dist/federation/handler.test.mjs +4 -4
- package/dist/federation/hashlink-media.test.mjs +2 -2
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/inbox-report.test.mjs +5 -5
- package/dist/federation/inbox.test.mjs +1 -1
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +465 -32
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +3 -3
- package/dist/federation/mod.d.ts +3 -3
- package/dist/federation/mod.js +1 -1
- package/dist/federation/outgoing-proof.test.mjs +9 -6
- package/dist/federation/portable-actor.test.mjs +3 -3
- package/dist/federation/portable-chain.test.mjs +3 -3
- package/dist/federation/portable-collection.test.mjs +4 -4
- package/dist/federation/portable-delivery.test.mjs +2 -2
- package/dist/federation/portable-gateway-keys.test.mjs +11 -5
- package/dist/federation/portable-inbox.test.mjs +4 -4
- package/dist/federation/portable-parse-uri.test.mjs +1 -1
- package/dist/federation/portable-route-activity.test.mjs +2 -2
- package/dist/federation/portable.test.mjs +3 -3
- package/dist/federation/send.test.mjs +498 -88
- package/dist/federation/tasks/enqueue.test.mjs +1 -1
- package/dist/federation/tasks/tasks.test.mjs +2 -2
- package/dist/federation/temporal.test.mjs +1 -1
- package/dist/federation/webfinger.test.mjs +1 -1
- package/dist/{federation-Dd9IwOEi.cjs → federation-BtPz9sr3.cjs} +240 -102
- package/dist/{http-D7L2pqLT.mjs → http-BSARoDPh.mjs} +204 -106
- package/dist/{http-DLf8QYD6.cjs → http-CeVcKQMZ.cjs} +179 -110
- package/dist/{http-BVrOR6MG.js → http-rH230xCD.js} +178 -109
- package/dist/{inbox-DtEKOr3W.mjs → inbox-BdZenpZQ.mjs} +2 -2
- package/dist/{key-DYxuyd-s.d.cts → key-CyCcut3h.d.ts} +1 -2
- package/dist/{key-C1nv7_jD.d.ts → key-VEL1QqHF.d.cts} +1 -2
- package/dist/{kv-cache-Dkzuqs8B.cjs → kv-cache-BpGIONmd.cjs} +72 -27
- package/dist/{kv-cache-DoZoRS9p.js → kv-cache-CNfNI_95.js} +73 -28
- package/dist/{kv-cache-m3RIr811.mjs → kv-cache-Cxn7pNMx.mjs} +1 -1
- package/dist/{ld-PJLGA2MJ.mjs → ld-DJZrjNuN.mjs} +2 -2
- package/dist/{metrics-CGR9cI-Y.mjs → metrics-DtxsYIkN.mjs} +1 -1
- package/dist/{middleware-BxhBd7pw.js → middleware-DDvck2q9.js} +241 -103
- package/dist/{middleware-ByanhTvb.mjs → middleware-DblGj642.mjs} +82 -42
- package/dist/{middleware-DPI-Jp9w.mjs → middleware-DyEjt33n.mjs} +1 -1
- package/dist/{mod-DEZXwV6J.d.ts → mod-B1Qt5cQQ.d.ts} +2 -9
- package/dist/{mod-Xj5P4s4d.d.ts → mod-B5COx5Wd.d.ts} +1 -1
- package/dist/{mod-CavhAgLw.d.cts → mod-BTxJG7ZH.d.cts} +1 -1
- package/dist/{mod-CtpF2BU5.d.ts → mod-CpyjFA9s.d.ts} +1 -1
- package/dist/{mod-BojP2Bw3.d.cts → mod-makPMVMg.d.cts} +1 -1
- package/dist/{mod-Dw-nPxQh.d.cts → mod-ss0wBUH1.d.cts} +2 -9
- package/dist/mod.cjs +5 -5
- package/dist/mod.d.cts +6 -6
- package/dist/mod.d.ts +6 -6
- package/dist/mod.js +5 -5
- package/dist/nodeinfo/client.test.mjs +337 -19
- package/dist/nodeinfo/handler.test.mjs +1 -1
- 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/otel/exporter.test.mjs +292 -30
- package/dist/otel/mod.cjs +36 -30
- package/dist/otel/mod.d.cts +13 -0
- package/dist/otel/mod.d.ts +13 -0
- package/dist/otel/mod.js +36 -30
- package/dist/{owner-B5c276hH.mjs → owner-2vfqhUJw.mjs} +3 -3
- package/dist/{portable-collection-BMZY9qT-.js → portable-collection-B7AQ_dW7.js} +1 -1
- package/dist/{portable-collection-CR5uwjws.cjs → portable-collection-Cs--KD5A.cjs} +1 -1
- package/dist/{portable-collection-C88tBiy0.mjs → portable-collection-DKJwGO8S.mjs} +2 -2
- package/dist/{portable-key-DxTXUpCK.mjs → portable-key-DXGepvtD.mjs} +1 -1
- package/dist/{proof-DNu-qP0i.mjs → proof-DW0FK6Cp.mjs} +3 -3
- package/dist/{send-CdCifQs4.mjs → send-CqsocpvF.mjs} +174 -76
- package/dist/sig/compound-proof-discovery.test.mjs +1 -1
- package/dist/sig/compound-proof-producer.test.mjs +1 -1
- package/dist/sig/compound-proof-verification.test.mjs +2 -2
- package/dist/sig/compound-proof.test.mjs +2 -2
- package/dist/sig/http.test.mjs +553 -2
- package/dist/sig/key.test.mjs +1 -1
- package/dist/sig/ld.test.mjs +1 -1
- package/dist/sig/mitra-receive-interop.test.mjs +4 -4
- package/dist/sig/mitra-send-interop.test.mjs +3 -3
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.d.cts +2 -2
- package/dist/sig/mod.d.ts +2 -2
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/portable-collection.test.mjs +1 -1
- package/dist/sig/portable-interop.test.mjs +3 -3
- package/dist/sig/portable-key.test.mjs +5 -5
- package/dist/sig/proof.test.mjs +4 -4
- package/dist/sig/tootik-request-interop.test.mjs +4 -4
- package/dist/sig/tootik-send-interop.test.mjs +3 -3
- package/dist/sig/verification.test.mjs +3 -3
- package/dist/{temporal-BuMu2bgP.mjs → temporal-Dm2E1-vs.mjs} +1 -1
- package/dist/testing/kv.test.mjs +1 -1
- package/dist/testing/mod.d.mts +26 -0
- package/dist/{types-_S2_MeLF.js → types-DUjZb4i6.js} +112 -63
- package/dist/{types-poIao40f.cjs → types-hKCCjii0.cjs} +111 -62
- package/dist/utils/docloader.test.mjs +344 -36
- 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/dist/docloader-DQNRDYDb.mjs +0 -91
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
2
2
|
let _logtape_logtape = require("@logtape/logtape");
|
|
3
3
|
let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
|
|
4
|
+
let _fedify_vocab_runtime_internal = require("@fedify/vocab-runtime/internal");
|
|
4
5
|
//#region src/utils/body.ts
|
|
5
6
|
/** Decoded JSON body limit: 16 MiB. @internal */
|
|
6
7
|
const MAX_BODY_SIZE = 16 * 1024 * 1024;
|
|
@@ -83,23 +84,34 @@ const logger = (0, _logtape_logtape.getLogger)([
|
|
|
83
84
|
]);
|
|
84
85
|
const DEFAULT_MAX_REDIRECTION = 5;
|
|
85
86
|
/**
|
|
86
|
-
* Fetches a URL while guarding against SSRF.
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* bounce the request to an internal
|
|
87
|
+
* Fetches a URL while guarding against SSRF. Every request—including each
|
|
88
|
+
* redirect hop—is made in its own session opened with `openPublicFetch()`,
|
|
89
|
+
* which rejects loopback, link-local, and private addresses, as well as
|
|
90
|
+
* non-HTTP(S) schemes such as `data:`, and connects only to the validated
|
|
91
|
+
* addresses where the runtime supports it. Redirects are followed manually
|
|
92
|
+
* with a cap, so that a public URL cannot bounce the request to an internal
|
|
93
|
+
* address.
|
|
94
|
+
*
|
|
95
|
+
* The final response is passed to `consume`, which must consume or cancel its
|
|
96
|
+
* body; the session is closed after `consume` settles. The cancellation of
|
|
97
|
+
* redirect response bodies is started before their sessions are closed.
|
|
92
98
|
* @param url The URL to fetch.
|
|
93
99
|
* @param headers The request headers.
|
|
94
|
-
* @
|
|
95
|
-
*
|
|
100
|
+
* @param consume The function to consume the final response with.
|
|
101
|
+
* @param deps The dependencies.
|
|
102
|
+
* @returns The value returned by `consume`, or `null` if the URL is
|
|
103
|
+
* disallowed, its hostname cannot be resolved, or there are too many
|
|
104
|
+
* redirects.
|
|
105
|
+
* @internal
|
|
96
106
|
*/
|
|
97
|
-
async function fetchPublicUrl(url, headers) {
|
|
107
|
+
async function fetchPublicUrl(url, headers, consume, deps = {}) {
|
|
108
|
+
const openHop = deps.openHop ?? ((hopUrl) => (0, _fedify_vocab_runtime_internal.openPublicFetch)(hopUrl));
|
|
98
109
|
let current = url instanceof URL ? url : new URL(url);
|
|
99
110
|
let redirected = 0;
|
|
100
111
|
while (true) {
|
|
112
|
+
let session;
|
|
101
113
|
try {
|
|
102
|
-
await (
|
|
114
|
+
session = await openHop(current.href);
|
|
103
115
|
} catch (e) {
|
|
104
116
|
if (e instanceof _fedify_vocab_runtime.UrlError) {
|
|
105
117
|
if (e.reason === "dns") logger.error("DNS lookup failed for {url}: {error}", {
|
|
@@ -114,52 +126,89 @@ async function fetchPublicUrl(url, headers) {
|
|
|
114
126
|
}
|
|
115
127
|
throw e;
|
|
116
128
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
129
|
+
let location;
|
|
130
|
+
try {
|
|
131
|
+
const response = await session.fetch(current, {
|
|
132
|
+
headers,
|
|
133
|
+
redirect: "manual"
|
|
134
|
+
});
|
|
135
|
+
if (response.status < 300 || response.status >= 400 || !response.headers.has("Location")) return await consume(response);
|
|
136
|
+
location = response.headers.get("Location");
|
|
137
|
+
cancelBody(response, current);
|
|
138
|
+
} finally {
|
|
139
|
+
await session.close();
|
|
140
|
+
}
|
|
141
|
+
redirected++;
|
|
142
|
+
if (redirected >= DEFAULT_MAX_REDIRECTION) {
|
|
143
|
+
logger.error("Too many redirections ({redirections}) while fetching {url}.", {
|
|
144
|
+
redirections: redirected,
|
|
145
|
+
url: current.href
|
|
146
|
+
});
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
const next = new URL(location, current);
|
|
150
|
+
if (next.protocol !== current.protocol) {
|
|
151
|
+
logger.error("Refused a redirect to a different protocol ({protocol} to {redirectedProtocol}) while fetching NodeInfo.", {
|
|
152
|
+
protocol: current.protocol,
|
|
153
|
+
redirectedProtocol: next.protocol
|
|
154
|
+
});
|
|
155
|
+
return null;
|
|
140
156
|
}
|
|
141
|
-
|
|
157
|
+
current = next;
|
|
142
158
|
}
|
|
143
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* Starts cancelling a response body that is not read, without waiting for it
|
|
162
|
+
* to settle. The cancellation of a branch of a tee (e.g., a body cloned by
|
|
163
|
+
* a fetch mock) settles only after the other branch is cancelled too, per
|
|
164
|
+
* the Streams standard, so awaiting it could block forever. A failure is
|
|
165
|
+
* only logged.
|
|
166
|
+
*/
|
|
167
|
+
function cancelBody(response, url) {
|
|
168
|
+
response.body?.cancel().catch((error) => {
|
|
169
|
+
logger.debug("Failed to cancel the response body from {url}: {error}", {
|
|
170
|
+
url: url.toString(),
|
|
171
|
+
error
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
}
|
|
144
175
|
async function getNodeInfo(url, options = {}) {
|
|
176
|
+
return await getNodeInfoInternal(url, options);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* The implementation of {@link getNodeInfo}, which is not exported from any
|
|
180
|
+
* entry point.
|
|
181
|
+
* @param url The base URL of the server, or the URL of the NodeInfo document
|
|
182
|
+
* if `options.direct` is turned on.
|
|
183
|
+
* @param options Options for fetching the NodeInfo document.
|
|
184
|
+
* @param deps The dependencies.
|
|
185
|
+
* @returns The NodeInfo document if it could be fetched successfully.
|
|
186
|
+
* Otherwise, `undefined` is returned.
|
|
187
|
+
* @internal
|
|
188
|
+
*/
|
|
189
|
+
async function getNodeInfoInternal(url, options = {}, deps = {}) {
|
|
145
190
|
try {
|
|
191
|
+
const headers = {
|
|
192
|
+
Accept: "application/json",
|
|
193
|
+
"User-Agent": typeof options.userAgent === "string" ? options.userAgent : (0, _fedify_vocab_runtime.getUserAgent)(options.userAgent)
|
|
194
|
+
};
|
|
146
195
|
let nodeInfoUrl = url;
|
|
147
196
|
if (!options.direct) {
|
|
148
197
|
const wellKnownUrl = new URL("/.well-known/nodeinfo", url);
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const wellKnownRd =
|
|
198
|
+
const wellKnown = await fetchPublicUrl(wellKnownUrl, headers, async (response) => {
|
|
199
|
+
if (!response.ok) {
|
|
200
|
+
logger.error("Failed to fetch {url}: {status} {statusText}", {
|
|
201
|
+
url: wellKnownUrl.href,
|
|
202
|
+
status: response.status,
|
|
203
|
+
statusText: response.statusText
|
|
204
|
+
});
|
|
205
|
+
cancelBody(response, wellKnownUrl);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
return { rd: JSON.parse(await readBoundedText(response, MAX_BODY_SIZE, wellKnownUrl)) };
|
|
209
|
+
}, deps);
|
|
210
|
+
if (wellKnown == null) return void 0;
|
|
211
|
+
const wellKnownRd = wellKnown.rd;
|
|
163
212
|
const link = wellKnownRd?.links?.find((link) => link != null && "rel" in link && (link.rel === "http://nodeinfo.diaspora.software/ns/schema/2.0" || link.rel === "http://nodeinfo.diaspora.software/ns/schema/2.1") && "href" in link && link.href != null);
|
|
164
213
|
if (link == null || link.href == null) {
|
|
165
214
|
logger.error("Failed to find a NodeInfo document link from {url}: {resourceDescriptor}", {
|
|
@@ -170,20 +219,20 @@ async function getNodeInfo(url, options = {}) {
|
|
|
170
219
|
}
|
|
171
220
|
nodeInfoUrl = link.href;
|
|
172
221
|
}
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const data =
|
|
222
|
+
const document = await fetchPublicUrl(nodeInfoUrl, headers, async (response) => {
|
|
223
|
+
if (!response.ok) {
|
|
224
|
+
logger.error("Failed to fetch NodeInfo document from {url}: {status} {statusText}", {
|
|
225
|
+
url: nodeInfoUrl.toString(),
|
|
226
|
+
status: response.status,
|
|
227
|
+
statusText: response.statusText
|
|
228
|
+
});
|
|
229
|
+
cancelBody(response, nodeInfoUrl);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
return { data: JSON.parse(await readBoundedText(response, MAX_BODY_SIZE, nodeInfoUrl)) };
|
|
233
|
+
}, deps);
|
|
234
|
+
if (document == null) return void 0;
|
|
235
|
+
const { data } = document;
|
|
187
236
|
if (options.parse === "none") return data;
|
|
188
237
|
return parseNodeInfo(data, { tryBestEffort: options.parse === "best-effort" }) ?? void 0;
|
|
189
238
|
} catch (error) {
|