@fedify/vocab-runtime 2.0.29-dev.1989 → 2.0.29-dev.2018
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 -1
- package/dist/mod.cjs +15 -4
- package/dist/mod.d.cts +7 -0
- package/dist/mod.d.ts +7 -0
- package/dist/mod.js +15 -5
- package/dist/tests/{body-D_AlSh8c.mjs → body-DNFbzsxI.mjs} +1 -1
- package/dist/tests/{body-BPzg1k_5.cjs → body-DPMAUTtz.cjs} +1 -1
- package/dist/tests/body.test.cjs +1 -1
- package/dist/tests/body.test.mjs +1 -1
- package/dist/tests/docloader.test.cjs +3 -3
- package/dist/tests/docloader.test.mjs +3 -3
- package/dist/tests/{request-Dz-c-sU9.mjs → request-D5j8WVks.mjs} +1 -1
- package/dist/tests/{request-CYnGwxbv.cjs → request-dIRBY7QP.cjs} +1 -1
- package/dist/tests/request.test.cjs +1 -1
- package/dist/tests/request.test.mjs +1 -1
- package/dist/tests/{url-o0e-aXQY.mjs → url-DzlMS01z.mjs} +14 -4
- package/dist/tests/{url-CWU5HT3Y.cjs → url-Qk1svKZU.cjs} +15 -4
- package/dist/tests/url.test.cjs +36 -3
- package/dist/tests/url.test.mjs +37 -4
- package/package.json +1 -1
- package/src/url.test.ts +29 -3
- package/src/url.ts +21 -5
package/deno.json
CHANGED
package/dist/mod.cjs
CHANGED
|
@@ -6,6 +6,7 @@ let _opentelemetry_api = require("@opentelemetry/api");
|
|
|
6
6
|
let node_process = require("node:process");
|
|
7
7
|
node_process = require_chunk.__toESM(node_process);
|
|
8
8
|
let node_dns_promises = require("node:dns/promises");
|
|
9
|
+
node_dns_promises = require_chunk.__toESM(node_dns_promises);
|
|
9
10
|
let node_net = require("node:net");
|
|
10
11
|
let asn1js = require("asn1js");
|
|
11
12
|
let byte_encodings_base64 = require("byte-encodings/base64");
|
|
@@ -4434,7 +4435,7 @@ const preloadedContexts = {
|
|
|
4434
4435
|
//#endregion
|
|
4435
4436
|
//#region deno.json
|
|
4436
4437
|
var name = "@fedify/vocab-runtime";
|
|
4437
|
-
var version = "2.0.29-dev.
|
|
4438
|
+
var version = "2.0.29-dev.2018+2e67253f";
|
|
4438
4439
|
//#endregion
|
|
4439
4440
|
//#region src/request.ts
|
|
4440
4441
|
/**
|
|
@@ -4757,9 +4758,16 @@ var HttpHeaderLink = class HttpHeaderLink {
|
|
|
4757
4758
|
//#endregion
|
|
4758
4759
|
//#region src/url.ts
|
|
4759
4760
|
var UrlError = class extends Error {
|
|
4761
|
+
/**
|
|
4762
|
+
* The failure category. Defaults to `"disallowed"`.
|
|
4763
|
+
* `"dns"` includes lookup errors and results with no usable IP addresses.
|
|
4764
|
+
* @since 2.0.29
|
|
4765
|
+
*/
|
|
4766
|
+
reason;
|
|
4760
4767
|
constructor(message, options) {
|
|
4761
4768
|
super(message, options);
|
|
4762
4769
|
this.name = "UrlError";
|
|
4770
|
+
this.reason = options?.reason ?? "disallowed";
|
|
4763
4771
|
}
|
|
4764
4772
|
};
|
|
4765
4773
|
/**
|
|
@@ -4785,9 +4793,12 @@ async function validatePublicUrl(url) {
|
|
|
4785
4793
|
}
|
|
4786
4794
|
let addresses;
|
|
4787
4795
|
try {
|
|
4788
|
-
addresses = await
|
|
4796
|
+
addresses = await node_dns_promises.default.lookup(hostname, { all: true });
|
|
4789
4797
|
} catch (error) {
|
|
4790
|
-
throw new UrlError("DNS lookup failed", {
|
|
4798
|
+
throw new UrlError("DNS lookup failed", {
|
|
4799
|
+
cause: error,
|
|
4800
|
+
reason: "dns"
|
|
4801
|
+
});
|
|
4791
4802
|
}
|
|
4792
4803
|
validateLookupAddresses(addresses);
|
|
4793
4804
|
}
|
|
@@ -4816,7 +4827,7 @@ function validateLookupAddresses(addresses) {
|
|
|
4816
4827
|
ipAddressCount++;
|
|
4817
4828
|
validatePublicIpAddress(address, family);
|
|
4818
4829
|
}
|
|
4819
|
-
if (ipAddressCount === 0) throw new UrlError("DNS lookup did not return any IP address");
|
|
4830
|
+
if (ipAddressCount === 0) throw new UrlError("DNS lookup did not return any IP address", { reason: "dns" });
|
|
4820
4831
|
}
|
|
4821
4832
|
function validatePublicIpAddress(address, family) {
|
|
4822
4833
|
if (family === 4 && isValidPublicIPv4Address(address) || family === 6 && isValidPublicIPv6Address(address)) return;
|
package/dist/mod.d.cts
CHANGED
|
@@ -320,8 +320,15 @@ declare function encodingFromBaseData(data: string | Uint8Array): Base;
|
|
|
320
320
|
//#endregion
|
|
321
321
|
//#region src/url.d.ts
|
|
322
322
|
declare class UrlError extends Error {
|
|
323
|
+
/**
|
|
324
|
+
* The failure category. Defaults to `"disallowed"`.
|
|
325
|
+
* `"dns"` includes lookup errors and results with no usable IP addresses.
|
|
326
|
+
* @since 2.0.29
|
|
327
|
+
*/
|
|
328
|
+
readonly reason: "dns" | "disallowed";
|
|
323
329
|
constructor(message: string, options?: {
|
|
324
330
|
cause?: unknown;
|
|
331
|
+
reason?: "dns" | "disallowed";
|
|
325
332
|
});
|
|
326
333
|
}
|
|
327
334
|
/**
|
package/dist/mod.d.ts
CHANGED
|
@@ -319,8 +319,15 @@ declare function encodingFromBaseData(data: string | Uint8Array): Base;
|
|
|
319
319
|
//#endregion
|
|
320
320
|
//#region src/url.d.ts
|
|
321
321
|
declare class UrlError extends Error {
|
|
322
|
+
/**
|
|
323
|
+
* The failure category. Defaults to `"disallowed"`.
|
|
324
|
+
* `"dns"` includes lookup errors and results with no usable IP addresses.
|
|
325
|
+
* @since 2.0.29
|
|
326
|
+
*/
|
|
327
|
+
readonly reason: "dns" | "disallowed";
|
|
322
328
|
constructor(message: string, options?: {
|
|
323
329
|
cause?: unknown;
|
|
330
|
+
reason?: "dns" | "disallowed";
|
|
324
331
|
});
|
|
325
332
|
}
|
|
326
333
|
/**
|
package/dist/mod.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { getLogger } from "@logtape/logtape";
|
|
3
3
|
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
4
4
|
import process from "node:process";
|
|
5
|
-
import
|
|
5
|
+
import dns from "node:dns/promises";
|
|
6
6
|
import { isIP } from "node:net";
|
|
7
7
|
import { Integer, Sequence } from "asn1js";
|
|
8
8
|
import { decodeBase64, encodeBase64 } from "byte-encodings/base64";
|
|
@@ -4430,7 +4430,7 @@ const preloadedContexts = {
|
|
|
4430
4430
|
//#endregion
|
|
4431
4431
|
//#region deno.json
|
|
4432
4432
|
var name = "@fedify/vocab-runtime";
|
|
4433
|
-
var version = "2.0.29-dev.
|
|
4433
|
+
var version = "2.0.29-dev.2018+2e67253f";
|
|
4434
4434
|
//#endregion
|
|
4435
4435
|
//#region src/request.ts
|
|
4436
4436
|
/**
|
|
@@ -4753,9 +4753,16 @@ var HttpHeaderLink = class HttpHeaderLink {
|
|
|
4753
4753
|
//#endregion
|
|
4754
4754
|
//#region src/url.ts
|
|
4755
4755
|
var UrlError = class extends Error {
|
|
4756
|
+
/**
|
|
4757
|
+
* The failure category. Defaults to `"disallowed"`.
|
|
4758
|
+
* `"dns"` includes lookup errors and results with no usable IP addresses.
|
|
4759
|
+
* @since 2.0.29
|
|
4760
|
+
*/
|
|
4761
|
+
reason;
|
|
4756
4762
|
constructor(message, options) {
|
|
4757
4763
|
super(message, options);
|
|
4758
4764
|
this.name = "UrlError";
|
|
4765
|
+
this.reason = options?.reason ?? "disallowed";
|
|
4759
4766
|
}
|
|
4760
4767
|
};
|
|
4761
4768
|
/**
|
|
@@ -4781,9 +4788,12 @@ async function validatePublicUrl(url) {
|
|
|
4781
4788
|
}
|
|
4782
4789
|
let addresses;
|
|
4783
4790
|
try {
|
|
4784
|
-
addresses = await lookup(hostname, { all: true });
|
|
4791
|
+
addresses = await dns.lookup(hostname, { all: true });
|
|
4785
4792
|
} catch (error) {
|
|
4786
|
-
throw new UrlError("DNS lookup failed", {
|
|
4793
|
+
throw new UrlError("DNS lookup failed", {
|
|
4794
|
+
cause: error,
|
|
4795
|
+
reason: "dns"
|
|
4796
|
+
});
|
|
4787
4797
|
}
|
|
4788
4798
|
validateLookupAddresses(addresses);
|
|
4789
4799
|
}
|
|
@@ -4812,7 +4822,7 @@ function validateLookupAddresses(addresses) {
|
|
|
4812
4822
|
ipAddressCount++;
|
|
4813
4823
|
validatePublicIpAddress(address, family);
|
|
4814
4824
|
}
|
|
4815
|
-
if (ipAddressCount === 0) throw new UrlError("DNS lookup did not return any IP address");
|
|
4825
|
+
if (ipAddressCount === 0) throw new UrlError("DNS lookup did not return any IP address", { reason: "dns" });
|
|
4816
4826
|
}
|
|
4817
4827
|
function validatePublicIpAddress(address, family) {
|
|
4818
4828
|
if (family === 4 && isValidPublicIPv4Address(address) || family === 6 && isValidPublicIPv6Address(address)) return;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
require("./chunk-Do9eywBl.cjs");
|
|
2
|
-
const require_request = require("./request-
|
|
2
|
+
const require_request = require("./request-dIRBY7QP.cjs");
|
|
3
3
|
let _logtape_logtape = require("@logtape/logtape");
|
|
4
4
|
//#region src/body.ts
|
|
5
5
|
/** Decoded JSON body limit: 16 MiB. @internal */
|
package/dist/tests/body.test.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
require("./chunk-Do9eywBl.cjs");
|
|
2
|
-
const require_body = require("./body-
|
|
2
|
+
const require_body = require("./body-DPMAUTtz.cjs");
|
|
3
3
|
let node_assert_strict = require("node:assert/strict");
|
|
4
4
|
let node_test = require("node:test");
|
|
5
5
|
let node_zlib = require("node:zlib");
|
package/dist/tests/body.test.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as readBoundedText, t as BodyTooLargeError } from "./body-
|
|
1
|
+
import { r as readBoundedText, t as BodyTooLargeError } from "./body-DNFbzsxI.mjs";
|
|
2
2
|
import { deepStrictEqual, ok, rejects } from "node:assert/strict";
|
|
3
3
|
import { test } from "node:test";
|
|
4
4
|
import { gunzipSync, gzipSync } from "node:zlib";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-Do9eywBl.cjs");
|
|
2
|
-
const require_request = require("./request-
|
|
3
|
-
const require_body = require("./body-
|
|
2
|
+
const require_request = require("./request-dIRBY7QP.cjs");
|
|
3
|
+
const require_body = require("./body-DPMAUTtz.cjs");
|
|
4
4
|
const require_link = require("./link-FguCydMA.cjs");
|
|
5
|
-
const require_url = require("./url-
|
|
5
|
+
const require_url = require("./url-Qk1svKZU.cjs");
|
|
6
6
|
let node_test = require("node:test");
|
|
7
7
|
let node_zlib = require("node:zlib");
|
|
8
8
|
let _logtape_logtape = require("@logtape/logtape");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-
|
|
2
|
-
import { n as MAX_BODY_SIZE, r as readBoundedText } from "./body-
|
|
1
|
+
import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-D5j8WVks.mjs";
|
|
2
|
+
import { n as MAX_BODY_SIZE, r as readBoundedText } from "./body-DNFbzsxI.mjs";
|
|
3
3
|
import { t as HttpHeaderLink } from "./link-NUUWCdnK.mjs";
|
|
4
|
-
import { o as validatePublicUrl, t as UrlError } from "./url-
|
|
4
|
+
import { o as validatePublicUrl, t as UrlError } from "./url-DzlMS01z.mjs";
|
|
5
5
|
import { test } from "node:test";
|
|
6
6
|
import { gzipSync } from "node:zlib";
|
|
7
7
|
import { getLogger } from "@logtape/logtape";
|
|
@@ -3,7 +3,7 @@ let node_process = require("node:process");
|
|
|
3
3
|
node_process = require_chunk.__toESM(node_process);
|
|
4
4
|
//#region deno.json
|
|
5
5
|
var name = "@fedify/vocab-runtime";
|
|
6
|
-
var version = "2.0.29-dev.
|
|
6
|
+
var version = "2.0.29-dev.2018+2e67253f";
|
|
7
7
|
//#endregion
|
|
8
8
|
//#region src/request.ts
|
|
9
9
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-Do9eywBl.cjs");
|
|
2
|
-
const require_request = require("./request-
|
|
2
|
+
const require_request = require("./request-dIRBY7QP.cjs");
|
|
3
3
|
let node_test = require("node:test");
|
|
4
4
|
let node_process = require("node:process");
|
|
5
5
|
node_process = require_chunk.__toESM(node_process);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { o as version, r as getUserAgent } from "./request-
|
|
1
|
+
import { o as version, r as getUserAgent } from "./request-D5j8WVks.mjs";
|
|
2
2
|
import { test } from "node:test";
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import { deepStrictEqual } from "node:assert";
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import
|
|
1
|
+
import dns from "node:dns/promises";
|
|
2
2
|
import { isIP } from "node:net";
|
|
3
3
|
//#region src/url.ts
|
|
4
4
|
var UrlError = class extends Error {
|
|
5
|
+
/**
|
|
6
|
+
* The failure category. Defaults to `"disallowed"`.
|
|
7
|
+
* `"dns"` includes lookup errors and results with no usable IP addresses.
|
|
8
|
+
* @since 2.0.29
|
|
9
|
+
*/
|
|
10
|
+
reason;
|
|
5
11
|
constructor(message, options) {
|
|
6
12
|
super(message, options);
|
|
7
13
|
this.name = "UrlError";
|
|
14
|
+
this.reason = options?.reason ?? "disallowed";
|
|
8
15
|
}
|
|
9
16
|
};
|
|
10
17
|
/**
|
|
@@ -30,9 +37,12 @@ async function validatePublicUrl(url) {
|
|
|
30
37
|
}
|
|
31
38
|
let addresses;
|
|
32
39
|
try {
|
|
33
|
-
addresses = await lookup(hostname, { all: true });
|
|
40
|
+
addresses = await dns.lookup(hostname, { all: true });
|
|
34
41
|
} catch (error) {
|
|
35
|
-
throw new UrlError("DNS lookup failed", {
|
|
42
|
+
throw new UrlError("DNS lookup failed", {
|
|
43
|
+
cause: error,
|
|
44
|
+
reason: "dns"
|
|
45
|
+
});
|
|
36
46
|
}
|
|
37
47
|
validateLookupAddresses(addresses);
|
|
38
48
|
}
|
|
@@ -61,7 +71,7 @@ function validateLookupAddresses(addresses) {
|
|
|
61
71
|
ipAddressCount++;
|
|
62
72
|
validatePublicIpAddress(address, family);
|
|
63
73
|
}
|
|
64
|
-
if (ipAddressCount === 0) throw new UrlError("DNS lookup did not return any IP address");
|
|
74
|
+
if (ipAddressCount === 0) throw new UrlError("DNS lookup did not return any IP address", { reason: "dns" });
|
|
65
75
|
}
|
|
66
76
|
function validatePublicIpAddress(address, family) {
|
|
67
77
|
if (family === 4 && isValidPublicIPv4Address(address) || family === 6 && isValidPublicIPv6Address(address)) return;
|
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
require("./chunk-Do9eywBl.cjs");
|
|
1
|
+
const require_chunk = require("./chunk-Do9eywBl.cjs");
|
|
2
2
|
let node_dns_promises = require("node:dns/promises");
|
|
3
|
+
node_dns_promises = require_chunk.__toESM(node_dns_promises);
|
|
3
4
|
let node_net = require("node:net");
|
|
4
5
|
//#region src/url.ts
|
|
5
6
|
var UrlError = class extends Error {
|
|
7
|
+
/**
|
|
8
|
+
* The failure category. Defaults to `"disallowed"`.
|
|
9
|
+
* `"dns"` includes lookup errors and results with no usable IP addresses.
|
|
10
|
+
* @since 2.0.29
|
|
11
|
+
*/
|
|
12
|
+
reason;
|
|
6
13
|
constructor(message, options) {
|
|
7
14
|
super(message, options);
|
|
8
15
|
this.name = "UrlError";
|
|
16
|
+
this.reason = options?.reason ?? "disallowed";
|
|
9
17
|
}
|
|
10
18
|
};
|
|
11
19
|
/**
|
|
@@ -31,9 +39,12 @@ async function validatePublicUrl(url) {
|
|
|
31
39
|
}
|
|
32
40
|
let addresses;
|
|
33
41
|
try {
|
|
34
|
-
addresses = await
|
|
42
|
+
addresses = await node_dns_promises.default.lookup(hostname, { all: true });
|
|
35
43
|
} catch (error) {
|
|
36
|
-
throw new UrlError("DNS lookup failed", {
|
|
44
|
+
throw new UrlError("DNS lookup failed", {
|
|
45
|
+
cause: error,
|
|
46
|
+
reason: "dns"
|
|
47
|
+
});
|
|
37
48
|
}
|
|
38
49
|
validateLookupAddresses(addresses);
|
|
39
50
|
}
|
|
@@ -62,7 +73,7 @@ function validateLookupAddresses(addresses) {
|
|
|
62
73
|
ipAddressCount++;
|
|
63
74
|
validatePublicIpAddress(address, family);
|
|
64
75
|
}
|
|
65
|
-
if (ipAddressCount === 0) throw new UrlError("DNS lookup did not return any IP address");
|
|
76
|
+
if (ipAddressCount === 0) throw new UrlError("DNS lookup did not return any IP address", { reason: "dns" });
|
|
66
77
|
}
|
|
67
78
|
function validatePublicIpAddress(address, family) {
|
|
68
79
|
if (family === 4 && isValidPublicIPv4Address(address) || family === 6 && isValidPublicIPv6Address(address)) return;
|
package/dist/tests/url.test.cjs
CHANGED
|
@@ -1,8 +1,31 @@
|
|
|
1
1
|
require("./chunk-Do9eywBl.cjs");
|
|
2
|
-
const require_url = require("./url-
|
|
2
|
+
const require_url = require("./url-Qk1svKZU.cjs");
|
|
3
3
|
let node_test = require("node:test");
|
|
4
4
|
let node_assert = require("node:assert");
|
|
5
5
|
//#region src/url.test.ts
|
|
6
|
+
(0, node_test.test)("UrlError reason is independent of its cause", () => {
|
|
7
|
+
const cause = /* @__PURE__ */ new Error("Underlying failure");
|
|
8
|
+
(0, node_assert.strictEqual)(new require_url.UrlError("Rejected URL").reason, "disallowed");
|
|
9
|
+
const disallowedError = new require_url.UrlError("Rejected URL", { cause });
|
|
10
|
+
(0, node_assert.strictEqual)(disallowedError.reason, "disallowed");
|
|
11
|
+
(0, node_assert.strictEqual)(disallowedError.cause, cause);
|
|
12
|
+
const dnsError = new require_url.UrlError("Lookup failed", {
|
|
13
|
+
reason: "dns",
|
|
14
|
+
cause
|
|
15
|
+
});
|
|
16
|
+
(0, node_assert.strictEqual)(dnsError.reason, "dns");
|
|
17
|
+
(0, node_assert.strictEqual)(dnsError.cause, cause);
|
|
18
|
+
});
|
|
19
|
+
(0, node_test.test)("validatePublicUrl() classifies disallowed URLs", async () => {
|
|
20
|
+
for (const url of [
|
|
21
|
+
"ftp://example.com",
|
|
22
|
+
"https://localhost",
|
|
23
|
+
"https://127.0.0.1"
|
|
24
|
+
]) await (0, node_assert.rejects)(() => require_url.validatePublicUrl(url), {
|
|
25
|
+
name: "UrlError",
|
|
26
|
+
reason: "disallowed"
|
|
27
|
+
});
|
|
28
|
+
});
|
|
6
29
|
(0, node_test.test)("validatePublicUrl()", async () => {
|
|
7
30
|
await (0, node_assert.rejects)(() => require_url.validatePublicUrl("ftp://localhost"), require_url.UrlError);
|
|
8
31
|
await (0, node_assert.rejects)(() => require_url.validatePublicUrl("data:text/plain;base64,SGVsbG8sIFdvcmxkIQ=="), require_url.UrlError);
|
|
@@ -52,11 +75,21 @@ let node_assert = require("node:assert");
|
|
|
52
75
|
}, {
|
|
53
76
|
address: "127.0.0.1",
|
|
54
77
|
family: 4
|
|
55
|
-
}]),
|
|
78
|
+
}]), {
|
|
79
|
+
name: "UrlError",
|
|
80
|
+
reason: "disallowed"
|
|
81
|
+
});
|
|
56
82
|
(0, node_assert.throws)(() => require_url.validateLookupAddresses([{
|
|
57
83
|
address: "app-host.example.net.",
|
|
58
84
|
family: 4
|
|
59
|
-
}]),
|
|
85
|
+
}]), {
|
|
86
|
+
name: "UrlError",
|
|
87
|
+
reason: "dns"
|
|
88
|
+
});
|
|
89
|
+
(0, node_assert.throws)(() => require_url.validateLookupAddresses([]), {
|
|
90
|
+
name: "UrlError",
|
|
91
|
+
reason: "dns"
|
|
92
|
+
});
|
|
60
93
|
});
|
|
61
94
|
(0, node_test.test)("isValidPublicIPv4Address()", () => {
|
|
62
95
|
(0, node_assert.ok)(require_url.isValidPublicIPv4Address("8.8.8.8"));
|
package/dist/tests/url.test.mjs
CHANGED
|
@@ -1,7 +1,30 @@
|
|
|
1
|
-
import { a as validateLookupAddresses, i as isValidPublicIPv6Address, n as expandIPv6Address, o as validatePublicUrl, r as isValidPublicIPv4Address, t as UrlError } from "./url-
|
|
1
|
+
import { a as validateLookupAddresses, i as isValidPublicIPv6Address, n as expandIPv6Address, o as validatePublicUrl, r as isValidPublicIPv4Address, t as UrlError } from "./url-DzlMS01z.mjs";
|
|
2
2
|
import { test } from "node:test";
|
|
3
|
-
import { deepStrictEqual, ok, rejects, throws } from "node:assert";
|
|
3
|
+
import { deepStrictEqual, ok, rejects, strictEqual, throws } from "node:assert";
|
|
4
4
|
//#region src/url.test.ts
|
|
5
|
+
test("UrlError reason is independent of its cause", () => {
|
|
6
|
+
const cause = /* @__PURE__ */ new Error("Underlying failure");
|
|
7
|
+
strictEqual(new UrlError("Rejected URL").reason, "disallowed");
|
|
8
|
+
const disallowedError = new UrlError("Rejected URL", { cause });
|
|
9
|
+
strictEqual(disallowedError.reason, "disallowed");
|
|
10
|
+
strictEqual(disallowedError.cause, cause);
|
|
11
|
+
const dnsError = new UrlError("Lookup failed", {
|
|
12
|
+
reason: "dns",
|
|
13
|
+
cause
|
|
14
|
+
});
|
|
15
|
+
strictEqual(dnsError.reason, "dns");
|
|
16
|
+
strictEqual(dnsError.cause, cause);
|
|
17
|
+
});
|
|
18
|
+
test("validatePublicUrl() classifies disallowed URLs", async () => {
|
|
19
|
+
for (const url of [
|
|
20
|
+
"ftp://example.com",
|
|
21
|
+
"https://localhost",
|
|
22
|
+
"https://127.0.0.1"
|
|
23
|
+
]) await rejects(() => validatePublicUrl(url), {
|
|
24
|
+
name: "UrlError",
|
|
25
|
+
reason: "disallowed"
|
|
26
|
+
});
|
|
27
|
+
});
|
|
5
28
|
test("validatePublicUrl()", async () => {
|
|
6
29
|
await rejects(() => validatePublicUrl("ftp://localhost"), UrlError);
|
|
7
30
|
await rejects(() => validatePublicUrl("data:text/plain;base64,SGVsbG8sIFdvcmxkIQ=="), UrlError);
|
|
@@ -51,11 +74,21 @@ test("validateLookupAddresses() rejects unsafe or CNAME-only Cloudflare Workers
|
|
|
51
74
|
}, {
|
|
52
75
|
address: "127.0.0.1",
|
|
53
76
|
family: 4
|
|
54
|
-
}]),
|
|
77
|
+
}]), {
|
|
78
|
+
name: "UrlError",
|
|
79
|
+
reason: "disallowed"
|
|
80
|
+
});
|
|
55
81
|
throws(() => validateLookupAddresses([{
|
|
56
82
|
address: "app-host.example.net.",
|
|
57
83
|
family: 4
|
|
58
|
-
}]),
|
|
84
|
+
}]), {
|
|
85
|
+
name: "UrlError",
|
|
86
|
+
reason: "dns"
|
|
87
|
+
});
|
|
88
|
+
throws(() => validateLookupAddresses([]), {
|
|
89
|
+
name: "UrlError",
|
|
90
|
+
reason: "dns"
|
|
91
|
+
});
|
|
59
92
|
});
|
|
60
93
|
test("isValidPublicIPv4Address()", () => {
|
|
61
94
|
ok(isValidPublicIPv4Address("8.8.8.8"));
|
package/package.json
CHANGED
package/src/url.test.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { deepStrictEqual, ok, rejects, throws } from "node:assert";
|
|
1
|
+
import { deepStrictEqual, ok, rejects, strictEqual, throws } from "node:assert";
|
|
2
2
|
import { test } from "node:test";
|
|
3
3
|
import {
|
|
4
4
|
expandIPv6Address,
|
|
@@ -9,6 +9,28 @@ import {
|
|
|
9
9
|
validatePublicUrl,
|
|
10
10
|
} from "./url.ts";
|
|
11
11
|
|
|
12
|
+
test("UrlError reason is independent of its cause", () => {
|
|
13
|
+
const cause = new Error("Underlying failure");
|
|
14
|
+
strictEqual(new UrlError("Rejected URL").reason, "disallowed");
|
|
15
|
+
const disallowedError = new UrlError("Rejected URL", { cause });
|
|
16
|
+
strictEqual(disallowedError.reason, "disallowed");
|
|
17
|
+
strictEqual(disallowedError.cause, cause);
|
|
18
|
+
const dnsError = new UrlError("Lookup failed", { reason: "dns", cause });
|
|
19
|
+
strictEqual(dnsError.reason, "dns");
|
|
20
|
+
strictEqual(dnsError.cause, cause);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("validatePublicUrl() classifies disallowed URLs", async () => {
|
|
24
|
+
for (
|
|
25
|
+
const url of ["ftp://example.com", "https://localhost", "https://127.0.0.1"]
|
|
26
|
+
) {
|
|
27
|
+
await rejects(() => validatePublicUrl(url), {
|
|
28
|
+
name: "UrlError",
|
|
29
|
+
reason: "disallowed",
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
12
34
|
test("validatePublicUrl()", async () => {
|
|
13
35
|
await rejects(() => validatePublicUrl("ftp://localhost"), UrlError);
|
|
14
36
|
await rejects(
|
|
@@ -81,15 +103,19 @@ test("validateLookupAddresses() rejects unsafe or CNAME-only Cloudflare Workers
|
|
|
81
103
|
{ address: "private-host.example.net.", family: 4 },
|
|
82
104
|
{ address: "127.0.0.1", family: 4 },
|
|
83
105
|
]),
|
|
84
|
-
UrlError,
|
|
106
|
+
{ name: "UrlError", reason: "disallowed" },
|
|
85
107
|
);
|
|
86
108
|
throws(
|
|
87
109
|
() =>
|
|
88
110
|
validateLookupAddresses([
|
|
89
111
|
{ address: "app-host.example.net.", family: 4 },
|
|
90
112
|
]),
|
|
91
|
-
UrlError,
|
|
113
|
+
{ name: "UrlError", reason: "dns" },
|
|
92
114
|
);
|
|
115
|
+
throws(() => validateLookupAddresses([]), {
|
|
116
|
+
name: "UrlError",
|
|
117
|
+
reason: "dns",
|
|
118
|
+
});
|
|
93
119
|
});
|
|
94
120
|
|
|
95
121
|
test("isValidPublicIPv4Address()", () => {
|
package/src/url.ts
CHANGED
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
import type { LookupAddress } from "node:dns";
|
|
2
|
-
import
|
|
2
|
+
// FIXME: the default dns import exists since tests can stub `dns.lookup()`.
|
|
3
|
+
// This should be replaced by injecting the lookup function such as an internal
|
|
4
|
+
// option on `validatePublicUrl()`
|
|
5
|
+
import dns from "node:dns/promises";
|
|
3
6
|
import { isIP } from "node:net";
|
|
4
7
|
|
|
5
8
|
export class UrlError extends Error {
|
|
6
|
-
|
|
9
|
+
/**
|
|
10
|
+
* The failure category. Defaults to `"disallowed"`.
|
|
11
|
+
* `"dns"` includes lookup errors and results with no usable IP addresses.
|
|
12
|
+
* @since 2.0.29
|
|
13
|
+
*/
|
|
14
|
+
readonly reason: "dns" | "disallowed";
|
|
15
|
+
|
|
16
|
+
constructor(
|
|
17
|
+
message: string,
|
|
18
|
+
options?: { cause?: unknown; reason?: "dns" | "disallowed" },
|
|
19
|
+
) {
|
|
7
20
|
super(message, options);
|
|
8
21
|
this.name = "UrlError";
|
|
22
|
+
this.reason = options?.reason ?? "disallowed";
|
|
9
23
|
}
|
|
10
24
|
}
|
|
11
25
|
|
|
@@ -50,9 +64,9 @@ export async function validatePublicUrl(url: string): Promise<void> {
|
|
|
50
64
|
// and ensure that they are all public:
|
|
51
65
|
let addresses: LookupAddress[];
|
|
52
66
|
try {
|
|
53
|
-
addresses = await lookup(hostname, { all: true });
|
|
67
|
+
addresses = await dns.lookup(hostname, { all: true });
|
|
54
68
|
} catch (error) {
|
|
55
|
-
throw new UrlError("DNS lookup failed", { cause: error });
|
|
69
|
+
throw new UrlError("DNS lookup failed", { cause: error, reason: "dns" });
|
|
56
70
|
}
|
|
57
71
|
validateLookupAddresses(addresses);
|
|
58
72
|
}
|
|
@@ -85,7 +99,9 @@ export function validateLookupAddresses(
|
|
|
85
99
|
validatePublicIpAddress(address, family);
|
|
86
100
|
}
|
|
87
101
|
if (ipAddressCount === 0) {
|
|
88
|
-
throw new UrlError("DNS lookup did not return any IP address"
|
|
102
|
+
throw new UrlError("DNS lookup did not return any IP address", {
|
|
103
|
+
reason: "dns",
|
|
104
|
+
});
|
|
89
105
|
}
|
|
90
106
|
}
|
|
91
107
|
|