@fedify/vocab-runtime 2.4.0-dev.1567 → 2.4.0-dev.1570
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 +1 -2
- package/dist/mod.cjs +190 -178
- package/dist/mod.d.cts +190 -59
- package/dist/mod.d.ts +190 -59
- package/dist/mod.js +184 -165
- package/dist/tests/decimal.test.cjs +3 -3
- package/dist/tests/decimal.test.mjs +3 -3
- package/dist/tests/{docloader-D61oV0K1.mjs → docloader-C76ldE5C.mjs} +10 -97
- package/dist/tests/{docloader-BcaQHb0Q.cjs → docloader-mvgIWKI7.cjs} +9 -102
- package/dist/tests/docloader.test.cjs +6 -58
- package/dist/tests/docloader.test.mjs +6 -58
- package/dist/tests/{key-CDGDH_vC.mjs → key-CrrK9mYh.mjs} +1 -69
- package/dist/tests/{key-_wXwomh_.cjs → key-pMmqUKuo.cjs} +0 -86
- package/dist/tests/key.test.cjs +1 -48
- package/dist/tests/key.test.mjs +1 -48
- package/dist/tests/{request-Cn0vP7Hj.mjs → request-BEXkv1ul.mjs} +1 -1
- package/dist/tests/{request-DnNWah97.cjs → request-SworbvLc.cjs} +1 -1
- package/dist/tests/request.test.cjs +1 -1
- package/dist/tests/request.test.mjs +1 -1
- package/dist/tests/{url-2XwVbUS_.cjs → url-C20FhC7p.cjs} +0 -108
- package/dist/tests/{url-YWJbnRlf.mjs → url-m9Qzxy-Y.mjs} +1 -85
- package/dist/tests/url.test.cjs +1 -104
- package/dist/tests/url.test.mjs +2 -105
- package/package.json +1 -11
- package/src/contexts.ts +0 -2
- package/src/docloader.test.ts +6 -102
- package/src/docloader.ts +8 -76
- package/src/key.test.ts +0 -86
- package/src/key.ts +0 -125
- package/src/mod.ts +0 -8
- package/src/url.test.ts +1 -252
- package/src/url.ts +0 -141
- package/tsdown.config.ts +1 -6
- package/dist/docloader-DnUMWHaJ.d.cts +0 -202
- package/dist/docloader-xRGn1azD.d.ts +0 -202
- package/dist/internal/jsonld-cache.cjs +0 -279
- package/dist/internal/jsonld-cache.d.cts +0 -48
- package/dist/internal/jsonld-cache.d.ts +0 -48
- package/dist/internal/jsonld-cache.js +0 -275
- package/dist/tests/jsonld-cache.test.cjs +0 -652
- package/dist/tests/jsonld-cache.test.d.cts +0 -1
- package/dist/tests/jsonld-cache.test.d.mts +0 -1
- package/dist/tests/jsonld-cache.test.mjs +0 -651
- package/dist/url-BAdyyqAa.cjs +0 -315
- package/dist/url-BuxPHxK2.js +0 -261
- package/src/contexts/fep-7aa9.json +0 -24
- package/src/internal/jsonld-cache.ts +0 -538
- package/src/jsonld-cache.test.ts +0 -554
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require("./chunk-C2EiDwsr.cjs");
|
|
2
|
-
const require_request = require("./request-
|
|
2
|
+
const require_request = require("./request-SworbvLc.cjs");
|
|
3
3
|
const require_link = require("./link-FguCydMA.cjs");
|
|
4
|
-
const require_url = require("./url-
|
|
4
|
+
const require_url = require("./url-C20FhC7p.cjs");
|
|
5
5
|
let _logtape_logtape = require("@logtape/logtape");
|
|
6
6
|
let _opentelemetry_api = require("@opentelemetry/api");
|
|
7
7
|
//#endregion
|
|
@@ -4278,28 +4278,6 @@ 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
|
-
} },
|
|
4303
4281
|
"https://join-lemmy.org/context.json": { "@context": ["https://w3id.org/security/v1", {
|
|
4304
4282
|
"as": "https://www.w3.org/ns/activitystreams#",
|
|
4305
4283
|
"lemmy": "https://join-lemmy.org/ns#",
|
|
@@ -4363,66 +4341,6 @@ const logger = (0, _logtape_logtape.getLogger)([
|
|
|
4363
4341
|
"docloader"
|
|
4364
4342
|
]);
|
|
4365
4343
|
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
|
-
}
|
|
4426
4344
|
/**
|
|
4427
4345
|
* Gets a {@link RemoteDocument} from the given response.
|
|
4428
4346
|
* @param url The URL of the document to load.
|
|
@@ -4477,19 +4395,19 @@ async function getRemoteDocument(url, response, fetch) {
|
|
|
4477
4395
|
}
|
|
4478
4396
|
let document;
|
|
4479
4397
|
if (!jsonLd && (contentType === "text/html" || contentType?.startsWith("text/html;") || contentType === "application/xhtml+xml" || contentType?.startsWith("application/xhtml+xml;"))) {
|
|
4480
|
-
const
|
|
4481
|
-
const html = await
|
|
4482
|
-
if (html.
|
|
4398
|
+
const MAX_HTML_SIZE = 1024 * 1024;
|
|
4399
|
+
const html = await response.text();
|
|
4400
|
+
if (html.length > MAX_HTML_SIZE) {
|
|
4483
4401
|
logger.warn("HTML response too large, skipping alternate link discovery: {url}", {
|
|
4484
4402
|
url: documentUrl,
|
|
4485
|
-
size: html.
|
|
4403
|
+
size: html.length
|
|
4486
4404
|
});
|
|
4487
|
-
|
|
4405
|
+
document = JSON.parse(html);
|
|
4488
4406
|
} else {
|
|
4489
4407
|
const tagPattern = /<(a|link)\s+([^>]*?)\s*\/?>/gi;
|
|
4490
4408
|
const attrPattern = /([a-z][a-z:_-]*)=(?:"([^"]*)"|'([^']*)'|([^\s>]+))/gi;
|
|
4491
4409
|
let tagMatch;
|
|
4492
|
-
while ((tagMatch = tagPattern.exec(html
|
|
4410
|
+
while ((tagMatch = tagPattern.exec(html)) !== null) {
|
|
4493
4411
|
const tagContent = tagMatch[2];
|
|
4494
4412
|
let attrMatch;
|
|
4495
4413
|
const attribs = {};
|
|
@@ -4506,12 +4424,7 @@ async function getRemoteDocument(url, response, fetch) {
|
|
|
4506
4424
|
return await fetch(new URL(attribs.href, docUrl).href);
|
|
4507
4425
|
}
|
|
4508
4426
|
}
|
|
4509
|
-
|
|
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
|
-
}
|
|
4427
|
+
document = JSON.parse(html);
|
|
4515
4428
|
}
|
|
4516
4429
|
} else document = await response.json();
|
|
4517
4430
|
logger.debug("Fetched document: {status} {url} {headers}", {
|
|
@@ -4622,12 +4535,6 @@ Object.defineProperty(exports, "getDocumentLoader", {
|
|
|
4622
4535
|
return getDocumentLoader;
|
|
4623
4536
|
}
|
|
4624
4537
|
});
|
|
4625
|
-
Object.defineProperty(exports, "getRemoteDocument", {
|
|
4626
|
-
enumerable: true,
|
|
4627
|
-
get: function() {
|
|
4628
|
-
return getRemoteDocument;
|
|
4629
|
-
}
|
|
4630
|
-
});
|
|
4631
4538
|
Object.defineProperty(exports, "preloadedContexts", {
|
|
4632
4539
|
enumerable: true,
|
|
4633
4540
|
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-mvgIWKI7.cjs");
|
|
3
|
+
const require_request = require("./request-SworbvLc.cjs");
|
|
4
|
+
const require_url = require("./url-C20FhC7p.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": "text/html; charset=utf-8",
|
|
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,27 +1399,6 @@ 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
|
-
});
|
|
1423
1402
|
esm_default.get("https://example.com/wrong-content-type", {
|
|
1424
1403
|
body: {
|
|
1425
1404
|
"@context": "https://www.w3.org/ns/activitystreams",
|
|
@@ -1429,7 +1408,7 @@ var esm_default = new class FetchMock {
|
|
|
1429
1408
|
},
|
|
1430
1409
|
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
1431
1410
|
});
|
|
1432
|
-
await t.test("
|
|
1411
|
+
await t.test("Wrong Content-Type", async () => {
|
|
1433
1412
|
(0, node_assert.deepStrictEqual)(await fetchDocumentLoader("https://example.com/wrong-content-type"), {
|
|
1434
1413
|
contextUrl: null,
|
|
1435
1414
|
documentUrl: "https://example.com/wrong-content-type",
|
|
@@ -1441,37 +1420,6 @@ var esm_default = new class FetchMock {
|
|
|
1441
1420
|
}
|
|
1442
1421
|
});
|
|
1443
1422
|
});
|
|
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
|
-
});
|
|
1475
1423
|
esm_default.get("https://example.com/404", { status: 404 });
|
|
1476
1424
|
await t.test("not ok", async () => {
|
|
1477
1425
|
await (0, node_assert.rejects)(() => fetchDocumentLoader("https://example.com/404"), require_request.FetchError, "HTTP 404: https://example.com/404");
|
|
@@ -1588,7 +1536,7 @@ var esm_default = new class FetchMock {
|
|
|
1588
1536
|
});
|
|
1589
1537
|
await t.test("ReDoS resistance (CVE-2025-68475)", async () => {
|
|
1590
1538
|
const start = performance.now();
|
|
1591
|
-
await (0, node_assert.rejects)(() => fetchDocumentLoader("https://example.com/redos"),
|
|
1539
|
+
await (0, node_assert.rejects)(() => fetchDocumentLoader("https://example.com/redos"), SyntaxError);
|
|
1592
1540
|
const elapsed = performance.now() - start;
|
|
1593
1541
|
(0, node_assert.ok)(elapsed < 1e3, `Potential ReDoS vulnerability detected: ${elapsed}ms (expected < 1000ms)`);
|
|
1594
1542
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as
|
|
2
|
-
import { t as FetchError } from "./request-
|
|
3
|
-
import { t as UrlError } from "./url-
|
|
1
|
+
import { n as preloadedContexts, t as getDocumentLoader } from "./docloader-C76ldE5C.mjs";
|
|
2
|
+
import { t as FetchError } from "./request-BEXkv1ul.mjs";
|
|
3
|
+
import { t as UrlError } from "./url-m9Qzxy-Y.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": "text/html; charset=utf-8",
|
|
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,27 +1418,6 @@ 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
|
-
});
|
|
1442
1421
|
esm_default.get("https://example.com/wrong-content-type", {
|
|
1443
1422
|
body: {
|
|
1444
1423
|
"@context": "https://www.w3.org/ns/activitystreams",
|
|
@@ -1448,7 +1427,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
1448
1427
|
},
|
|
1449
1428
|
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
1450
1429
|
});
|
|
1451
|
-
await t.test("
|
|
1430
|
+
await t.test("Wrong Content-Type", async () => {
|
|
1452
1431
|
deepStrictEqual(await fetchDocumentLoader("https://example.com/wrong-content-type"), {
|
|
1453
1432
|
contextUrl: null,
|
|
1454
1433
|
documentUrl: "https://example.com/wrong-content-type",
|
|
@@ -1460,37 +1439,6 @@ test("getDocumentLoader()", async (t) => {
|
|
|
1460
1439
|
}
|
|
1461
1440
|
});
|
|
1462
1441
|
});
|
|
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
|
-
});
|
|
1494
1442
|
esm_default.get("https://example.com/404", { status: 404 });
|
|
1495
1443
|
await t.test("not ok", async () => {
|
|
1496
1444
|
await rejects(() => fetchDocumentLoader("https://example.com/404"), FetchError, "HTTP 404: https://example.com/404");
|
|
@@ -1607,7 +1555,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
1607
1555
|
});
|
|
1608
1556
|
await t.test("ReDoS resistance (CVE-2025-68475)", async () => {
|
|
1609
1557
|
const start = performance.now();
|
|
1610
|
-
await rejects(() => fetchDocumentLoader("https://example.com/redos"),
|
|
1558
|
+
await rejects(() => fetchDocumentLoader("https://example.com/redos"), SyntaxError);
|
|
1611
1559
|
const elapsed = performance.now() - start;
|
|
1612
1560
|
ok(elapsed < 1e3, `Potential ReDoS vulnerability detected: ${elapsed}ms (expected < 1000ms)`);
|
|
1613
1561
|
});
|
|
@@ -45,11 +45,6 @@ const algorithms = {
|
|
|
45
45
|
},
|
|
46
46
|
"1.3.101.112": "Ed25519"
|
|
47
47
|
};
|
|
48
|
-
const DID_KEY_PREFIX = "did:key:";
|
|
49
|
-
const ED25519_PUBLIC_KEY_MULTICODEC = 237;
|
|
50
|
-
const ED25519_PUBLIC_KEY_LENGTH = 32;
|
|
51
|
-
const DID_KEY_PATTERN = /^did:key:([^/?#]+)$/;
|
|
52
|
-
const DID_KEY_VERIFICATION_METHOD_PATTERN = /^did:key:([^/?#]+)#([^/?#]+)$/;
|
|
53
48
|
/**
|
|
54
49
|
* Imports a PEM-SPKI formatted public key.
|
|
55
50
|
* @param pem The PEM-SPKI formatted public key.
|
|
@@ -111,69 +106,6 @@ const PKCS1_HEADER = /^\s*-----BEGIN\s+RSA\s+PUBLIC\s+KEY-----\s*\n/;
|
|
|
111
106
|
function importPem(pem) {
|
|
112
107
|
return PKCS1_HEADER.test(pem) ? importPkcs1(pem) : importSpki(pem);
|
|
113
108
|
}
|
|
114
|
-
function decodeEd25519DidKeyMultibase(multibaseKey) {
|
|
115
|
-
if (!multibaseKey.startsWith("z")) throw new TypeError("did:key must use base58-btc Multibase encoding.");
|
|
116
|
-
let decoded;
|
|
117
|
-
try {
|
|
118
|
-
decoded = decodeMultibase(multibaseKey);
|
|
119
|
-
} catch (error) {
|
|
120
|
-
throw new TypeError("Invalid did:key Multibase encoding.", { cause: error });
|
|
121
|
-
}
|
|
122
|
-
const { code } = getMulticodecPrefix(decoded);
|
|
123
|
-
if (code !== ED25519_PUBLIC_KEY_MULTICODEC) throw new TypeError("Unsupported did:key type: 0x" + code.toString(16));
|
|
124
|
-
const content = removeMulticodecPrefix(decoded);
|
|
125
|
-
if (content.length !== ED25519_PUBLIC_KEY_LENGTH) throw new TypeError("Invalid Ed25519 did:key length.");
|
|
126
|
-
return content;
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Imports an Ed25519 `did:key` DID.
|
|
130
|
-
*
|
|
131
|
-
* @param did The `did:key` DID.
|
|
132
|
-
* @returns The imported Ed25519 public key.
|
|
133
|
-
* @throws {TypeError} If the DID is malformed or uses an unsupported key type.
|
|
134
|
-
* @since 2.4.0
|
|
135
|
-
*/
|
|
136
|
-
async function importDidKey(did) {
|
|
137
|
-
const match = (did instanceof URL ? did.href : did).match(DID_KEY_PATTERN);
|
|
138
|
-
if (match == null) throw new TypeError("Invalid did:key DID.");
|
|
139
|
-
const content = decodeEd25519DidKeyMultibase(match[1]);
|
|
140
|
-
return await crypto.subtle.importKey("raw", content.slice(), "Ed25519", true, ["verify"]);
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Exports an Ed25519 public key as a `did:key` DID.
|
|
144
|
-
*
|
|
145
|
-
* @param key The Ed25519 public key.
|
|
146
|
-
* @returns The `did:key` DID.
|
|
147
|
-
* @throws {TypeError} If the key is invalid or unsupported.
|
|
148
|
-
* @since 2.4.0
|
|
149
|
-
*/
|
|
150
|
-
async function exportDidKey(key) {
|
|
151
|
-
if (key.algorithm.name !== "Ed25519") throw new TypeError("Unsupported key type: " + JSON.stringify(key.algorithm));
|
|
152
|
-
return DID_KEY_PREFIX + await exportMultibaseKey(key);
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* Parses an Ed25519 `did:key` verification method DID URL.
|
|
156
|
-
*
|
|
157
|
-
* @param didUrl The `did:key` DID URL.
|
|
158
|
-
* @returns The parsed verification method.
|
|
159
|
-
* @throws {TypeError} If the DID URL is malformed, unsupported, or its
|
|
160
|
-
* fragment does not identify the same key as the DID.
|
|
161
|
-
* @since 2.4.0
|
|
162
|
-
*/
|
|
163
|
-
async function parseDidKeyVerificationMethod(didUrl) {
|
|
164
|
-
const didUrlString = didUrl instanceof URL ? didUrl.href : didUrl;
|
|
165
|
-
const match = didUrlString.match(DID_KEY_VERIFICATION_METHOD_PATTERN);
|
|
166
|
-
if (match == null) throw new TypeError("Invalid did:key verification method.");
|
|
167
|
-
const [, publicKeyMultibase, fragment] = match;
|
|
168
|
-
if (publicKeyMultibase !== fragment) throw new TypeError("Invalid did:key verification method fragment.");
|
|
169
|
-
const publicKey = await importDidKey(DID_KEY_PREFIX + publicKeyMultibase);
|
|
170
|
-
return {
|
|
171
|
-
id: new URL(didUrlString),
|
|
172
|
-
controller: new URL(DID_KEY_PREFIX + publicKeyMultibase),
|
|
173
|
-
publicKeyMultibase,
|
|
174
|
-
publicKey
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
109
|
/**
|
|
178
110
|
* Imports a [Multibase]-encoded public key.
|
|
179
111
|
*
|
|
@@ -237,4 +169,4 @@ async function exportMultibaseKey(key) {
|
|
|
237
169
|
return new TextDecoder().decode(encoded);
|
|
238
170
|
}
|
|
239
171
|
//#endregion
|
|
240
|
-
export {
|
|
172
|
+
export { importPkcs1 as a, importJwk as c, importPem as i, exportSpki as n, importSpki as o, importMultibaseKey as r, exportJwk as s, exportMultibaseKey as t };
|
|
@@ -46,11 +46,6 @@ const algorithms = {
|
|
|
46
46
|
},
|
|
47
47
|
"1.3.101.112": "Ed25519"
|
|
48
48
|
};
|
|
49
|
-
const DID_KEY_PREFIX = "did:key:";
|
|
50
|
-
const ED25519_PUBLIC_KEY_MULTICODEC = 237;
|
|
51
|
-
const ED25519_PUBLIC_KEY_LENGTH = 32;
|
|
52
|
-
const DID_KEY_PATTERN = /^did:key:([^/?#]+)$/;
|
|
53
|
-
const DID_KEY_VERIFICATION_METHOD_PATTERN = /^did:key:([^/?#]+)#([^/?#]+)$/;
|
|
54
49
|
/**
|
|
55
50
|
* Imports a PEM-SPKI formatted public key.
|
|
56
51
|
* @param pem The PEM-SPKI formatted public key.
|
|
@@ -112,69 +107,6 @@ const PKCS1_HEADER = /^\s*-----BEGIN\s+RSA\s+PUBLIC\s+KEY-----\s*\n/;
|
|
|
112
107
|
function importPem(pem) {
|
|
113
108
|
return PKCS1_HEADER.test(pem) ? importPkcs1(pem) : importSpki(pem);
|
|
114
109
|
}
|
|
115
|
-
function decodeEd25519DidKeyMultibase(multibaseKey) {
|
|
116
|
-
if (!multibaseKey.startsWith("z")) throw new TypeError("did:key must use base58-btc Multibase encoding.");
|
|
117
|
-
let decoded;
|
|
118
|
-
try {
|
|
119
|
-
decoded = require_multibase.decodeMultibase(multibaseKey);
|
|
120
|
-
} catch (error) {
|
|
121
|
-
throw new TypeError("Invalid did:key Multibase encoding.", { cause: error });
|
|
122
|
-
}
|
|
123
|
-
const { code } = require_multicodec.getMulticodecPrefix(decoded);
|
|
124
|
-
if (code !== ED25519_PUBLIC_KEY_MULTICODEC) throw new TypeError("Unsupported did:key type: 0x" + code.toString(16));
|
|
125
|
-
const content = require_multicodec.removeMulticodecPrefix(decoded);
|
|
126
|
-
if (content.length !== ED25519_PUBLIC_KEY_LENGTH) throw new TypeError("Invalid Ed25519 did:key length.");
|
|
127
|
-
return content;
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Imports an Ed25519 `did:key` DID.
|
|
131
|
-
*
|
|
132
|
-
* @param did The `did:key` DID.
|
|
133
|
-
* @returns The imported Ed25519 public key.
|
|
134
|
-
* @throws {TypeError} If the DID is malformed or uses an unsupported key type.
|
|
135
|
-
* @since 2.4.0
|
|
136
|
-
*/
|
|
137
|
-
async function importDidKey(did) {
|
|
138
|
-
const match = (did instanceof URL ? did.href : did).match(DID_KEY_PATTERN);
|
|
139
|
-
if (match == null) throw new TypeError("Invalid did:key DID.");
|
|
140
|
-
const content = decodeEd25519DidKeyMultibase(match[1]);
|
|
141
|
-
return await crypto.subtle.importKey("raw", content.slice(), "Ed25519", true, ["verify"]);
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Exports an Ed25519 public key as a `did:key` DID.
|
|
145
|
-
*
|
|
146
|
-
* @param key The Ed25519 public key.
|
|
147
|
-
* @returns The `did:key` DID.
|
|
148
|
-
* @throws {TypeError} If the key is invalid or unsupported.
|
|
149
|
-
* @since 2.4.0
|
|
150
|
-
*/
|
|
151
|
-
async function exportDidKey(key) {
|
|
152
|
-
if (key.algorithm.name !== "Ed25519") throw new TypeError("Unsupported key type: " + JSON.stringify(key.algorithm));
|
|
153
|
-
return DID_KEY_PREFIX + await exportMultibaseKey(key);
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Parses an Ed25519 `did:key` verification method DID URL.
|
|
157
|
-
*
|
|
158
|
-
* @param didUrl The `did:key` DID URL.
|
|
159
|
-
* @returns The parsed verification method.
|
|
160
|
-
* @throws {TypeError} If the DID URL is malformed, unsupported, or its
|
|
161
|
-
* fragment does not identify the same key as the DID.
|
|
162
|
-
* @since 2.4.0
|
|
163
|
-
*/
|
|
164
|
-
async function parseDidKeyVerificationMethod(didUrl) {
|
|
165
|
-
const didUrlString = didUrl instanceof URL ? didUrl.href : didUrl;
|
|
166
|
-
const match = didUrlString.match(DID_KEY_VERIFICATION_METHOD_PATTERN);
|
|
167
|
-
if (match == null) throw new TypeError("Invalid did:key verification method.");
|
|
168
|
-
const [, publicKeyMultibase, fragment] = match;
|
|
169
|
-
if (publicKeyMultibase !== fragment) throw new TypeError("Invalid did:key verification method fragment.");
|
|
170
|
-
const publicKey = await importDidKey(DID_KEY_PREFIX + publicKeyMultibase);
|
|
171
|
-
return {
|
|
172
|
-
id: new URL(didUrlString),
|
|
173
|
-
controller: new URL(DID_KEY_PREFIX + publicKeyMultibase),
|
|
174
|
-
publicKeyMultibase,
|
|
175
|
-
publicKey
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
110
|
/**
|
|
179
111
|
* Imports a [Multibase]-encoded public key.
|
|
180
112
|
*
|
|
@@ -238,12 +170,6 @@ async function exportMultibaseKey(key) {
|
|
|
238
170
|
return new TextDecoder().decode(encoded);
|
|
239
171
|
}
|
|
240
172
|
//#endregion
|
|
241
|
-
Object.defineProperty(exports, "exportDidKey", {
|
|
242
|
-
enumerable: true,
|
|
243
|
-
get: function() {
|
|
244
|
-
return exportDidKey;
|
|
245
|
-
}
|
|
246
|
-
});
|
|
247
173
|
Object.defineProperty(exports, "exportJwk", {
|
|
248
174
|
enumerable: true,
|
|
249
175
|
get: function() {
|
|
@@ -262,12 +188,6 @@ Object.defineProperty(exports, "exportSpki", {
|
|
|
262
188
|
return exportSpki;
|
|
263
189
|
}
|
|
264
190
|
});
|
|
265
|
-
Object.defineProperty(exports, "importDidKey", {
|
|
266
|
-
enumerable: true,
|
|
267
|
-
get: function() {
|
|
268
|
-
return importDidKey;
|
|
269
|
-
}
|
|
270
|
-
});
|
|
271
191
|
Object.defineProperty(exports, "importJwk", {
|
|
272
192
|
enumerable: true,
|
|
273
193
|
get: function() {
|
|
@@ -298,9 +218,3 @@ Object.defineProperty(exports, "importSpki", {
|
|
|
298
218
|
return importSpki;
|
|
299
219
|
}
|
|
300
220
|
});
|
|
301
|
-
Object.defineProperty(exports, "parseDidKeyVerificationMethod", {
|
|
302
|
-
enumerable: true,
|
|
303
|
-
get: function() {
|
|
304
|
-
return parseDidKeyVerificationMethod;
|
|
305
|
-
}
|
|
306
|
-
});
|
package/dist/tests/key.test.cjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
require("./chunk-C2EiDwsr.cjs");
|
|
2
|
-
const
|
|
3
|
-
const require_key = require("./key-_wXwomh_.cjs");
|
|
2
|
+
const require_key = require("./key-pMmqUKuo.cjs");
|
|
4
3
|
const require_multibase = require("./multibase-Bz_UUDtL.cjs");
|
|
5
4
|
let node_assert = require("node:assert");
|
|
6
5
|
let node_test = require("node:test");
|
|
@@ -72,50 +71,4 @@ const ed25519Multibase = "z6MksHj1MJnidCtDiyYW9ugNFftoX9fLK4bornTxmMZ6X7vq";
|
|
|
72
71
|
x: "Lm_M42cB3HkUiODQsXRcweM6TByfzEHGO9ND274JcOY"
|
|
73
72
|
}, "public")), "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK");
|
|
74
73
|
});
|
|
75
|
-
(0, node_test.test)("exportDidKey() and importDidKey()", async () => {
|
|
76
|
-
const did = await require_key.exportDidKey(await require_key.importJwk(ed25519Jwk, "public"));
|
|
77
|
-
(0, node_assert.deepStrictEqual)(did, `did:key:${ed25519Multibase}`);
|
|
78
|
-
(0, node_assert.deepStrictEqual)(await require_key.exportJwk(await require_key.importDidKey(did)), ed25519Jwk);
|
|
79
|
-
(0, node_assert.deepStrictEqual)(await require_key.exportJwk(await require_key.importDidKey(new URL(did))), ed25519Jwk);
|
|
80
|
-
const rsaKey = await require_key.importJwk(rsaJwk, "public");
|
|
81
|
-
await (0, node_assert.rejects)(() => require_key.exportDidKey(rsaKey), TypeError);
|
|
82
|
-
await (0, node_assert.rejects)(() => require_key.importDidKey(`did:key:${rsaMultibase}`), /* @__PURE__ */ new TypeError("Unsupported did:key type: 0x1205"));
|
|
83
|
-
});
|
|
84
|
-
(0, node_test.test)("parseDidKeyVerificationMethod()", async () => {
|
|
85
|
-
const verificationMethod = `did:key:${ed25519Multibase}#${ed25519Multibase}`;
|
|
86
|
-
const parsed = await require_key.parseDidKeyVerificationMethod(verificationMethod);
|
|
87
|
-
(0, node_assert.deepStrictEqual)(parsed.id, new URL(verificationMethod));
|
|
88
|
-
(0, node_assert.deepStrictEqual)(parsed.controller, new URL(`did:key:${ed25519Multibase}`));
|
|
89
|
-
(0, node_assert.deepStrictEqual)(parsed.publicKeyMultibase, ed25519Multibase);
|
|
90
|
-
(0, node_assert.deepStrictEqual)(await require_key.exportJwk(parsed.publicKey), ed25519Jwk);
|
|
91
|
-
});
|
|
92
|
-
(0, node_test.test)("did:key helpers reject malformed values", async () => {
|
|
93
|
-
const ed25519Key = await require_key.importJwk(ed25519Jwk, "public");
|
|
94
|
-
const raw = new Uint8Array(await crypto.subtle.exportKey("raw", ed25519Key));
|
|
95
|
-
const shortEd25519 = new TextDecoder().decode(require_multibase.encodeMultibase("base58btc", require_multicodec.addMulticodecPrefix(237, raw.slice(0, 31))));
|
|
96
|
-
const base64UrlEd25519 = new TextDecoder().decode(require_multibase.encodeMultibase("base64url", require_multicodec.addMulticodecPrefix(237, raw)));
|
|
97
|
-
const malformedMulticodec = new TextDecoder().decode(require_multibase.encodeMultibase("base58btc", Uint8Array.from([128])));
|
|
98
|
-
for (const value of [
|
|
99
|
-
"did:web:example.com",
|
|
100
|
-
"did:key:",
|
|
101
|
-
`did:key:${ed25519Multibase}/path`,
|
|
102
|
-
`did:key:${ed25519Multibase}?query`,
|
|
103
|
-
`did:key:${ed25519Multibase}#${ed25519Multibase}`,
|
|
104
|
-
"did:key:z0",
|
|
105
|
-
`did:key:${base64UrlEd25519}`,
|
|
106
|
-
`did:key:${shortEd25519}`,
|
|
107
|
-
`did:key:${malformedMulticodec}`
|
|
108
|
-
]) await (0, node_assert.rejects)(() => require_key.importDidKey(value), TypeError);
|
|
109
|
-
for (const value of [
|
|
110
|
-
`did:key:${ed25519Multibase}`,
|
|
111
|
-
`did:key:${ed25519Multibase}#`,
|
|
112
|
-
`did:key:${ed25519Multibase}#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK`,
|
|
113
|
-
`did:key:${ed25519Multibase}#${base64UrlEd25519}`,
|
|
114
|
-
"did:key:z0#z0",
|
|
115
|
-
`did:key:${rsaMultibase}#${rsaMultibase}`,
|
|
116
|
-
`did:key:${shortEd25519}#${shortEd25519}`,
|
|
117
|
-
`did:key:${malformedMulticodec}#${malformedMulticodec}`,
|
|
118
|
-
"https://example.com/key"
|
|
119
|
-
]) await (0, node_assert.rejects)(() => require_key.parseDidKeyVerificationMethod(value), TypeError);
|
|
120
|
-
});
|
|
121
74
|
//#endregion
|