@fedify/vocab-runtime 2.0.0-dev.1875 → 2.0.0-dev.196
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/LICENSE +1 -1
- package/README.md +2 -1
- package/deno.json +7 -1
- package/dist/chunk-DWy1uDak.cjs +39 -0
- package/dist/docloader.test.cjs +5851 -0
- package/dist/docloader.test.d.cts +1 -0
- package/dist/docloader.test.d.ts +1 -0
- package/dist/docloader.test.js +5877 -0
- package/dist/key.test.cjs +272 -0
- package/dist/key.test.d.cts +1 -0
- package/dist/key.test.d.ts +1 -0
- package/dist/key.test.js +271 -0
- package/dist/langstr.test.cjs +51 -0
- package/dist/langstr.test.d.cts +1 -0
- package/dist/langstr.test.d.ts +1 -0
- package/dist/langstr.test.js +50 -0
- package/dist/link-CdFPEo9O.cjs +189 -0
- package/dist/link-Ck2yj4dH.js +183 -0
- package/dist/link.test.cjs +56 -0
- package/dist/link.test.d.cts +1 -0
- package/dist/link.test.d.ts +1 -0
- package/dist/link.test.js +55 -0
- package/dist/mod.cjs +102 -63
- package/dist/mod.js +102 -63
- package/dist/multibase/multibase.test.cjs +346 -0
- package/dist/multibase/multibase.test.d.cts +1 -0
- package/dist/multibase/multibase.test.d.ts +1 -0
- package/dist/multibase/multibase.test.js +345 -0
- package/dist/multibase-BFbBiaPE.cjs +347 -0
- package/dist/multibase-DStmqni9.js +311 -0
- package/dist/request-BPQb2VYj.cjs +138 -0
- package/dist/request-SuYiIZUu.js +108 -0
- package/dist/request.test.cjs +44 -0
- package/dist/request.test.d.cts +1 -0
- package/dist/request.test.d.ts +1 -0
- package/dist/request.test.js +43 -0
- package/dist/url-C5Vs9nYh.cjs +93 -0
- package/dist/url-fW_DHbih.js +63 -0
- package/dist/url.test.cjs +37 -0
- package/dist/url.test.d.cts +1 -0
- package/dist/url.test.d.ts +1 -0
- package/dist/url.test.js +36 -0
- package/package.json +4 -3
- package/src/docloader.test.ts +29 -1
- package/src/docloader.ts +101 -45
- package/tsdown.config.ts +18 -7
package/src/docloader.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { getLogger } from "@logtape/logtape";
|
|
2
|
+
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
3
|
+
import metadata from "../deno.json" with { type: "json" };
|
|
2
4
|
import preloadedContexts from "./contexts.ts";
|
|
3
5
|
import { HttpHeaderLink } from "./link.ts";
|
|
4
6
|
import {
|
|
@@ -189,37 +191,55 @@ export async function getRemoteDocument(
|
|
|
189
191
|
contentType === "application/xhtml+xml" ||
|
|
190
192
|
contentType?.startsWith("application/xhtml+xml;"))
|
|
191
193
|
) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const p2 = /\s+([a-z][a-z:_-]*)=("([^"]*)"|'([^']*)'|([^\s>]+))/ig;
|
|
194
|
+
// Security: Limit HTML response size to mitigate ReDoS attacks
|
|
195
|
+
const MAX_HTML_SIZE = 1024 * 1024; // 1MB
|
|
195
196
|
const html = await response.text();
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
197
|
+
if (html.length > MAX_HTML_SIZE) {
|
|
198
|
+
logger.warn(
|
|
199
|
+
"HTML response too large, skipping alternate link discovery: {url}",
|
|
200
|
+
{ url: documentUrl, size: html.length },
|
|
201
|
+
);
|
|
202
|
+
document = JSON.parse(html);
|
|
203
|
+
} else {
|
|
204
|
+
// Safe regex patterns without nested quantifiers to prevent ReDoS
|
|
205
|
+
// (CVE-2025-68475)
|
|
206
|
+
// Step 1: Extract <a ...> or <link ...> tags
|
|
207
|
+
const tagPattern = /<(a|link)\s+([^>]*?)\s*\/?>/gi;
|
|
208
|
+
// Step 2: Parse attributes
|
|
209
|
+
const attrPattern =
|
|
210
|
+
/([a-z][a-z:_-]*)=(?:"([^"]*)"|'([^']*)'|([^\s>]+))/gi;
|
|
211
|
+
|
|
212
|
+
let tagMatch: RegExpExecArray | null;
|
|
213
|
+
while ((tagMatch = tagPattern.exec(html)) !== null) {
|
|
214
|
+
const tagContent = tagMatch[2];
|
|
215
|
+
let attrMatch: RegExpExecArray | null;
|
|
216
|
+
const attribs: Record<string, string> = {};
|
|
217
|
+
|
|
218
|
+
// Reset regex state for attribute parsing
|
|
219
|
+
attrPattern.lastIndex = 0;
|
|
220
|
+
while ((attrMatch = attrPattern.exec(tagContent)) !== null) {
|
|
221
|
+
const key = attrMatch[1].toLowerCase();
|
|
222
|
+
const value = attrMatch[2] ?? attrMatch[3] ?? attrMatch[4] ?? "";
|
|
223
|
+
attribs[key] = value;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (
|
|
227
|
+
attribs.rel === "alternate" && "type" in attribs && (
|
|
228
|
+
attribs.type === "application/activity+json" ||
|
|
229
|
+
attribs.type === "application/ld+json" ||
|
|
230
|
+
attribs.type.startsWith("application/ld+json;")
|
|
231
|
+
) && "href" in attribs &&
|
|
232
|
+
new URL(attribs.href, docUrl).href !== docUrl.href
|
|
233
|
+
) {
|
|
234
|
+
logger.debug(
|
|
235
|
+
"Found alternate document: {alternateUrl} from {url}",
|
|
236
|
+
{ alternateUrl: attribs.href, url: documentUrl },
|
|
237
|
+
);
|
|
238
|
+
return await fetch(new URL(attribs.href, docUrl).href);
|
|
239
|
+
}
|
|
220
240
|
}
|
|
241
|
+
document = JSON.parse(html);
|
|
221
242
|
}
|
|
222
|
-
document = JSON.parse(html);
|
|
223
243
|
} else {
|
|
224
244
|
document = await response.json();
|
|
225
245
|
}
|
|
@@ -266,6 +286,9 @@ export function getDocumentLoader(
|
|
|
266
286
|
{ allowPrivateAddress, skipPreloadedContexts, userAgent }:
|
|
267
287
|
GetDocumentLoaderOptions = {},
|
|
268
288
|
): DocumentLoader {
|
|
289
|
+
const tracerProvider = trace.getTracerProvider();
|
|
290
|
+
const tracer = tracerProvider.getTracer(metadata.name, metadata.version);
|
|
291
|
+
|
|
269
292
|
async function load(
|
|
270
293
|
url: string,
|
|
271
294
|
options?: DocumentLoaderOptions,
|
|
@@ -289,23 +312,56 @@ export function getDocumentLoader(
|
|
|
289
312
|
throw error;
|
|
290
313
|
}
|
|
291
314
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
315
|
+
|
|
316
|
+
return await tracer.startActiveSpan(
|
|
317
|
+
"activitypub.fetch_document",
|
|
318
|
+
{
|
|
319
|
+
kind: SpanKind.CLIENT,
|
|
320
|
+
attributes: {
|
|
321
|
+
"url.full": url,
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
async (span) => {
|
|
325
|
+
try {
|
|
326
|
+
const request = createActivityPubRequest(url, { userAgent });
|
|
327
|
+
logRequest(logger, request);
|
|
328
|
+
const response = await fetch(request, {
|
|
329
|
+
// Since Bun has a bug that ignores the `Request.redirect` option,
|
|
330
|
+
// to work around it we specify `redirect: "manual"` here too:
|
|
331
|
+
// https://github.com/oven-sh/bun/issues/10754
|
|
332
|
+
redirect: "manual",
|
|
333
|
+
signal: options?.signal,
|
|
334
|
+
});
|
|
335
|
+
span.setAttribute("http.response.status_code", response.status);
|
|
336
|
+
|
|
337
|
+
// Follow redirects manually to get the final URL:
|
|
338
|
+
if (
|
|
339
|
+
response.status >= 300 && response.status < 400 &&
|
|
340
|
+
response.headers.has("Location")
|
|
341
|
+
) {
|
|
342
|
+
const redirectUrl = response.headers.get("Location")!;
|
|
343
|
+
span.setAttribute("http.redirect.url", redirectUrl);
|
|
344
|
+
return await load(redirectUrl, options);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const result = await getRemoteDocument(url, response, load);
|
|
348
|
+
span.setAttribute("docloader.document_url", result.documentUrl);
|
|
349
|
+
if (result.contextUrl != null) {
|
|
350
|
+
span.setAttribute("docloader.context_url", result.contextUrl);
|
|
351
|
+
}
|
|
352
|
+
return result;
|
|
353
|
+
} catch (error) {
|
|
354
|
+
span.recordException(error as Error);
|
|
355
|
+
span.setStatus({
|
|
356
|
+
code: SpanStatusCode.ERROR,
|
|
357
|
+
message: String(error),
|
|
358
|
+
});
|
|
359
|
+
throw error;
|
|
360
|
+
} finally {
|
|
361
|
+
span.end();
|
|
362
|
+
}
|
|
363
|
+
},
|
|
364
|
+
);
|
|
309
365
|
}
|
|
310
366
|
return load;
|
|
311
367
|
}
|
package/tsdown.config.ts
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
|
+
import { glob } from "node:fs/promises";
|
|
2
|
+
import { sep } from "node:path";
|
|
1
3
|
import { defineConfig } from "tsdown";
|
|
2
4
|
|
|
3
|
-
export default
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
export default [
|
|
6
|
+
defineConfig({
|
|
7
|
+
entry: ["src/mod.ts"],
|
|
8
|
+
dts: true,
|
|
9
|
+
format: ["esm", "cjs"],
|
|
10
|
+
platform: "neutral",
|
|
11
|
+
external: [/^node:/],
|
|
12
|
+
}),
|
|
13
|
+
defineConfig({
|
|
14
|
+
entry: (await Array.fromAsync(glob(`src/**/*.test.ts`)))
|
|
15
|
+
.map((f) => f.replace(sep, "/")),
|
|
16
|
+
format: ["esm", "cjs"],
|
|
17
|
+
platform: "node",
|
|
18
|
+
external: [/^node:/],
|
|
19
|
+
}),
|
|
20
|
+
];
|