@fedify/vocab-runtime 2.0.17 → 2.0.19
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 +121 -13
- package/dist/mod.js +121 -13
- package/dist/tests/docloader.test.cjs +2 -2
- package/dist/tests/docloader.test.mjs +2 -2
- package/dist/tests/{request-zv1xzJko.mjs → request-BUC0LY4m.mjs} +1 -1
- package/dist/tests/{request-CzYkWN86.cjs → request-CuxFKHwd.cjs} +1 -1
- package/dist/tests/request.test.cjs +1 -1
- package/dist/tests/request.test.mjs +1 -1
- package/dist/tests/url-CjF2c1ff.cjs +206 -0
- package/dist/tests/url-Cyu1RKqd.mjs +176 -0
- package/dist/tests/url.test.cjs +52 -1
- package/dist/tests/url.test.mjs +52 -1
- package/package.json +1 -1
- package/src/url.test.ts +76 -0
- package/src/url.ts +182 -16
- package/dist/tests/url-DftNtd3d.mjs +0 -68
- package/dist/tests/url-SR63sukb.cjs +0 -98
package/deno.json
CHANGED
package/dist/mod.cjs
CHANGED
|
@@ -4295,7 +4295,7 @@ const preloadedContexts = {
|
|
|
4295
4295
|
//#endregion
|
|
4296
4296
|
//#region deno.json
|
|
4297
4297
|
var name = "@fedify/vocab-runtime";
|
|
4298
|
-
var version = "2.0.
|
|
4298
|
+
var version = "2.0.19";
|
|
4299
4299
|
//#endregion
|
|
4300
4300
|
//#region src/link.ts
|
|
4301
4301
|
const parametersNeedLowerCase = ["rel", "type"];
|
|
@@ -4583,29 +4583,137 @@ function validatePublicIpAddress(address, family) {
|
|
|
4583
4583
|
throw new UrlError(`Invalid or private address: ${address}`);
|
|
4584
4584
|
}
|
|
4585
4585
|
function isValidPublicIPv4Address(address) {
|
|
4586
|
-
const parts = address
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
if (first === 169 && second === 254) return false;
|
|
4591
|
-
if (first === 172 && second >= 16 && second <= 31) return false;
|
|
4592
|
-
if (first === 192 && second === 168) return false;
|
|
4593
|
-
return true;
|
|
4586
|
+
const parts = parseIPv4Address(address);
|
|
4587
|
+
if (parts == null) return false;
|
|
4588
|
+
const value = ipv4PartsToNumber(parts);
|
|
4589
|
+
return !nonPublicIPv4Prefixes.some(({ base, prefix }) => matchesIPv4Prefix(value, base, prefix));
|
|
4594
4590
|
}
|
|
4595
4591
|
function isValidPublicIPv6Address(address) {
|
|
4596
|
-
|
|
4597
|
-
if (
|
|
4598
|
-
|
|
4599
|
-
|
|
4592
|
+
const words = parseIPv6Address(address);
|
|
4593
|
+
if (words == null) return false;
|
|
4594
|
+
if (nonPublicIPv6Prefixes.some(({ words: prefixWords, prefix }) => matchesIPv6Prefix(words, prefixWords, prefix))) return false;
|
|
4595
|
+
for (const { extractIPv4, prefix, words: prefixWords } of ipv6WithIPv4Prefixes) {
|
|
4596
|
+
if (!matchesIPv6Prefix(words, prefixWords, prefix)) continue;
|
|
4597
|
+
const ipv4Address = extractIPv4(words);
|
|
4598
|
+
if (ipv4Address != null && !isValidPublicIPv4Address(ipv4Address)) return false;
|
|
4599
|
+
}
|
|
4600
|
+
return true;
|
|
4600
4601
|
}
|
|
4601
4602
|
function expandIPv6Address(address) {
|
|
4602
4603
|
address = address.toLowerCase();
|
|
4604
|
+
const ipv4Delimiter = address.lastIndexOf(":");
|
|
4605
|
+
if (address.includes(".") && ipv4Delimiter >= 0) {
|
|
4606
|
+
const ipv4Parts = parseIPv4Address(address.substring(ipv4Delimiter + 1));
|
|
4607
|
+
if (ipv4Parts == null) return address;
|
|
4608
|
+
const high = (ipv4Parts[0] << 8) + ipv4Parts[1];
|
|
4609
|
+
const low = (ipv4Parts[2] << 8) + ipv4Parts[3];
|
|
4610
|
+
address = address.substring(0, ipv4Delimiter + 1) + high.toString(16) + ":" + low.toString(16);
|
|
4611
|
+
}
|
|
4603
4612
|
if (address === "::") return "0000:0000:0000:0000:0000:0000:0000:0000";
|
|
4604
4613
|
if (address.startsWith("::")) address = "0000" + address;
|
|
4605
4614
|
if (address.endsWith("::")) address = address + "0000";
|
|
4606
4615
|
address = address.replace("::", ":0000".repeat(8 - (address.match(/:/g) || []).length) + ":");
|
|
4607
4616
|
return address.split(":").map((part) => part.padStart(4, "0")).join(":");
|
|
4608
4617
|
}
|
|
4618
|
+
const nonPublicIPv4Prefixes = [
|
|
4619
|
+
ipv4Prefix("0.0.0.0/8", "RFC 6890"),
|
|
4620
|
+
ipv4Prefix("10.0.0.0/8", "RFC 1918"),
|
|
4621
|
+
ipv4Prefix("100.64.0.0/10", "RFC 6598"),
|
|
4622
|
+
ipv4Prefix("127.0.0.0/8", "RFC 1122"),
|
|
4623
|
+
ipv4Prefix("169.254.0.0/16", "RFC 3927"),
|
|
4624
|
+
ipv4Prefix("172.16.0.0/12", "RFC 1918"),
|
|
4625
|
+
ipv4Prefix("192.0.0.0/24", "RFC 6890"),
|
|
4626
|
+
ipv4Prefix("192.0.2.0/24", "RFC 5737"),
|
|
4627
|
+
ipv4Prefix("192.88.99.0/24", "RFC 7526"),
|
|
4628
|
+
ipv4Prefix("192.168.0.0/16", "RFC 1918"),
|
|
4629
|
+
ipv4Prefix("198.18.0.0/15", "RFC 2544"),
|
|
4630
|
+
ipv4Prefix("198.51.100.0/24", "RFC 5737"),
|
|
4631
|
+
ipv4Prefix("203.0.113.0/24", "RFC 5737"),
|
|
4632
|
+
ipv4Prefix("224.0.0.0/4", "RFC 5771"),
|
|
4633
|
+
ipv4Prefix("240.0.0.0/4", "RFC 1112")
|
|
4634
|
+
];
|
|
4635
|
+
const nonPublicIPv6Prefixes = [
|
|
4636
|
+
ipv6Prefix("::/16", "RFC 4291"),
|
|
4637
|
+
ipv6Prefix("2001::/32", "RFC 4380"),
|
|
4638
|
+
ipv6Prefix("2002::/16", "RFC 3056"),
|
|
4639
|
+
ipv6Prefix("64:ff9b:1::/48", "RFC 8215"),
|
|
4640
|
+
ipv6Prefix("fc00::/7", "RFC 4193"),
|
|
4641
|
+
ipv6Prefix("fe80::/10", "RFC 4291"),
|
|
4642
|
+
ipv6Prefix("ff00::/8", "RFC 4291")
|
|
4643
|
+
];
|
|
4644
|
+
const ipv6WithIPv4Prefixes = [{
|
|
4645
|
+
...ipv6Prefix("64:ff9b::/96", "RFC 6052"),
|
|
4646
|
+
extractIPv4: (words) => ipv4FromWords(words[6], words[7])
|
|
4647
|
+
}];
|
|
4648
|
+
function ipv4Prefix(cidr, rfc) {
|
|
4649
|
+
const [address, prefixText] = cidr.split("/");
|
|
4650
|
+
const prefix = parseInt(prefixText, 10);
|
|
4651
|
+
const parts = parseIPv4Address(address);
|
|
4652
|
+
if (parts == null || !Number.isInteger(prefix) || prefix < 0 || prefix > 32) throw new Error(`Invalid IPv4 prefix: ${cidr}`);
|
|
4653
|
+
return {
|
|
4654
|
+
cidr,
|
|
4655
|
+
base: ipv4PartsToNumber(parts),
|
|
4656
|
+
prefix,
|
|
4657
|
+
rfc
|
|
4658
|
+
};
|
|
4659
|
+
}
|
|
4660
|
+
function ipv6Prefix(cidr, rfc) {
|
|
4661
|
+
const [address, prefixText] = cidr.split("/");
|
|
4662
|
+
const prefix = parseInt(prefixText, 10);
|
|
4663
|
+
const words = parseIPv6Address(address);
|
|
4664
|
+
if (words == null || !Number.isInteger(prefix) || prefix < 0 || prefix > 128) throw new Error(`Invalid IPv6 prefix: ${cidr}`);
|
|
4665
|
+
return {
|
|
4666
|
+
cidr,
|
|
4667
|
+
words,
|
|
4668
|
+
prefix,
|
|
4669
|
+
rfc
|
|
4670
|
+
};
|
|
4671
|
+
}
|
|
4672
|
+
function parseIPv4Address(address) {
|
|
4673
|
+
const parts = address.split(".").map((part) => {
|
|
4674
|
+
if (!/^\d+$/.test(part)) return NaN;
|
|
4675
|
+
return parseInt(part, 10);
|
|
4676
|
+
});
|
|
4677
|
+
if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) return null;
|
|
4678
|
+
return parts;
|
|
4679
|
+
}
|
|
4680
|
+
function parseIPv6Address(address) {
|
|
4681
|
+
const parts = expandIPv6Address(address).split(":");
|
|
4682
|
+
if (parts.length !== 8) return null;
|
|
4683
|
+
const words = parts.map((part) => {
|
|
4684
|
+
if (!/^[0-9a-f]{1,4}$/i.test(part)) return NaN;
|
|
4685
|
+
return parseInt(part, 16);
|
|
4686
|
+
});
|
|
4687
|
+
if (words.some((word) => !Number.isInteger(word) || word < 0 || word > 65535)) return null;
|
|
4688
|
+
return words;
|
|
4689
|
+
}
|
|
4690
|
+
function ipv4PartsToNumber(parts) {
|
|
4691
|
+
return parts[0] * 2 ** 24 + parts[1] * 2 ** 16 + parts[2] * 2 ** 8 + parts[3];
|
|
4692
|
+
}
|
|
4693
|
+
function ipv4FromWords(highWord, lowWord) {
|
|
4694
|
+
return [
|
|
4695
|
+
highWord >> 8,
|
|
4696
|
+
highWord & 255,
|
|
4697
|
+
lowWord >> 8,
|
|
4698
|
+
lowWord & 255
|
|
4699
|
+
].join(".");
|
|
4700
|
+
}
|
|
4701
|
+
function matchesIPv4Prefix(address, prefixBase, prefixLength) {
|
|
4702
|
+
const blockSize = 2 ** (32 - prefixLength);
|
|
4703
|
+
return Math.floor(address / blockSize) === Math.floor(prefixBase / blockSize);
|
|
4704
|
+
}
|
|
4705
|
+
function matchesIPv6Prefix(address, prefixWords, prefixLength) {
|
|
4706
|
+
let remaining = prefixLength;
|
|
4707
|
+
for (let i = 0; i < 8 && remaining > 0; i++) if (remaining >= 16) {
|
|
4708
|
+
if (address[i] !== prefixWords[i]) return false;
|
|
4709
|
+
remaining -= 16;
|
|
4710
|
+
} else {
|
|
4711
|
+
const mask = 65535 << 16 - remaining & 65535;
|
|
4712
|
+
if ((address[i] & mask) !== (prefixWords[i] & mask)) return false;
|
|
4713
|
+
remaining = 0;
|
|
4714
|
+
}
|
|
4715
|
+
return true;
|
|
4716
|
+
}
|
|
4609
4717
|
//#endregion
|
|
4610
4718
|
//#region src/docloader.ts
|
|
4611
4719
|
const logger = (0, _logtape_logtape.getLogger)([
|
package/dist/mod.js
CHANGED
|
@@ -4291,7 +4291,7 @@ const preloadedContexts = {
|
|
|
4291
4291
|
//#endregion
|
|
4292
4292
|
//#region deno.json
|
|
4293
4293
|
var name = "@fedify/vocab-runtime";
|
|
4294
|
-
var version = "2.0.
|
|
4294
|
+
var version = "2.0.19";
|
|
4295
4295
|
//#endregion
|
|
4296
4296
|
//#region src/link.ts
|
|
4297
4297
|
const parametersNeedLowerCase = ["rel", "type"];
|
|
@@ -4579,29 +4579,137 @@ function validatePublicIpAddress(address, family) {
|
|
|
4579
4579
|
throw new UrlError(`Invalid or private address: ${address}`);
|
|
4580
4580
|
}
|
|
4581
4581
|
function isValidPublicIPv4Address(address) {
|
|
4582
|
-
const parts = address
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
if (first === 169 && second === 254) return false;
|
|
4587
|
-
if (first === 172 && second >= 16 && second <= 31) return false;
|
|
4588
|
-
if (first === 192 && second === 168) return false;
|
|
4589
|
-
return true;
|
|
4582
|
+
const parts = parseIPv4Address(address);
|
|
4583
|
+
if (parts == null) return false;
|
|
4584
|
+
const value = ipv4PartsToNumber(parts);
|
|
4585
|
+
return !nonPublicIPv4Prefixes.some(({ base, prefix }) => matchesIPv4Prefix(value, base, prefix));
|
|
4590
4586
|
}
|
|
4591
4587
|
function isValidPublicIPv6Address(address) {
|
|
4592
|
-
|
|
4593
|
-
if (
|
|
4594
|
-
|
|
4595
|
-
|
|
4588
|
+
const words = parseIPv6Address(address);
|
|
4589
|
+
if (words == null) return false;
|
|
4590
|
+
if (nonPublicIPv6Prefixes.some(({ words: prefixWords, prefix }) => matchesIPv6Prefix(words, prefixWords, prefix))) return false;
|
|
4591
|
+
for (const { extractIPv4, prefix, words: prefixWords } of ipv6WithIPv4Prefixes) {
|
|
4592
|
+
if (!matchesIPv6Prefix(words, prefixWords, prefix)) continue;
|
|
4593
|
+
const ipv4Address = extractIPv4(words);
|
|
4594
|
+
if (ipv4Address != null && !isValidPublicIPv4Address(ipv4Address)) return false;
|
|
4595
|
+
}
|
|
4596
|
+
return true;
|
|
4596
4597
|
}
|
|
4597
4598
|
function expandIPv6Address(address) {
|
|
4598
4599
|
address = address.toLowerCase();
|
|
4600
|
+
const ipv4Delimiter = address.lastIndexOf(":");
|
|
4601
|
+
if (address.includes(".") && ipv4Delimiter >= 0) {
|
|
4602
|
+
const ipv4Parts = parseIPv4Address(address.substring(ipv4Delimiter + 1));
|
|
4603
|
+
if (ipv4Parts == null) return address;
|
|
4604
|
+
const high = (ipv4Parts[0] << 8) + ipv4Parts[1];
|
|
4605
|
+
const low = (ipv4Parts[2] << 8) + ipv4Parts[3];
|
|
4606
|
+
address = address.substring(0, ipv4Delimiter + 1) + high.toString(16) + ":" + low.toString(16);
|
|
4607
|
+
}
|
|
4599
4608
|
if (address === "::") return "0000:0000:0000:0000:0000:0000:0000:0000";
|
|
4600
4609
|
if (address.startsWith("::")) address = "0000" + address;
|
|
4601
4610
|
if (address.endsWith("::")) address = address + "0000";
|
|
4602
4611
|
address = address.replace("::", ":0000".repeat(8 - (address.match(/:/g) || []).length) + ":");
|
|
4603
4612
|
return address.split(":").map((part) => part.padStart(4, "0")).join(":");
|
|
4604
4613
|
}
|
|
4614
|
+
const nonPublicIPv4Prefixes = [
|
|
4615
|
+
ipv4Prefix("0.0.0.0/8", "RFC 6890"),
|
|
4616
|
+
ipv4Prefix("10.0.0.0/8", "RFC 1918"),
|
|
4617
|
+
ipv4Prefix("100.64.0.0/10", "RFC 6598"),
|
|
4618
|
+
ipv4Prefix("127.0.0.0/8", "RFC 1122"),
|
|
4619
|
+
ipv4Prefix("169.254.0.0/16", "RFC 3927"),
|
|
4620
|
+
ipv4Prefix("172.16.0.0/12", "RFC 1918"),
|
|
4621
|
+
ipv4Prefix("192.0.0.0/24", "RFC 6890"),
|
|
4622
|
+
ipv4Prefix("192.0.2.0/24", "RFC 5737"),
|
|
4623
|
+
ipv4Prefix("192.88.99.0/24", "RFC 7526"),
|
|
4624
|
+
ipv4Prefix("192.168.0.0/16", "RFC 1918"),
|
|
4625
|
+
ipv4Prefix("198.18.0.0/15", "RFC 2544"),
|
|
4626
|
+
ipv4Prefix("198.51.100.0/24", "RFC 5737"),
|
|
4627
|
+
ipv4Prefix("203.0.113.0/24", "RFC 5737"),
|
|
4628
|
+
ipv4Prefix("224.0.0.0/4", "RFC 5771"),
|
|
4629
|
+
ipv4Prefix("240.0.0.0/4", "RFC 1112")
|
|
4630
|
+
];
|
|
4631
|
+
const nonPublicIPv6Prefixes = [
|
|
4632
|
+
ipv6Prefix("::/16", "RFC 4291"),
|
|
4633
|
+
ipv6Prefix("2001::/32", "RFC 4380"),
|
|
4634
|
+
ipv6Prefix("2002::/16", "RFC 3056"),
|
|
4635
|
+
ipv6Prefix("64:ff9b:1::/48", "RFC 8215"),
|
|
4636
|
+
ipv6Prefix("fc00::/7", "RFC 4193"),
|
|
4637
|
+
ipv6Prefix("fe80::/10", "RFC 4291"),
|
|
4638
|
+
ipv6Prefix("ff00::/8", "RFC 4291")
|
|
4639
|
+
];
|
|
4640
|
+
const ipv6WithIPv4Prefixes = [{
|
|
4641
|
+
...ipv6Prefix("64:ff9b::/96", "RFC 6052"),
|
|
4642
|
+
extractIPv4: (words) => ipv4FromWords(words[6], words[7])
|
|
4643
|
+
}];
|
|
4644
|
+
function ipv4Prefix(cidr, rfc) {
|
|
4645
|
+
const [address, prefixText] = cidr.split("/");
|
|
4646
|
+
const prefix = parseInt(prefixText, 10);
|
|
4647
|
+
const parts = parseIPv4Address(address);
|
|
4648
|
+
if (parts == null || !Number.isInteger(prefix) || prefix < 0 || prefix > 32) throw new Error(`Invalid IPv4 prefix: ${cidr}`);
|
|
4649
|
+
return {
|
|
4650
|
+
cidr,
|
|
4651
|
+
base: ipv4PartsToNumber(parts),
|
|
4652
|
+
prefix,
|
|
4653
|
+
rfc
|
|
4654
|
+
};
|
|
4655
|
+
}
|
|
4656
|
+
function ipv6Prefix(cidr, rfc) {
|
|
4657
|
+
const [address, prefixText] = cidr.split("/");
|
|
4658
|
+
const prefix = parseInt(prefixText, 10);
|
|
4659
|
+
const words = parseIPv6Address(address);
|
|
4660
|
+
if (words == null || !Number.isInteger(prefix) || prefix < 0 || prefix > 128) throw new Error(`Invalid IPv6 prefix: ${cidr}`);
|
|
4661
|
+
return {
|
|
4662
|
+
cidr,
|
|
4663
|
+
words,
|
|
4664
|
+
prefix,
|
|
4665
|
+
rfc
|
|
4666
|
+
};
|
|
4667
|
+
}
|
|
4668
|
+
function parseIPv4Address(address) {
|
|
4669
|
+
const parts = address.split(".").map((part) => {
|
|
4670
|
+
if (!/^\d+$/.test(part)) return NaN;
|
|
4671
|
+
return parseInt(part, 10);
|
|
4672
|
+
});
|
|
4673
|
+
if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) return null;
|
|
4674
|
+
return parts;
|
|
4675
|
+
}
|
|
4676
|
+
function parseIPv6Address(address) {
|
|
4677
|
+
const parts = expandIPv6Address(address).split(":");
|
|
4678
|
+
if (parts.length !== 8) return null;
|
|
4679
|
+
const words = parts.map((part) => {
|
|
4680
|
+
if (!/^[0-9a-f]{1,4}$/i.test(part)) return NaN;
|
|
4681
|
+
return parseInt(part, 16);
|
|
4682
|
+
});
|
|
4683
|
+
if (words.some((word) => !Number.isInteger(word) || word < 0 || word > 65535)) return null;
|
|
4684
|
+
return words;
|
|
4685
|
+
}
|
|
4686
|
+
function ipv4PartsToNumber(parts) {
|
|
4687
|
+
return parts[0] * 2 ** 24 + parts[1] * 2 ** 16 + parts[2] * 2 ** 8 + parts[3];
|
|
4688
|
+
}
|
|
4689
|
+
function ipv4FromWords(highWord, lowWord) {
|
|
4690
|
+
return [
|
|
4691
|
+
highWord >> 8,
|
|
4692
|
+
highWord & 255,
|
|
4693
|
+
lowWord >> 8,
|
|
4694
|
+
lowWord & 255
|
|
4695
|
+
].join(".");
|
|
4696
|
+
}
|
|
4697
|
+
function matchesIPv4Prefix(address, prefixBase, prefixLength) {
|
|
4698
|
+
const blockSize = 2 ** (32 - prefixLength);
|
|
4699
|
+
return Math.floor(address / blockSize) === Math.floor(prefixBase / blockSize);
|
|
4700
|
+
}
|
|
4701
|
+
function matchesIPv6Prefix(address, prefixWords, prefixLength) {
|
|
4702
|
+
let remaining = prefixLength;
|
|
4703
|
+
for (let i = 0; i < 8 && remaining > 0; i++) if (remaining >= 16) {
|
|
4704
|
+
if (address[i] !== prefixWords[i]) return false;
|
|
4705
|
+
remaining -= 16;
|
|
4706
|
+
} else {
|
|
4707
|
+
const mask = 65535 << 16 - remaining & 65535;
|
|
4708
|
+
if ((address[i] & mask) !== (prefixWords[i] & mask)) return false;
|
|
4709
|
+
remaining = 0;
|
|
4710
|
+
}
|
|
4711
|
+
return true;
|
|
4712
|
+
}
|
|
4605
4713
|
//#endregion
|
|
4606
4714
|
//#region src/docloader.ts
|
|
4607
4715
|
const logger = getLogger([
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-Do9eywBl.cjs");
|
|
2
|
-
const require_request = require("./request-
|
|
2
|
+
const require_request = require("./request-CuxFKHwd.cjs");
|
|
3
3
|
const require_link = require("./link-B6ZWBZhf.cjs");
|
|
4
|
-
const require_url = require("./url-
|
|
4
|
+
const require_url = require("./url-CjF2c1ff.cjs");
|
|
5
5
|
let node_assert = require("node:assert");
|
|
6
6
|
let node_test = require("node:test");
|
|
7
7
|
let _logtape_logtape = require("@logtape/logtape");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-
|
|
1
|
+
import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-BUC0LY4m.mjs";
|
|
2
2
|
import { t as HttpHeaderLink } from "./link-B8JGXSS2.mjs";
|
|
3
|
-
import { a as validatePublicUrl, t as UrlError } from "./url-
|
|
3
|
+
import { a as validatePublicUrl, t as UrlError } from "./url-Cyu1RKqd.mjs";
|
|
4
4
|
import { deepStrictEqual, ok, rejects } from "node:assert";
|
|
5
5
|
import { test } from "node:test";
|
|
6
6
|
import { getLogger } from "@logtape/logtape";
|
|
@@ -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-CuxFKHwd.cjs");
|
|
3
3
|
let node_assert = require("node:assert");
|
|
4
4
|
let node_test = require("node:test");
|
|
5
5
|
let node_process = require("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-BUC0LY4m.mjs";
|
|
2
2
|
import { deepStrictEqual } from "node:assert";
|
|
3
3
|
import { test } from "node:test";
|
|
4
4
|
import process from "node:process";
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
require("./chunk-Do9eywBl.cjs");
|
|
2
|
+
let node_dns_promises = require("node:dns/promises");
|
|
3
|
+
let node_net = require("node:net");
|
|
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.slice(1, -1);
|
|
19
|
+
if (hostname === "localhost") throw new UrlError("Localhost is not allowed");
|
|
20
|
+
const hostnameFamily = (0, node_net.isIP)(hostname);
|
|
21
|
+
if (hostnameFamily !== 0) {
|
|
22
|
+
validatePublicIpAddress(hostname, hostnameFamily);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if ("Deno" in globalThis && !(0, node_net.isIP)(hostname)) {
|
|
26
|
+
if ((await Deno.permissions.query({ name: "net" })).state !== "granted") return;
|
|
27
|
+
}
|
|
28
|
+
if ("Bun" in globalThis) {
|
|
29
|
+
if (hostname === "example.com" || hostname.endsWith(".example.com")) return;
|
|
30
|
+
else if (hostname === "fedify-test.internal") throw new UrlError("Invalid or private address: fedify-test.internal");
|
|
31
|
+
}
|
|
32
|
+
let addresses;
|
|
33
|
+
try {
|
|
34
|
+
addresses = await (0, node_dns_promises.lookup)(hostname, { all: true });
|
|
35
|
+
} catch {
|
|
36
|
+
addresses = [];
|
|
37
|
+
}
|
|
38
|
+
for (const { address, family } of addresses) validatePublicIpAddress(address, family);
|
|
39
|
+
}
|
|
40
|
+
function validatePublicIpAddress(address, family) {
|
|
41
|
+
if (family === 4 && isValidPublicIPv4Address(address) || family === 6 && isValidPublicIPv6Address(address)) return;
|
|
42
|
+
throw new UrlError(`Invalid or private address: ${address}`);
|
|
43
|
+
}
|
|
44
|
+
function isValidPublicIPv4Address(address) {
|
|
45
|
+
const parts = parseIPv4Address(address);
|
|
46
|
+
if (parts == null) return false;
|
|
47
|
+
const value = ipv4PartsToNumber(parts);
|
|
48
|
+
return !nonPublicIPv4Prefixes.some(({ base, prefix }) => matchesIPv4Prefix(value, base, prefix));
|
|
49
|
+
}
|
|
50
|
+
function isValidPublicIPv6Address(address) {
|
|
51
|
+
const words = parseIPv6Address(address);
|
|
52
|
+
if (words == null) return false;
|
|
53
|
+
if (nonPublicIPv6Prefixes.some(({ words: prefixWords, prefix }) => matchesIPv6Prefix(words, prefixWords, prefix))) return false;
|
|
54
|
+
for (const { extractIPv4, prefix, words: prefixWords } of ipv6WithIPv4Prefixes) {
|
|
55
|
+
if (!matchesIPv6Prefix(words, prefixWords, prefix)) continue;
|
|
56
|
+
const ipv4Address = extractIPv4(words);
|
|
57
|
+
if (ipv4Address != null && !isValidPublicIPv4Address(ipv4Address)) return false;
|
|
58
|
+
}
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
function expandIPv6Address(address) {
|
|
62
|
+
address = address.toLowerCase();
|
|
63
|
+
const ipv4Delimiter = address.lastIndexOf(":");
|
|
64
|
+
if (address.includes(".") && ipv4Delimiter >= 0) {
|
|
65
|
+
const ipv4Parts = parseIPv4Address(address.substring(ipv4Delimiter + 1));
|
|
66
|
+
if (ipv4Parts == null) return address;
|
|
67
|
+
const high = (ipv4Parts[0] << 8) + ipv4Parts[1];
|
|
68
|
+
const low = (ipv4Parts[2] << 8) + ipv4Parts[3];
|
|
69
|
+
address = address.substring(0, ipv4Delimiter + 1) + high.toString(16) + ":" + low.toString(16);
|
|
70
|
+
}
|
|
71
|
+
if (address === "::") return "0000:0000:0000:0000:0000:0000:0000:0000";
|
|
72
|
+
if (address.startsWith("::")) address = "0000" + address;
|
|
73
|
+
if (address.endsWith("::")) address = address + "0000";
|
|
74
|
+
address = address.replace("::", ":0000".repeat(8 - (address.match(/:/g) || []).length) + ":");
|
|
75
|
+
return address.split(":").map((part) => part.padStart(4, "0")).join(":");
|
|
76
|
+
}
|
|
77
|
+
const nonPublicIPv4Prefixes = [
|
|
78
|
+
ipv4Prefix("0.0.0.0/8", "RFC 6890"),
|
|
79
|
+
ipv4Prefix("10.0.0.0/8", "RFC 1918"),
|
|
80
|
+
ipv4Prefix("100.64.0.0/10", "RFC 6598"),
|
|
81
|
+
ipv4Prefix("127.0.0.0/8", "RFC 1122"),
|
|
82
|
+
ipv4Prefix("169.254.0.0/16", "RFC 3927"),
|
|
83
|
+
ipv4Prefix("172.16.0.0/12", "RFC 1918"),
|
|
84
|
+
ipv4Prefix("192.0.0.0/24", "RFC 6890"),
|
|
85
|
+
ipv4Prefix("192.0.2.0/24", "RFC 5737"),
|
|
86
|
+
ipv4Prefix("192.88.99.0/24", "RFC 7526"),
|
|
87
|
+
ipv4Prefix("192.168.0.0/16", "RFC 1918"),
|
|
88
|
+
ipv4Prefix("198.18.0.0/15", "RFC 2544"),
|
|
89
|
+
ipv4Prefix("198.51.100.0/24", "RFC 5737"),
|
|
90
|
+
ipv4Prefix("203.0.113.0/24", "RFC 5737"),
|
|
91
|
+
ipv4Prefix("224.0.0.0/4", "RFC 5771"),
|
|
92
|
+
ipv4Prefix("240.0.0.0/4", "RFC 1112")
|
|
93
|
+
];
|
|
94
|
+
const nonPublicIPv6Prefixes = [
|
|
95
|
+
ipv6Prefix("::/16", "RFC 4291"),
|
|
96
|
+
ipv6Prefix("2001::/32", "RFC 4380"),
|
|
97
|
+
ipv6Prefix("2002::/16", "RFC 3056"),
|
|
98
|
+
ipv6Prefix("64:ff9b:1::/48", "RFC 8215"),
|
|
99
|
+
ipv6Prefix("fc00::/7", "RFC 4193"),
|
|
100
|
+
ipv6Prefix("fe80::/10", "RFC 4291"),
|
|
101
|
+
ipv6Prefix("ff00::/8", "RFC 4291")
|
|
102
|
+
];
|
|
103
|
+
const ipv6WithIPv4Prefixes = [{
|
|
104
|
+
...ipv6Prefix("64:ff9b::/96", "RFC 6052"),
|
|
105
|
+
extractIPv4: (words) => ipv4FromWords(words[6], words[7])
|
|
106
|
+
}];
|
|
107
|
+
function ipv4Prefix(cidr, rfc) {
|
|
108
|
+
const [address, prefixText] = cidr.split("/");
|
|
109
|
+
const prefix = parseInt(prefixText, 10);
|
|
110
|
+
const parts = parseIPv4Address(address);
|
|
111
|
+
if (parts == null || !Number.isInteger(prefix) || prefix < 0 || prefix > 32) throw new Error(`Invalid IPv4 prefix: ${cidr}`);
|
|
112
|
+
return {
|
|
113
|
+
cidr,
|
|
114
|
+
base: ipv4PartsToNumber(parts),
|
|
115
|
+
prefix,
|
|
116
|
+
rfc
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function ipv6Prefix(cidr, rfc) {
|
|
120
|
+
const [address, prefixText] = cidr.split("/");
|
|
121
|
+
const prefix = parseInt(prefixText, 10);
|
|
122
|
+
const words = parseIPv6Address(address);
|
|
123
|
+
if (words == null || !Number.isInteger(prefix) || prefix < 0 || prefix > 128) throw new Error(`Invalid IPv6 prefix: ${cidr}`);
|
|
124
|
+
return {
|
|
125
|
+
cidr,
|
|
126
|
+
words,
|
|
127
|
+
prefix,
|
|
128
|
+
rfc
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function parseIPv4Address(address) {
|
|
132
|
+
const parts = address.split(".").map((part) => {
|
|
133
|
+
if (!/^\d+$/.test(part)) return NaN;
|
|
134
|
+
return parseInt(part, 10);
|
|
135
|
+
});
|
|
136
|
+
if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) return null;
|
|
137
|
+
return parts;
|
|
138
|
+
}
|
|
139
|
+
function parseIPv6Address(address) {
|
|
140
|
+
const parts = expandIPv6Address(address).split(":");
|
|
141
|
+
if (parts.length !== 8) return null;
|
|
142
|
+
const words = parts.map((part) => {
|
|
143
|
+
if (!/^[0-9a-f]{1,4}$/i.test(part)) return NaN;
|
|
144
|
+
return parseInt(part, 16);
|
|
145
|
+
});
|
|
146
|
+
if (words.some((word) => !Number.isInteger(word) || word < 0 || word > 65535)) return null;
|
|
147
|
+
return words;
|
|
148
|
+
}
|
|
149
|
+
function ipv4PartsToNumber(parts) {
|
|
150
|
+
return parts[0] * 2 ** 24 + parts[1] * 2 ** 16 + parts[2] * 2 ** 8 + parts[3];
|
|
151
|
+
}
|
|
152
|
+
function ipv4FromWords(highWord, lowWord) {
|
|
153
|
+
return [
|
|
154
|
+
highWord >> 8,
|
|
155
|
+
highWord & 255,
|
|
156
|
+
lowWord >> 8,
|
|
157
|
+
lowWord & 255
|
|
158
|
+
].join(".");
|
|
159
|
+
}
|
|
160
|
+
function matchesIPv4Prefix(address, prefixBase, prefixLength) {
|
|
161
|
+
const blockSize = 2 ** (32 - prefixLength);
|
|
162
|
+
return Math.floor(address / blockSize) === Math.floor(prefixBase / blockSize);
|
|
163
|
+
}
|
|
164
|
+
function matchesIPv6Prefix(address, prefixWords, prefixLength) {
|
|
165
|
+
let remaining = prefixLength;
|
|
166
|
+
for (let i = 0; i < 8 && remaining > 0; i++) if (remaining >= 16) {
|
|
167
|
+
if (address[i] !== prefixWords[i]) return false;
|
|
168
|
+
remaining -= 16;
|
|
169
|
+
} else {
|
|
170
|
+
const mask = 65535 << 16 - remaining & 65535;
|
|
171
|
+
if ((address[i] & mask) !== (prefixWords[i] & mask)) return false;
|
|
172
|
+
remaining = 0;
|
|
173
|
+
}
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
//#endregion
|
|
177
|
+
Object.defineProperty(exports, "UrlError", {
|
|
178
|
+
enumerable: true,
|
|
179
|
+
get: function() {
|
|
180
|
+
return UrlError;
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
Object.defineProperty(exports, "expandIPv6Address", {
|
|
184
|
+
enumerable: true,
|
|
185
|
+
get: function() {
|
|
186
|
+
return expandIPv6Address;
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
Object.defineProperty(exports, "isValidPublicIPv4Address", {
|
|
190
|
+
enumerable: true,
|
|
191
|
+
get: function() {
|
|
192
|
+
return isValidPublicIPv4Address;
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
Object.defineProperty(exports, "isValidPublicIPv6Address", {
|
|
196
|
+
enumerable: true,
|
|
197
|
+
get: function() {
|
|
198
|
+
return isValidPublicIPv6Address;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
Object.defineProperty(exports, "validatePublicUrl", {
|
|
202
|
+
enumerable: true,
|
|
203
|
+
get: function() {
|
|
204
|
+
return validatePublicUrl;
|
|
205
|
+
}
|
|
206
|
+
});
|