@fedify/vocab-runtime 2.4.0-dev.1570 → 2.4.0-dev.1571
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/deno.json +2 -1
- package/dist/docloader-DnUMWHaJ.d.cts +202 -0
- package/dist/docloader-xRGn1azD.d.ts +202 -0
- package/dist/internal/jsonld-cache.cjs +279 -0
- package/dist/internal/jsonld-cache.d.cts +48 -0
- package/dist/internal/jsonld-cache.d.ts +48 -0
- package/dist/internal/jsonld-cache.js +275 -0
- package/dist/mod.cjs +178 -190
- package/dist/mod.d.cts +59 -190
- package/dist/mod.d.ts +59 -190
- package/dist/mod.js +165 -184
- package/dist/tests/decimal.test.cjs +3 -3
- package/dist/tests/decimal.test.mjs +3 -3
- package/dist/tests/{docloader-C76ldE5C.mjs → docloader-DHSAmRW2.mjs} +97 -10
- package/dist/tests/{docloader-mvgIWKI7.cjs → docloader-DkiPIIAk.cjs} +102 -9
- package/dist/tests/docloader.test.cjs +58 -6
- package/dist/tests/docloader.test.mjs +58 -6
- package/dist/tests/jsonld-cache.test.cjs +652 -0
- package/dist/tests/jsonld-cache.test.d.cts +1 -0
- package/dist/tests/jsonld-cache.test.d.mts +1 -0
- package/dist/tests/jsonld-cache.test.mjs +651 -0
- package/dist/tests/{key-CrrK9mYh.mjs → key-CDGDH_vC.mjs} +69 -1
- package/dist/tests/{key-pMmqUKuo.cjs → key-_wXwomh_.cjs} +86 -0
- package/dist/tests/key.test.cjs +48 -1
- package/dist/tests/key.test.mjs +48 -1
- package/dist/tests/{request-BEXkv1ul.mjs → request-CYFeP37O.mjs} +1 -1
- package/dist/tests/{request-SworbvLc.cjs → request-D3H8nWtX.cjs} +1 -1
- package/dist/tests/request.test.cjs +1 -1
- package/dist/tests/request.test.mjs +1 -1
- package/dist/tests/{url-C20FhC7p.cjs → url-2XwVbUS_.cjs} +108 -0
- package/dist/tests/{url-m9Qzxy-Y.mjs → url-YWJbnRlf.mjs} +85 -1
- package/dist/tests/url.test.cjs +104 -1
- package/dist/tests/url.test.mjs +105 -2
- package/dist/url-BAdyyqAa.cjs +315 -0
- package/dist/url-BuxPHxK2.js +261 -0
- package/package.json +11 -1
- package/src/contexts/fep-7aa9.json +24 -0
- package/src/contexts.ts +2 -0
- package/src/docloader.test.ts +102 -6
- package/src/docloader.ts +76 -8
- package/src/internal/jsonld-cache.ts +538 -0
- package/src/jsonld-cache.test.ts +554 -0
- package/src/key.test.ts +86 -0
- package/src/key.ts +125 -0
- package/src/mod.ts +8 -0
- package/src/url.test.ts +252 -1
- package/src/url.ts +141 -0
- package/tsdown.config.ts +6 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require("./chunk-C2EiDwsr.cjs");
|
|
2
|
-
const require_request = require("./request-
|
|
2
|
+
const require_request = require("./request-D3H8nWtX.cjs");
|
|
3
3
|
const require_link = require("./link-FguCydMA.cjs");
|
|
4
|
-
const require_url = require("./url-
|
|
4
|
+
const require_url = require("./url-2XwVbUS_.cjs");
|
|
5
5
|
let _logtape_logtape = require("@logtape/logtape");
|
|
6
6
|
let _opentelemetry_api = require("@opentelemetry/api");
|
|
7
7
|
//#endregion
|
|
@@ -4278,6 +4278,28 @@ const preloadedContexts = {
|
|
|
4278
4278
|
"@type": "@id"
|
|
4279
4279
|
}
|
|
4280
4280
|
} },
|
|
4281
|
+
"https://w3id.org/fep/7aa9": { "@context": {
|
|
4282
|
+
"FeaturedCollection": "https://w3id.org/fep/7aa9#FeaturedCollection",
|
|
4283
|
+
"FeaturedItem": "https://w3id.org/fep/7aa9#FeaturedItem",
|
|
4284
|
+
"FeatureRequest": "https://w3id.org/fep/7aa9#FeatureRequest",
|
|
4285
|
+
"FeatureAuthorization": "https://w3id.org/fep/7aa9#FeatureAuthorization",
|
|
4286
|
+
"topic": {
|
|
4287
|
+
"@id": "https://w3id.org/fep/7aa9#topic",
|
|
4288
|
+
"@type": "@id"
|
|
4289
|
+
},
|
|
4290
|
+
"featuredObject": {
|
|
4291
|
+
"@id": "https://w3id.org/fep/7aa9#featuredObject",
|
|
4292
|
+
"@type": "@id"
|
|
4293
|
+
},
|
|
4294
|
+
"canFeature": {
|
|
4295
|
+
"@id": "https://w3id.org/fep/7aa9#canFeature",
|
|
4296
|
+
"@type": "@id"
|
|
4297
|
+
},
|
|
4298
|
+
"featureAuthorization": {
|
|
4299
|
+
"@id": "https://w3id.org/fep/7aa9#featureAuthorization",
|
|
4300
|
+
"@type": "@id"
|
|
4301
|
+
}
|
|
4302
|
+
} },
|
|
4281
4303
|
"https://join-lemmy.org/context.json": { "@context": ["https://w3id.org/security/v1", {
|
|
4282
4304
|
"as": "https://www.w3.org/ns/activitystreams#",
|
|
4283
4305
|
"lemmy": "https://join-lemmy.org/ns#",
|
|
@@ -4341,6 +4363,66 @@ const logger = (0, _logtape_logtape.getLogger)([
|
|
|
4341
4363
|
"docloader"
|
|
4342
4364
|
]);
|
|
4343
4365
|
const DEFAULT_MAX_REDIRECTION = 20;
|
|
4366
|
+
const MAX_HTML_SIZE = 1024 * 1024;
|
|
4367
|
+
function createResponseMetadata(response) {
|
|
4368
|
+
return new Response(null, {
|
|
4369
|
+
headers: response.headers,
|
|
4370
|
+
status: response.status,
|
|
4371
|
+
statusText: response.statusText
|
|
4372
|
+
});
|
|
4373
|
+
}
|
|
4374
|
+
async function cancelResponseBody(response) {
|
|
4375
|
+
if (response.body != null) await response.body.cancel();
|
|
4376
|
+
}
|
|
4377
|
+
async function readBoundedText(response, maxBytes) {
|
|
4378
|
+
const contentLength = response.headers.get("Content-Length");
|
|
4379
|
+
if (contentLength != null) {
|
|
4380
|
+
const size = Number(contentLength);
|
|
4381
|
+
if (size > maxBytes) {
|
|
4382
|
+
await cancelResponseBody(response);
|
|
4383
|
+
return {
|
|
4384
|
+
text: "",
|
|
4385
|
+
size,
|
|
4386
|
+
tooLarge: true
|
|
4387
|
+
};
|
|
4388
|
+
}
|
|
4389
|
+
}
|
|
4390
|
+
if (response.body == null) return {
|
|
4391
|
+
text: "",
|
|
4392
|
+
size: 0,
|
|
4393
|
+
tooLarge: false
|
|
4394
|
+
};
|
|
4395
|
+
const reader = response.body.getReader();
|
|
4396
|
+
const decoder = new TextDecoder();
|
|
4397
|
+
let text = "";
|
|
4398
|
+
let size = 0;
|
|
4399
|
+
try {
|
|
4400
|
+
while (true) {
|
|
4401
|
+
const result = await reader.read();
|
|
4402
|
+
if (result.done) break;
|
|
4403
|
+
const chunkSize = result.value.byteLength;
|
|
4404
|
+
if (size + chunkSize > maxBytes) {
|
|
4405
|
+
size += chunkSize;
|
|
4406
|
+
await reader.cancel();
|
|
4407
|
+
return {
|
|
4408
|
+
text: "",
|
|
4409
|
+
size,
|
|
4410
|
+
tooLarge: true
|
|
4411
|
+
};
|
|
4412
|
+
}
|
|
4413
|
+
size += chunkSize;
|
|
4414
|
+
text += decoder.decode(result.value, { stream: true });
|
|
4415
|
+
}
|
|
4416
|
+
text += decoder.decode();
|
|
4417
|
+
return {
|
|
4418
|
+
text,
|
|
4419
|
+
size,
|
|
4420
|
+
tooLarge: false
|
|
4421
|
+
};
|
|
4422
|
+
} finally {
|
|
4423
|
+
reader.releaseLock();
|
|
4424
|
+
}
|
|
4425
|
+
}
|
|
4344
4426
|
/**
|
|
4345
4427
|
* Gets a {@link RemoteDocument} from the given response.
|
|
4346
4428
|
* @param url The URL of the document to load.
|
|
@@ -4395,19 +4477,19 @@ async function getRemoteDocument(url, response, fetch) {
|
|
|
4395
4477
|
}
|
|
4396
4478
|
let document;
|
|
4397
4479
|
if (!jsonLd && (contentType === "text/html" || contentType?.startsWith("text/html;") || contentType === "application/xhtml+xml" || contentType?.startsWith("application/xhtml+xml;"))) {
|
|
4398
|
-
const
|
|
4399
|
-
const html = await response
|
|
4400
|
-
if (html.
|
|
4480
|
+
const errorResponse = createResponseMetadata(response);
|
|
4481
|
+
const html = await readBoundedText(response, MAX_HTML_SIZE);
|
|
4482
|
+
if (html.tooLarge) {
|
|
4401
4483
|
logger.warn("HTML response too large, skipping alternate link discovery: {url}", {
|
|
4402
4484
|
url: documentUrl,
|
|
4403
|
-
size: html.
|
|
4485
|
+
size: html.size
|
|
4404
4486
|
});
|
|
4405
|
-
|
|
4487
|
+
throw new require_request.FetchError(documentUrl, `HTML document is too large to scan for an ActivityPub alternate link (Content-Type: ${contentType})`, errorResponse);
|
|
4406
4488
|
} else {
|
|
4407
4489
|
const tagPattern = /<(a|link)\s+([^>]*?)\s*\/?>/gi;
|
|
4408
4490
|
const attrPattern = /([a-z][a-z:_-]*)=(?:"([^"]*)"|'([^']*)'|([^\s>]+))/gi;
|
|
4409
4491
|
let tagMatch;
|
|
4410
|
-
while ((tagMatch = tagPattern.exec(html)) !== null) {
|
|
4492
|
+
while ((tagMatch = tagPattern.exec(html.text)) !== null) {
|
|
4411
4493
|
const tagContent = tagMatch[2];
|
|
4412
4494
|
let attrMatch;
|
|
4413
4495
|
const attribs = {};
|
|
@@ -4424,7 +4506,12 @@ async function getRemoteDocument(url, response, fetch) {
|
|
|
4424
4506
|
return await fetch(new URL(attribs.href, docUrl).href);
|
|
4425
4507
|
}
|
|
4426
4508
|
}
|
|
4427
|
-
|
|
4509
|
+
try {
|
|
4510
|
+
document = JSON.parse(html.text);
|
|
4511
|
+
} catch (error) {
|
|
4512
|
+
if (!(error instanceof SyntaxError)) throw error;
|
|
4513
|
+
throw new require_request.FetchError(documentUrl, `HTML document has no ActivityPub alternate link (Content-Type: ${contentType})`, errorResponse);
|
|
4514
|
+
}
|
|
4428
4515
|
}
|
|
4429
4516
|
} else document = await response.json();
|
|
4430
4517
|
logger.debug("Fetched document: {status} {url} {headers}", {
|
|
@@ -4535,6 +4622,12 @@ Object.defineProperty(exports, "getDocumentLoader", {
|
|
|
4535
4622
|
return getDocumentLoader;
|
|
4536
4623
|
}
|
|
4537
4624
|
});
|
|
4625
|
+
Object.defineProperty(exports, "getRemoteDocument", {
|
|
4626
|
+
enumerable: true,
|
|
4627
|
+
get: function() {
|
|
4628
|
+
return getRemoteDocument;
|
|
4629
|
+
}
|
|
4630
|
+
});
|
|
4538
4631
|
Object.defineProperty(exports, "preloadedContexts", {
|
|
4539
4632
|
enumerable: true,
|
|
4540
4633
|
get: function() {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-C2EiDwsr.cjs");
|
|
2
|
-
const require_docloader = require("./docloader-
|
|
3
|
-
const require_request = require("./request-
|
|
4
|
-
const require_url = require("./url-
|
|
2
|
+
const require_docloader = require("./docloader-DkiPIIAk.cjs");
|
|
3
|
+
const require_request = require("./request-D3H8nWtX.cjs");
|
|
4
|
+
const require_url = require("./url-2XwVbUS_.cjs");
|
|
5
5
|
let node_assert = require("node:assert");
|
|
6
6
|
let node_test = require("node:test");
|
|
7
7
|
//#region ../../node_modules/.pnpm/glob-to-regexp@0.4.1/node_modules/glob-to-regexp/index.js
|
|
@@ -1267,7 +1267,7 @@ var esm_default = new class FetchMock {
|
|
|
1267
1267
|
type: "Object"
|
|
1268
1268
|
},
|
|
1269
1269
|
headers: {
|
|
1270
|
-
"Content-Type": "
|
|
1270
|
+
"Content-Type": "application/activity+json",
|
|
1271
1271
|
Link: "<https://example.com/object>; rel=\"alternate\"; type=\"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"\""
|
|
1272
1272
|
}
|
|
1273
1273
|
});
|
|
@@ -1399,6 +1399,27 @@ var esm_default = new class FetchMock {
|
|
|
1399
1399
|
}
|
|
1400
1400
|
});
|
|
1401
1401
|
});
|
|
1402
|
+
esm_default.get("https://example.com/html-no-alternate", {
|
|
1403
|
+
body: `<!DOCTYPE html>
|
|
1404
|
+
<html>
|
|
1405
|
+
<head>
|
|
1406
|
+
<title>Not an ActivityPub document</title>
|
|
1407
|
+
</head>
|
|
1408
|
+
<body>Not found</body>
|
|
1409
|
+
</html>`,
|
|
1410
|
+
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
1411
|
+
});
|
|
1412
|
+
await t.test("HTML without ActivityPub alternate link", async () => {
|
|
1413
|
+
await (0, node_assert.rejects)(() => fetchDocumentLoader("https://example.com/html-no-alternate"), (error) => {
|
|
1414
|
+
(0, node_assert.ok)(error instanceof require_request.FetchError);
|
|
1415
|
+
(0, node_assert.ok)(error.message.includes("HTML document has no ActivityPub alternate link"));
|
|
1416
|
+
(0, node_assert.ok)(error.message.includes("Content-Type: text/html; charset=utf-8"));
|
|
1417
|
+
(0, node_assert.deepStrictEqual)(error.url, new URL("https://example.com/html-no-alternate"));
|
|
1418
|
+
(0, node_assert.ok)(error.response != null);
|
|
1419
|
+
(0, node_assert.deepStrictEqual)(error.response.headers.get("Content-Type"), "text/html; charset=utf-8");
|
|
1420
|
+
return true;
|
|
1421
|
+
});
|
|
1422
|
+
});
|
|
1402
1423
|
esm_default.get("https://example.com/wrong-content-type", {
|
|
1403
1424
|
body: {
|
|
1404
1425
|
"@context": "https://www.w3.org/ns/activitystreams",
|
|
@@ -1408,7 +1429,7 @@ var esm_default = new class FetchMock {
|
|
|
1408
1429
|
},
|
|
1409
1430
|
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
1410
1431
|
});
|
|
1411
|
-
await t.test("
|
|
1432
|
+
await t.test("wrong Content-Type with JSON body", async () => {
|
|
1412
1433
|
(0, node_assert.deepStrictEqual)(await fetchDocumentLoader("https://example.com/wrong-content-type"), {
|
|
1413
1434
|
contextUrl: null,
|
|
1414
1435
|
documentUrl: "https://example.com/wrong-content-type",
|
|
@@ -1420,6 +1441,37 @@ var esm_default = new class FetchMock {
|
|
|
1420
1441
|
}
|
|
1421
1442
|
});
|
|
1422
1443
|
});
|
|
1444
|
+
esm_default.get("https://example.com/large-html", {
|
|
1445
|
+
body: "<!DOCTYPE html>",
|
|
1446
|
+
headers: {
|
|
1447
|
+
"Content-Length": String(1048577),
|
|
1448
|
+
"Content-Type": "text/html; charset=utf-8"
|
|
1449
|
+
}
|
|
1450
|
+
});
|
|
1451
|
+
await t.test("HTML Content-Length over limit", async () => {
|
|
1452
|
+
await (0, node_assert.rejects)(() => fetchDocumentLoader("https://example.com/large-html"), (error) => {
|
|
1453
|
+
(0, node_assert.ok)(error instanceof require_request.FetchError);
|
|
1454
|
+
(0, node_assert.ok)(error.message.includes("HTML document is too large to scan for an ActivityPub alternate link"));
|
|
1455
|
+
(0, node_assert.ok)(error.response != null);
|
|
1456
|
+
(0, node_assert.deepStrictEqual)(error.response.status, 200);
|
|
1457
|
+
(0, node_assert.deepStrictEqual)(error.response.headers.get("Content-Type"), "text/html; charset=utf-8");
|
|
1458
|
+
return true;
|
|
1459
|
+
});
|
|
1460
|
+
});
|
|
1461
|
+
await t.test("HTML Content-Length over limit cancels body", async () => {
|
|
1462
|
+
let canceled = false;
|
|
1463
|
+
const response = new Response("<!DOCTYPE html>", { headers: {
|
|
1464
|
+
"Content-Length": String(1048577),
|
|
1465
|
+
"Content-Type": "text/html; charset=utf-8"
|
|
1466
|
+
} });
|
|
1467
|
+
Object.defineProperty(response, "body", { value: { cancel: () => {
|
|
1468
|
+
canceled = true;
|
|
1469
|
+
} } });
|
|
1470
|
+
await (0, node_assert.rejects)(() => require_docloader.getRemoteDocument("https://example.com/large-html-cancel", response, () => {
|
|
1471
|
+
throw new Error("unexpected alternate fetch");
|
|
1472
|
+
}), require_request.FetchError);
|
|
1473
|
+
(0, node_assert.deepStrictEqual)(canceled, true);
|
|
1474
|
+
});
|
|
1423
1475
|
esm_default.get("https://example.com/404", { status: 404 });
|
|
1424
1476
|
await t.test("not ok", async () => {
|
|
1425
1477
|
await (0, node_assert.rejects)(() => fetchDocumentLoader("https://example.com/404"), require_request.FetchError, "HTTP 404: https://example.com/404");
|
|
@@ -1536,7 +1588,7 @@ var esm_default = new class FetchMock {
|
|
|
1536
1588
|
});
|
|
1537
1589
|
await t.test("ReDoS resistance (CVE-2025-68475)", async () => {
|
|
1538
1590
|
const start = performance.now();
|
|
1539
|
-
await (0, node_assert.rejects)(() => fetchDocumentLoader("https://example.com/redos"),
|
|
1591
|
+
await (0, node_assert.rejects)(() => fetchDocumentLoader("https://example.com/redos"), require_request.FetchError);
|
|
1540
1592
|
const elapsed = performance.now() - start;
|
|
1541
1593
|
(0, node_assert.ok)(elapsed < 1e3, `Potential ReDoS vulnerability detected: ${elapsed}ms (expected < 1000ms)`);
|
|
1542
1594
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as preloadedContexts, t as getDocumentLoader } from "./docloader-
|
|
2
|
-
import { t as FetchError } from "./request-
|
|
3
|
-
import { t as UrlError } from "./url-
|
|
1
|
+
import { n as getRemoteDocument, r as preloadedContexts, t as getDocumentLoader } from "./docloader-DHSAmRW2.mjs";
|
|
2
|
+
import { t as FetchError } from "./request-CYFeP37O.mjs";
|
|
3
|
+
import { t as UrlError } from "./url-YWJbnRlf.mjs";
|
|
4
4
|
import { deepStrictEqual, ok, rejects } from "node:assert";
|
|
5
5
|
import { test } from "node:test";
|
|
6
6
|
//#region \0rolldown/runtime.js
|
|
@@ -1286,7 +1286,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
1286
1286
|
type: "Object"
|
|
1287
1287
|
},
|
|
1288
1288
|
headers: {
|
|
1289
|
-
"Content-Type": "
|
|
1289
|
+
"Content-Type": "application/activity+json",
|
|
1290
1290
|
Link: "<https://example.com/object>; rel=\"alternate\"; type=\"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"\""
|
|
1291
1291
|
}
|
|
1292
1292
|
});
|
|
@@ -1418,6 +1418,27 @@ test("getDocumentLoader()", async (t) => {
|
|
|
1418
1418
|
}
|
|
1419
1419
|
});
|
|
1420
1420
|
});
|
|
1421
|
+
esm_default.get("https://example.com/html-no-alternate", {
|
|
1422
|
+
body: `<!DOCTYPE html>
|
|
1423
|
+
<html>
|
|
1424
|
+
<head>
|
|
1425
|
+
<title>Not an ActivityPub document</title>
|
|
1426
|
+
</head>
|
|
1427
|
+
<body>Not found</body>
|
|
1428
|
+
</html>`,
|
|
1429
|
+
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
1430
|
+
});
|
|
1431
|
+
await t.test("HTML without ActivityPub alternate link", async () => {
|
|
1432
|
+
await rejects(() => fetchDocumentLoader("https://example.com/html-no-alternate"), (error) => {
|
|
1433
|
+
ok(error instanceof FetchError);
|
|
1434
|
+
ok(error.message.includes("HTML document has no ActivityPub alternate link"));
|
|
1435
|
+
ok(error.message.includes("Content-Type: text/html; charset=utf-8"));
|
|
1436
|
+
deepStrictEqual(error.url, new URL("https://example.com/html-no-alternate"));
|
|
1437
|
+
ok(error.response != null);
|
|
1438
|
+
deepStrictEqual(error.response.headers.get("Content-Type"), "text/html; charset=utf-8");
|
|
1439
|
+
return true;
|
|
1440
|
+
});
|
|
1441
|
+
});
|
|
1421
1442
|
esm_default.get("https://example.com/wrong-content-type", {
|
|
1422
1443
|
body: {
|
|
1423
1444
|
"@context": "https://www.w3.org/ns/activitystreams",
|
|
@@ -1427,7 +1448,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
1427
1448
|
},
|
|
1428
1449
|
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
1429
1450
|
});
|
|
1430
|
-
await t.test("
|
|
1451
|
+
await t.test("wrong Content-Type with JSON body", async () => {
|
|
1431
1452
|
deepStrictEqual(await fetchDocumentLoader("https://example.com/wrong-content-type"), {
|
|
1432
1453
|
contextUrl: null,
|
|
1433
1454
|
documentUrl: "https://example.com/wrong-content-type",
|
|
@@ -1439,6 +1460,37 @@ test("getDocumentLoader()", async (t) => {
|
|
|
1439
1460
|
}
|
|
1440
1461
|
});
|
|
1441
1462
|
});
|
|
1463
|
+
esm_default.get("https://example.com/large-html", {
|
|
1464
|
+
body: "<!DOCTYPE html>",
|
|
1465
|
+
headers: {
|
|
1466
|
+
"Content-Length": String(1048577),
|
|
1467
|
+
"Content-Type": "text/html; charset=utf-8"
|
|
1468
|
+
}
|
|
1469
|
+
});
|
|
1470
|
+
await t.test("HTML Content-Length over limit", async () => {
|
|
1471
|
+
await rejects(() => fetchDocumentLoader("https://example.com/large-html"), (error) => {
|
|
1472
|
+
ok(error instanceof FetchError);
|
|
1473
|
+
ok(error.message.includes("HTML document is too large to scan for an ActivityPub alternate link"));
|
|
1474
|
+
ok(error.response != null);
|
|
1475
|
+
deepStrictEqual(error.response.status, 200);
|
|
1476
|
+
deepStrictEqual(error.response.headers.get("Content-Type"), "text/html; charset=utf-8");
|
|
1477
|
+
return true;
|
|
1478
|
+
});
|
|
1479
|
+
});
|
|
1480
|
+
await t.test("HTML Content-Length over limit cancels body", async () => {
|
|
1481
|
+
let canceled = false;
|
|
1482
|
+
const response = new Response("<!DOCTYPE html>", { headers: {
|
|
1483
|
+
"Content-Length": String(1048577),
|
|
1484
|
+
"Content-Type": "text/html; charset=utf-8"
|
|
1485
|
+
} });
|
|
1486
|
+
Object.defineProperty(response, "body", { value: { cancel: () => {
|
|
1487
|
+
canceled = true;
|
|
1488
|
+
} } });
|
|
1489
|
+
await rejects(() => getRemoteDocument("https://example.com/large-html-cancel", response, () => {
|
|
1490
|
+
throw new Error("unexpected alternate fetch");
|
|
1491
|
+
}), FetchError);
|
|
1492
|
+
deepStrictEqual(canceled, true);
|
|
1493
|
+
});
|
|
1442
1494
|
esm_default.get("https://example.com/404", { status: 404 });
|
|
1443
1495
|
await t.test("not ok", async () => {
|
|
1444
1496
|
await rejects(() => fetchDocumentLoader("https://example.com/404"), FetchError, "HTTP 404: https://example.com/404");
|
|
@@ -1555,7 +1607,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
1555
1607
|
});
|
|
1556
1608
|
await t.test("ReDoS resistance (CVE-2025-68475)", async () => {
|
|
1557
1609
|
const start = performance.now();
|
|
1558
|
-
await rejects(() => fetchDocumentLoader("https://example.com/redos"),
|
|
1610
|
+
await rejects(() => fetchDocumentLoader("https://example.com/redos"), FetchError);
|
|
1559
1611
|
const elapsed = performance.now() - start;
|
|
1560
1612
|
ok(elapsed < 1e3, `Potential ReDoS vulnerability detected: ${elapsed}ms (expected < 1000ms)`);
|
|
1561
1613
|
});
|