@fedify/vocab-runtime 2.0.0-dev.1908 → 2.0.0-dev.206

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.
Files changed (46) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +2 -1
  3. package/deno.json +7 -1
  4. package/dist/chunk-DWy1uDak.cjs +39 -0
  5. package/dist/docloader.test.cjs +5851 -0
  6. package/dist/docloader.test.d.cts +1 -0
  7. package/dist/docloader.test.d.ts +1 -0
  8. package/dist/docloader.test.js +5877 -0
  9. package/dist/key.test.cjs +272 -0
  10. package/dist/key.test.d.cts +1 -0
  11. package/dist/key.test.d.ts +1 -0
  12. package/dist/key.test.js +271 -0
  13. package/dist/langstr.test.cjs +51 -0
  14. package/dist/langstr.test.d.cts +1 -0
  15. package/dist/langstr.test.d.ts +1 -0
  16. package/dist/langstr.test.js +50 -0
  17. package/dist/link-CdFPEo9O.cjs +189 -0
  18. package/dist/link-Ck2yj4dH.js +183 -0
  19. package/dist/link.test.cjs +56 -0
  20. package/dist/link.test.d.cts +1 -0
  21. package/dist/link.test.d.ts +1 -0
  22. package/dist/link.test.js +55 -0
  23. package/dist/mod.cjs +102 -63
  24. package/dist/mod.js +102 -63
  25. package/dist/multibase/multibase.test.cjs +346 -0
  26. package/dist/multibase/multibase.test.d.cts +1 -0
  27. package/dist/multibase/multibase.test.d.ts +1 -0
  28. package/dist/multibase/multibase.test.js +345 -0
  29. package/dist/multibase-BFbBiaPE.cjs +347 -0
  30. package/dist/multibase-DStmqni9.js +311 -0
  31. package/dist/request-CHSKwWMf.cjs +138 -0
  32. package/dist/request-CXamqU2m.js +108 -0
  33. package/dist/request.test.cjs +44 -0
  34. package/dist/request.test.d.cts +1 -0
  35. package/dist/request.test.d.ts +1 -0
  36. package/dist/request.test.js +43 -0
  37. package/dist/url-C5Vs9nYh.cjs +93 -0
  38. package/dist/url-fW_DHbih.js +63 -0
  39. package/dist/url.test.cjs +37 -0
  40. package/dist/url.test.d.cts +1 -0
  41. package/dist/url.test.d.ts +1 -0
  42. package/dist/url.test.js +36 -0
  43. package/package.json +4 -3
  44. package/src/docloader.test.ts +29 -1
  45. package/src/docloader.ts +101 -45
  46. package/tsdown.config.ts +18 -7
@@ -0,0 +1,138 @@
1
+ const require_chunk = require('./chunk-DWy1uDak.cjs');
2
+ const node_process = require_chunk.__toESM(require("node:process"));
3
+
4
+ //#region deno.json
5
+ var name = "@fedify/vocab-runtime";
6
+ var version = "2.0.0-dev.206+fa815928";
7
+ var license = "MIT";
8
+ var exports$1 = { ".": "./src/mod.ts" };
9
+ var description = "Runtime library for @fedify/vocab";
10
+ var author = {
11
+ "name": "Hong Minhee",
12
+ "email": "hong@minhee.org",
13
+ "url": "https://hongminhee.org/"
14
+ };
15
+ var imports = {
16
+ "@multiformats/base-x": "npm:@multiformats/base-x@^4.0.1",
17
+ "asn1js": "npm:asn1js@^3.0.6",
18
+ "byte-encodings": "npm:byte-encodings@^1.0.11",
19
+ "fetch-mock": "npm:fetch-mock@^12.5.4",
20
+ "multicodec": "npm:multicodec@^3.2.1",
21
+ "pkijs": "npm:pkijs@^3.2.5"
22
+ };
23
+ var exclude = ["dist", "node_modules"];
24
+ var publish = { "exclude": ["**/*.test.ts"] };
25
+ var tasks = {
26
+ "check": "deno fmt --check && deno lint && deno check src/*.ts",
27
+ "test": "deno test"
28
+ };
29
+ var deno_default = {
30
+ name,
31
+ version,
32
+ license,
33
+ exports: exports$1,
34
+ description,
35
+ author,
36
+ imports,
37
+ exclude,
38
+ publish,
39
+ tasks
40
+ };
41
+
42
+ //#endregion
43
+ //#region src/request.ts
44
+ /**
45
+ * Error thrown when fetching a JSON-LD document failed.
46
+ */
47
+ var FetchError = class extends Error {
48
+ /**
49
+ * The URL that failed to fetch.
50
+ */
51
+ url;
52
+ /**
53
+ * Constructs a new `FetchError`.
54
+ *
55
+ * @param url The URL that failed to fetch.
56
+ * @param message Error message.
57
+ */
58
+ constructor(url, message) {
59
+ super(message == null ? url.toString() : `${url}: ${message}`);
60
+ this.name = "FetchError";
61
+ this.url = typeof url === "string" ? new URL(url) : url;
62
+ }
63
+ };
64
+ /**
65
+ * Creates a request for the given URL.
66
+ * @param url The URL to create the request for.
67
+ * @param options The options for the request.
68
+ * @returns The created request.
69
+ * @internal
70
+ */
71
+ function createActivityPubRequest(url, options = {}) {
72
+ return new Request(url, {
73
+ headers: {
74
+ Accept: "application/activity+json, application/ld+json",
75
+ "User-Agent": typeof options.userAgent === "string" ? options.userAgent : getUserAgent(options.userAgent)
76
+ },
77
+ redirect: "manual"
78
+ });
79
+ }
80
+ /**
81
+ * Gets the user agent string for the given application and URL.
82
+ * @param options The options for making the user agent string.
83
+ * @returns The user agent string.
84
+ * @since 1.3.0
85
+ */
86
+ function getUserAgent({ software, url } = {}) {
87
+ const fedify = `Fedify/${deno_default.version}`;
88
+ const runtime = globalThis.Deno?.version?.deno != null ? `Deno/${Deno.version.deno}` : globalThis.process?.versions?.bun != null ? `Bun/${node_process.default.versions.bun}` : "navigator" in globalThis && navigator.userAgent === "Cloudflare-Workers" ? navigator.userAgent : globalThis.process?.versions?.node != null ? `Node.js/${node_process.default.versions.node}` : null;
89
+ const userAgent = software == null ? [fedify] : [software, fedify];
90
+ if (runtime != null) userAgent.push(runtime);
91
+ if (url != null) userAgent.push(`+${url.toString()}`);
92
+ const first = userAgent.shift();
93
+ return `${first} (${userAgent.join("; ")})`;
94
+ }
95
+ /**
96
+ * Logs the request.
97
+ * @param request The request to log.
98
+ * @internal
99
+ */
100
+ function logRequest(logger, request) {
101
+ logger.debug("Fetching document: {method} {url} {headers}", {
102
+ method: request.method,
103
+ url: request.url,
104
+ headers: Object.fromEntries(request.headers.entries())
105
+ });
106
+ }
107
+
108
+ //#endregion
109
+ Object.defineProperty(exports, 'FetchError', {
110
+ enumerable: true,
111
+ get: function () {
112
+ return FetchError;
113
+ }
114
+ });
115
+ Object.defineProperty(exports, 'createActivityPubRequest', {
116
+ enumerable: true,
117
+ get: function () {
118
+ return createActivityPubRequest;
119
+ }
120
+ });
121
+ Object.defineProperty(exports, 'deno_default', {
122
+ enumerable: true,
123
+ get: function () {
124
+ return deno_default;
125
+ }
126
+ });
127
+ Object.defineProperty(exports, 'getUserAgent', {
128
+ enumerable: true,
129
+ get: function () {
130
+ return getUserAgent;
131
+ }
132
+ });
133
+ Object.defineProperty(exports, 'logRequest', {
134
+ enumerable: true,
135
+ get: function () {
136
+ return logRequest;
137
+ }
138
+ });
@@ -0,0 +1,108 @@
1
+ import process from "node:process";
2
+
3
+ //#region deno.json
4
+ var name = "@fedify/vocab-runtime";
5
+ var version = "2.0.0-dev.206+fa815928";
6
+ var license = "MIT";
7
+ var exports = { ".": "./src/mod.ts" };
8
+ var description = "Runtime library for @fedify/vocab";
9
+ var author = {
10
+ "name": "Hong Minhee",
11
+ "email": "hong@minhee.org",
12
+ "url": "https://hongminhee.org/"
13
+ };
14
+ var imports = {
15
+ "@multiformats/base-x": "npm:@multiformats/base-x@^4.0.1",
16
+ "asn1js": "npm:asn1js@^3.0.6",
17
+ "byte-encodings": "npm:byte-encodings@^1.0.11",
18
+ "fetch-mock": "npm:fetch-mock@^12.5.4",
19
+ "multicodec": "npm:multicodec@^3.2.1",
20
+ "pkijs": "npm:pkijs@^3.2.5"
21
+ };
22
+ var exclude = ["dist", "node_modules"];
23
+ var publish = { "exclude": ["**/*.test.ts"] };
24
+ var tasks = {
25
+ "check": "deno fmt --check && deno lint && deno check src/*.ts",
26
+ "test": "deno test"
27
+ };
28
+ var deno_default = {
29
+ name,
30
+ version,
31
+ license,
32
+ exports,
33
+ description,
34
+ author,
35
+ imports,
36
+ exclude,
37
+ publish,
38
+ tasks
39
+ };
40
+
41
+ //#endregion
42
+ //#region src/request.ts
43
+ /**
44
+ * Error thrown when fetching a JSON-LD document failed.
45
+ */
46
+ var FetchError = class extends Error {
47
+ /**
48
+ * The URL that failed to fetch.
49
+ */
50
+ url;
51
+ /**
52
+ * Constructs a new `FetchError`.
53
+ *
54
+ * @param url The URL that failed to fetch.
55
+ * @param message Error message.
56
+ */
57
+ constructor(url, message) {
58
+ super(message == null ? url.toString() : `${url}: ${message}`);
59
+ this.name = "FetchError";
60
+ this.url = typeof url === "string" ? new URL(url) : url;
61
+ }
62
+ };
63
+ /**
64
+ * Creates a request for the given URL.
65
+ * @param url The URL to create the request for.
66
+ * @param options The options for the request.
67
+ * @returns The created request.
68
+ * @internal
69
+ */
70
+ function createActivityPubRequest(url, options = {}) {
71
+ return new Request(url, {
72
+ headers: {
73
+ Accept: "application/activity+json, application/ld+json",
74
+ "User-Agent": typeof options.userAgent === "string" ? options.userAgent : getUserAgent(options.userAgent)
75
+ },
76
+ redirect: "manual"
77
+ });
78
+ }
79
+ /**
80
+ * Gets the user agent string for the given application and URL.
81
+ * @param options The options for making the user agent string.
82
+ * @returns The user agent string.
83
+ * @since 1.3.0
84
+ */
85
+ function getUserAgent({ software, url } = {}) {
86
+ const fedify = `Fedify/${deno_default.version}`;
87
+ const runtime = globalThis.Deno?.version?.deno != null ? `Deno/${Deno.version.deno}` : globalThis.process?.versions?.bun != null ? `Bun/${process.versions.bun}` : "navigator" in globalThis && navigator.userAgent === "Cloudflare-Workers" ? navigator.userAgent : globalThis.process?.versions?.node != null ? `Node.js/${process.versions.node}` : null;
88
+ const userAgent = software == null ? [fedify] : [software, fedify];
89
+ if (runtime != null) userAgent.push(runtime);
90
+ if (url != null) userAgent.push(`+${url.toString()}`);
91
+ const first = userAgent.shift();
92
+ return `${first} (${userAgent.join("; ")})`;
93
+ }
94
+ /**
95
+ * Logs the request.
96
+ * @param request The request to log.
97
+ * @internal
98
+ */
99
+ function logRequest(logger, request) {
100
+ logger.debug("Fetching document: {method} {url} {headers}", {
101
+ method: request.method,
102
+ url: request.url,
103
+ headers: Object.fromEntries(request.headers.entries())
104
+ });
105
+ }
106
+
107
+ //#endregion
108
+ export { FetchError, createActivityPubRequest, deno_default, getUserAgent, logRequest };
@@ -0,0 +1,44 @@
1
+ const require_chunk = require('./chunk-DWy1uDak.cjs');
2
+ const require_request = require('./request-CHSKwWMf.cjs');
3
+ const node_assert = require_chunk.__toESM(require("node:assert"));
4
+ const node_test = require_chunk.__toESM(require("node:test"));
5
+ const node_process = require_chunk.__toESM(require("node:process"));
6
+
7
+ //#region src/request.test.ts
8
+ (0, node_test.test)("getUserAgent()", () => {
9
+ if ("Deno" in globalThis) {
10
+ (0, node_assert.deepStrictEqual)(require_request.getUserAgent(), `Fedify/${require_request.deno_default.version} (Deno/${Deno.version.deno})`);
11
+ (0, node_assert.deepStrictEqual)(require_request.getUserAgent({ software: "MyApp/1.0.0" }), `MyApp/1.0.0 (Fedify/${require_request.deno_default.version}; Deno/${Deno.version.deno})`);
12
+ (0, node_assert.deepStrictEqual)(require_request.getUserAgent({ url: "https://example.com/" }), `Fedify/${require_request.deno_default.version} (Deno/${Deno.version.deno}; +https://example.com/)`);
13
+ (0, node_assert.deepStrictEqual)(require_request.getUserAgent({
14
+ software: "MyApp/1.0.0",
15
+ url: new URL("https://example.com/")
16
+ }), `MyApp/1.0.0 (Fedify/${require_request.deno_default.version}; Deno/${Deno.version.deno}; +https://example.com/)`);
17
+ } else if ("Bun" in globalThis) {
18
+ (0, node_assert.deepStrictEqual)(require_request.getUserAgent(), `Fedify/${require_request.deno_default.version} (Bun/${Bun.version})`);
19
+ (0, node_assert.deepStrictEqual)(require_request.getUserAgent({ software: "MyApp/1.0.0" }), `MyApp/1.0.0 (Fedify/${require_request.deno_default.version}; Bun/${Bun.version})`);
20
+ (0, node_assert.deepStrictEqual)(require_request.getUserAgent({ url: "https://example.com/" }), `Fedify/${require_request.deno_default.version} (Bun/${Bun.version}; +https://example.com/)`);
21
+ (0, node_assert.deepStrictEqual)(require_request.getUserAgent({
22
+ software: "MyApp/1.0.0",
23
+ url: new URL("https://example.com/")
24
+ }), `MyApp/1.0.0 (Fedify/${require_request.deno_default.version}; Bun/${Bun.version}; +https://example.com/)`);
25
+ } else if (navigator.userAgent === "Cloudflare-Workers") {
26
+ (0, node_assert.deepStrictEqual)(require_request.getUserAgent(), `Fedify/${require_request.deno_default.version} (Cloudflare-Workers)`);
27
+ (0, node_assert.deepStrictEqual)(require_request.getUserAgent({ software: "MyApp/1.0.0" }), `MyApp/1.0.0 (Fedify/${require_request.deno_default.version}; Cloudflare-Workers)`);
28
+ (0, node_assert.deepStrictEqual)(require_request.getUserAgent({ url: "https://example.com/" }), `Fedify/${require_request.deno_default.version} (Cloudflare-Workers; +https://example.com/)`);
29
+ (0, node_assert.deepStrictEqual)(require_request.getUserAgent({
30
+ software: "MyApp/1.0.0",
31
+ url: new URL("https://example.com/")
32
+ }), `MyApp/1.0.0 (Fedify/${require_request.deno_default.version}; Cloudflare-Workers; +https://example.com/)`);
33
+ } else {
34
+ (0, node_assert.deepStrictEqual)(require_request.getUserAgent(), `Fedify/${require_request.deno_default.version} (Node.js/${node_process.default.versions.node})`);
35
+ (0, node_assert.deepStrictEqual)(require_request.getUserAgent({ software: "MyApp/1.0.0" }), `MyApp/1.0.0 (Fedify/${require_request.deno_default.version}; Node.js/${node_process.default.versions.node})`);
36
+ (0, node_assert.deepStrictEqual)(require_request.getUserAgent({ url: "https://example.com/" }), `Fedify/${require_request.deno_default.version} (Node.js/${node_process.default.versions.node}; +https://example.com/)`);
37
+ (0, node_assert.deepStrictEqual)(require_request.getUserAgent({
38
+ software: "MyApp/1.0.0",
39
+ url: new URL("https://example.com/")
40
+ }), `MyApp/1.0.0 (Fedify/${require_request.deno_default.version}; Node.js/${node_process.default.versions.node}; +https://example.com/)`);
41
+ }
42
+ });
43
+
44
+ //#endregion
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,43 @@
1
+ import { deno_default, getUserAgent } from "./request-CXamqU2m.js";
2
+ import { deepStrictEqual } from "node:assert";
3
+ import { test } from "node:test";
4
+ import process from "node:process";
5
+
6
+ //#region src/request.test.ts
7
+ test("getUserAgent()", () => {
8
+ if ("Deno" in globalThis) {
9
+ deepStrictEqual(getUserAgent(), `Fedify/${deno_default.version} (Deno/${Deno.version.deno})`);
10
+ deepStrictEqual(getUserAgent({ software: "MyApp/1.0.0" }), `MyApp/1.0.0 (Fedify/${deno_default.version}; Deno/${Deno.version.deno})`);
11
+ deepStrictEqual(getUserAgent({ url: "https://example.com/" }), `Fedify/${deno_default.version} (Deno/${Deno.version.deno}; +https://example.com/)`);
12
+ deepStrictEqual(getUserAgent({
13
+ software: "MyApp/1.0.0",
14
+ url: new URL("https://example.com/")
15
+ }), `MyApp/1.0.0 (Fedify/${deno_default.version}; Deno/${Deno.version.deno}; +https://example.com/)`);
16
+ } else if ("Bun" in globalThis) {
17
+ deepStrictEqual(getUserAgent(), `Fedify/${deno_default.version} (Bun/${Bun.version})`);
18
+ deepStrictEqual(getUserAgent({ software: "MyApp/1.0.0" }), `MyApp/1.0.0 (Fedify/${deno_default.version}; Bun/${Bun.version})`);
19
+ deepStrictEqual(getUserAgent({ url: "https://example.com/" }), `Fedify/${deno_default.version} (Bun/${Bun.version}; +https://example.com/)`);
20
+ deepStrictEqual(getUserAgent({
21
+ software: "MyApp/1.0.0",
22
+ url: new URL("https://example.com/")
23
+ }), `MyApp/1.0.0 (Fedify/${deno_default.version}; Bun/${Bun.version}; +https://example.com/)`);
24
+ } else if (navigator.userAgent === "Cloudflare-Workers") {
25
+ deepStrictEqual(getUserAgent(), `Fedify/${deno_default.version} (Cloudflare-Workers)`);
26
+ deepStrictEqual(getUserAgent({ software: "MyApp/1.0.0" }), `MyApp/1.0.0 (Fedify/${deno_default.version}; Cloudflare-Workers)`);
27
+ deepStrictEqual(getUserAgent({ url: "https://example.com/" }), `Fedify/${deno_default.version} (Cloudflare-Workers; +https://example.com/)`);
28
+ deepStrictEqual(getUserAgent({
29
+ software: "MyApp/1.0.0",
30
+ url: new URL("https://example.com/")
31
+ }), `MyApp/1.0.0 (Fedify/${deno_default.version}; Cloudflare-Workers; +https://example.com/)`);
32
+ } else {
33
+ deepStrictEqual(getUserAgent(), `Fedify/${deno_default.version} (Node.js/${process.versions.node})`);
34
+ deepStrictEqual(getUserAgent({ software: "MyApp/1.0.0" }), `MyApp/1.0.0 (Fedify/${deno_default.version}; Node.js/${process.versions.node})`);
35
+ deepStrictEqual(getUserAgent({ url: "https://example.com/" }), `Fedify/${deno_default.version} (Node.js/${process.versions.node}; +https://example.com/)`);
36
+ deepStrictEqual(getUserAgent({
37
+ software: "MyApp/1.0.0",
38
+ url: new URL("https://example.com/")
39
+ }), `MyApp/1.0.0 (Fedify/${deno_default.version}; Node.js/${process.versions.node}; +https://example.com/)`);
40
+ }
41
+ });
42
+
43
+ //#endregion
@@ -0,0 +1,93 @@
1
+ const require_chunk = require('./chunk-DWy1uDak.cjs');
2
+ const node_dns_promises = require_chunk.__toESM(require("node:dns/promises"));
3
+ const node_net = require_chunk.__toESM(require("node:net"));
4
+
5
+ //#region src/url.ts
6
+ var UrlError = class extends Error {
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = "UrlError";
10
+ }
11
+ };
12
+ /**
13
+ * Validates a URL to prevent SSRF attacks.
14
+ */
15
+ async function validatePublicUrl(url) {
16
+ const parsed = new URL(url);
17
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new UrlError(`Unsupported protocol: ${parsed.protocol}`);
18
+ let hostname = parsed.hostname;
19
+ if (hostname.startsWith("[") && hostname.endsWith("]")) hostname = hostname.substring(1, hostname.length - 2);
20
+ if (hostname === "localhost") throw new UrlError("Localhost is not allowed");
21
+ if ("Deno" in globalThis && !(0, node_net.isIP)(hostname)) {
22
+ const netPermission = await Deno.permissions.query({ name: "net" });
23
+ if (netPermission.state !== "granted") return;
24
+ }
25
+ if ("Bun" in globalThis) {
26
+ if (hostname === "example.com" || hostname.endsWith(".example.com")) return;
27
+ else if (hostname === "fedify-test.internal") throw new UrlError("Invalid or private address: fedify-test.internal");
28
+ }
29
+ let addresses;
30
+ try {
31
+ addresses = await (0, node_dns_promises.lookup)(hostname, { all: true });
32
+ } catch {
33
+ addresses = [];
34
+ }
35
+ for (const { address, family } of addresses) if (family === 4 && !isValidPublicIPv4Address(address) || family === 6 && !isValidPublicIPv6Address(address) || family < 4 || family === 5 || family > 6) throw new UrlError(`Invalid or private address: ${address}`);
36
+ }
37
+ function isValidPublicIPv4Address(address) {
38
+ const parts = address.split(".");
39
+ const first = parseInt(parts[0]);
40
+ if (first === 0 || first === 10 || first === 127) return false;
41
+ const second = parseInt(parts[1]);
42
+ if (first === 169 && second === 254) return false;
43
+ if (first === 172 && second >= 16 && second <= 31) return false;
44
+ if (first === 192 && second === 168) return false;
45
+ return true;
46
+ }
47
+ function isValidPublicIPv6Address(address) {
48
+ address = expandIPv6Address(address);
49
+ if (address.at(4) !== ":") return false;
50
+ const firstWord = parseInt(address.substring(0, 4), 16);
51
+ return !(firstWord >= 64512 && firstWord <= 65023 || firstWord >= 65152 && firstWord <= 65215 || firstWord === 0 || firstWord >= 65280);
52
+ }
53
+ function expandIPv6Address(address) {
54
+ address = address.toLowerCase();
55
+ if (address === "::") return "0000:0000:0000:0000:0000:0000:0000:0000";
56
+ if (address.startsWith("::")) address = "0000" + address;
57
+ if (address.endsWith("::")) address = address + "0000";
58
+ address = address.replace("::", ":0000".repeat(8 - (address.match(/:/g) || []).length) + ":");
59
+ const parts = address.split(":");
60
+ return parts.map((part) => part.padStart(4, "0")).join(":");
61
+ }
62
+
63
+ //#endregion
64
+ Object.defineProperty(exports, 'UrlError', {
65
+ enumerable: true,
66
+ get: function () {
67
+ return UrlError;
68
+ }
69
+ });
70
+ Object.defineProperty(exports, 'expandIPv6Address', {
71
+ enumerable: true,
72
+ get: function () {
73
+ return expandIPv6Address;
74
+ }
75
+ });
76
+ Object.defineProperty(exports, 'isValidPublicIPv4Address', {
77
+ enumerable: true,
78
+ get: function () {
79
+ return isValidPublicIPv4Address;
80
+ }
81
+ });
82
+ Object.defineProperty(exports, 'isValidPublicIPv6Address', {
83
+ enumerable: true,
84
+ get: function () {
85
+ return isValidPublicIPv6Address;
86
+ }
87
+ });
88
+ Object.defineProperty(exports, 'validatePublicUrl', {
89
+ enumerable: true,
90
+ get: function () {
91
+ return validatePublicUrl;
92
+ }
93
+ });
@@ -0,0 +1,63 @@
1
+ import { lookup } from "node:dns/promises";
2
+ import { isIP } from "node:net";
3
+
4
+ //#region src/url.ts
5
+ var UrlError = class extends Error {
6
+ constructor(message) {
7
+ super(message);
8
+ this.name = "UrlError";
9
+ }
10
+ };
11
+ /**
12
+ * Validates a URL to prevent SSRF attacks.
13
+ */
14
+ async function validatePublicUrl(url) {
15
+ const parsed = new URL(url);
16
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new UrlError(`Unsupported protocol: ${parsed.protocol}`);
17
+ let hostname = parsed.hostname;
18
+ if (hostname.startsWith("[") && hostname.endsWith("]")) hostname = hostname.substring(1, hostname.length - 2);
19
+ if (hostname === "localhost") throw new UrlError("Localhost is not allowed");
20
+ if ("Deno" in globalThis && !isIP(hostname)) {
21
+ const netPermission = await Deno.permissions.query({ name: "net" });
22
+ if (netPermission.state !== "granted") return;
23
+ }
24
+ if ("Bun" in globalThis) {
25
+ if (hostname === "example.com" || hostname.endsWith(".example.com")) return;
26
+ else if (hostname === "fedify-test.internal") throw new UrlError("Invalid or private address: fedify-test.internal");
27
+ }
28
+ let addresses;
29
+ try {
30
+ addresses = await lookup(hostname, { all: true });
31
+ } catch {
32
+ addresses = [];
33
+ }
34
+ for (const { address, family } of addresses) if (family === 4 && !isValidPublicIPv4Address(address) || family === 6 && !isValidPublicIPv6Address(address) || family < 4 || family === 5 || family > 6) throw new UrlError(`Invalid or private address: ${address}`);
35
+ }
36
+ function isValidPublicIPv4Address(address) {
37
+ const parts = address.split(".");
38
+ const first = parseInt(parts[0]);
39
+ if (first === 0 || first === 10 || first === 127) return false;
40
+ const second = parseInt(parts[1]);
41
+ if (first === 169 && second === 254) return false;
42
+ if (first === 172 && second >= 16 && second <= 31) return false;
43
+ if (first === 192 && second === 168) return false;
44
+ return true;
45
+ }
46
+ function isValidPublicIPv6Address(address) {
47
+ address = expandIPv6Address(address);
48
+ if (address.at(4) !== ":") return false;
49
+ const firstWord = parseInt(address.substring(0, 4), 16);
50
+ return !(firstWord >= 64512 && firstWord <= 65023 || firstWord >= 65152 && firstWord <= 65215 || firstWord === 0 || firstWord >= 65280);
51
+ }
52
+ function expandIPv6Address(address) {
53
+ address = address.toLowerCase();
54
+ if (address === "::") return "0000:0000:0000:0000:0000:0000:0000:0000";
55
+ if (address.startsWith("::")) address = "0000" + address;
56
+ if (address.endsWith("::")) address = address + "0000";
57
+ address = address.replace("::", ":0000".repeat(8 - (address.match(/:/g) || []).length) + ":");
58
+ const parts = address.split(":");
59
+ return parts.map((part) => part.padStart(4, "0")).join(":");
60
+ }
61
+
62
+ //#endregion
63
+ export { UrlError, expandIPv6Address, isValidPublicIPv4Address, isValidPublicIPv6Address, validatePublicUrl };
@@ -0,0 +1,37 @@
1
+ const require_chunk = require('./chunk-DWy1uDak.cjs');
2
+ const require_url = require('./url-C5Vs9nYh.cjs');
3
+ const node_assert = require_chunk.__toESM(require("node:assert"));
4
+ const node_test = require_chunk.__toESM(require("node:test"));
5
+
6
+ //#region src/url.test.ts
7
+ (0, node_test.test)("validatePublicUrl()", async () => {
8
+ await (0, node_assert.rejects)(() => require_url.validatePublicUrl("ftp://localhost"), require_url.UrlError);
9
+ await (0, node_assert.rejects)(() => require_url.validatePublicUrl("data:text/plain;base64,SGVsbG8sIFdvcmxkIQ=="), require_url.UrlError);
10
+ await (0, node_assert.rejects)(() => require_url.validatePublicUrl("https://localhost"), require_url.UrlError);
11
+ await (0, node_assert.rejects)(() => require_url.validatePublicUrl("https://127.0.0.1"), require_url.UrlError);
12
+ await (0, node_assert.rejects)(() => require_url.validatePublicUrl("https://[::1]"), require_url.UrlError);
13
+ });
14
+ (0, node_test.test)("isValidPublicIPv4Address()", () => {
15
+ (0, node_assert.ok)(require_url.isValidPublicIPv4Address("8.8.8.8"));
16
+ (0, node_assert.ok)(!require_url.isValidPublicIPv4Address("192.168.1.1"));
17
+ (0, node_assert.ok)(!require_url.isValidPublicIPv4Address("127.0.0.1"));
18
+ (0, node_assert.ok)(!require_url.isValidPublicIPv4Address("10.0.0.1"));
19
+ (0, node_assert.ok)(!require_url.isValidPublicIPv4Address("127.16.0.1"));
20
+ (0, node_assert.ok)(!require_url.isValidPublicIPv4Address("169.254.0.1"));
21
+ });
22
+ (0, node_test.test)("isValidPublicIPv6Address()", () => {
23
+ (0, node_assert.ok)(require_url.isValidPublicIPv6Address("2001:db8::1"));
24
+ (0, node_assert.ok)(!require_url.isValidPublicIPv6Address("::1"));
25
+ (0, node_assert.ok)(!require_url.isValidPublicIPv6Address("fc00::1"));
26
+ (0, node_assert.ok)(!require_url.isValidPublicIPv6Address("fe80::1"));
27
+ (0, node_assert.ok)(!require_url.isValidPublicIPv6Address("ff00::1"));
28
+ (0, node_assert.ok)(!require_url.isValidPublicIPv6Address("::"));
29
+ });
30
+ (0, node_test.test)("expandIPv6Address()", () => {
31
+ (0, node_assert.deepStrictEqual)(require_url.expandIPv6Address("::"), "0000:0000:0000:0000:0000:0000:0000:0000");
32
+ (0, node_assert.deepStrictEqual)(require_url.expandIPv6Address("::1"), "0000:0000:0000:0000:0000:0000:0000:0001");
33
+ (0, node_assert.deepStrictEqual)(require_url.expandIPv6Address("2001:db8::"), "2001:0db8:0000:0000:0000:0000:0000:0000");
34
+ (0, node_assert.deepStrictEqual)(require_url.expandIPv6Address("2001:db8::1"), "2001:0db8:0000:0000:0000:0000:0000:0001");
35
+ });
36
+
37
+ //#endregion
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,36 @@
1
+ import { UrlError, expandIPv6Address, isValidPublicIPv4Address, isValidPublicIPv6Address, validatePublicUrl } from "./url-fW_DHbih.js";
2
+ import { deepStrictEqual, ok, rejects } from "node:assert";
3
+ import { test } from "node:test";
4
+
5
+ //#region src/url.test.ts
6
+ test("validatePublicUrl()", async () => {
7
+ await rejects(() => validatePublicUrl("ftp://localhost"), UrlError);
8
+ await rejects(() => validatePublicUrl("data:text/plain;base64,SGVsbG8sIFdvcmxkIQ=="), UrlError);
9
+ await rejects(() => validatePublicUrl("https://localhost"), UrlError);
10
+ await rejects(() => validatePublicUrl("https://127.0.0.1"), UrlError);
11
+ await rejects(() => validatePublicUrl("https://[::1]"), UrlError);
12
+ });
13
+ test("isValidPublicIPv4Address()", () => {
14
+ ok(isValidPublicIPv4Address("8.8.8.8"));
15
+ ok(!isValidPublicIPv4Address("192.168.1.1"));
16
+ ok(!isValidPublicIPv4Address("127.0.0.1"));
17
+ ok(!isValidPublicIPv4Address("10.0.0.1"));
18
+ ok(!isValidPublicIPv4Address("127.16.0.1"));
19
+ ok(!isValidPublicIPv4Address("169.254.0.1"));
20
+ });
21
+ test("isValidPublicIPv6Address()", () => {
22
+ ok(isValidPublicIPv6Address("2001:db8::1"));
23
+ ok(!isValidPublicIPv6Address("::1"));
24
+ ok(!isValidPublicIPv6Address("fc00::1"));
25
+ ok(!isValidPublicIPv6Address("fe80::1"));
26
+ ok(!isValidPublicIPv6Address("ff00::1"));
27
+ ok(!isValidPublicIPv6Address("::"));
28
+ });
29
+ test("expandIPv6Address()", () => {
30
+ deepStrictEqual(expandIPv6Address("::"), "0000:0000:0000:0000:0000:0000:0000:0000");
31
+ deepStrictEqual(expandIPv6Address("::1"), "0000:0000:0000:0000:0000:0000:0000:0001");
32
+ deepStrictEqual(expandIPv6Address("2001:db8::"), "2001:0db8:0000:0000:0000:0000:0000:0000");
33
+ deepStrictEqual(expandIPv6Address("2001:db8::1"), "2001:0db8:0000:0000:0000:0000:0000:0001");
34
+ });
35
+
36
+ //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/vocab-runtime",
3
- "version": "2.0.0-dev.1908+c31cc639",
3
+ "version": "2.0.0-dev.206+fa815928",
4
4
  "homepage": "https://fedify.dev/",
5
5
  "repository": {
6
6
  "type": "git",
@@ -55,12 +55,13 @@
55
55
  "typescript": "^5.9.3"
56
56
  },
57
57
  "dependencies": {
58
- "@logtape/logtape": "^1.1.1",
58
+ "@logtape/logtape": "^2.0.0",
59
59
  "@multiformats/base-x": "^4.0.1",
60
+ "@opentelemetry/api": "^1.9.0",
60
61
  "asn1js": "^3.0.6",
61
62
  "byte-encodings": "^1.0.11",
62
63
  "multicodec": "^3.2.1",
63
- "pkijs": "^3.2.5"
64
+ "pkijs": "^3.3.3"
64
65
  },
65
66
  "scripts": {
66
67
  "build": "tsdown",
@@ -1,5 +1,5 @@
1
1
  import fetchMock from "fetch-mock";
2
- import { deepStrictEqual, rejects } from "node:assert";
2
+ import { deepStrictEqual, ok, rejects } from "node:assert";
3
3
  import { test } from "node:test";
4
4
  import preloadedContexts from "./contexts.ts";
5
5
  import { getDocumentLoader } from "./docloader.ts";
@@ -361,5 +361,33 @@ test("getDocumentLoader()", async (t) => {
361
361
  );
362
362
  });
363
363
 
364
+ // Regression test for ReDoS vulnerability (CVE-2025-68475)
365
+ // Malicious HTML payload: <a a="b" a="b" ... (unclosed tag)
366
+ // With the vulnerable regex, this causes catastrophic backtracking
367
+ const maliciousPayload = "<a" + ' a="b"'.repeat(30) + " ";
368
+
369
+ fetchMock.get("https://example.com/redos", {
370
+ body: maliciousPayload,
371
+ headers: { "Content-Type": "text/html; charset=utf-8" },
372
+ });
373
+
374
+ await t.test("ReDoS resistance (CVE-2025-68475)", async () => {
375
+ const start = performance.now();
376
+ // The malicious HTML will fail JSON parsing, but the important thing is
377
+ // that it should complete quickly (not hang due to ReDoS)
378
+ await rejects(
379
+ () => fetchDocumentLoader("https://example.com/redos"),
380
+ SyntaxError,
381
+ );
382
+ const elapsed = performance.now() - start;
383
+
384
+ // Should complete in under 1 second. With the vulnerable regex,
385
+ // this would take 14+ seconds for 30 repetitions.
386
+ ok(
387
+ elapsed < 1000,
388
+ `Potential ReDoS vulnerability detected: ${elapsed}ms (expected < 1000ms)`,
389
+ );
390
+ });
391
+
364
392
  fetchMock.hardReset();
365
393
  });