@fedify/vocab-runtime 2.0.18 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/vocab-runtime",
3
- "version": "2.0.18",
3
+ "version": "2.0.19",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./src/mod.ts",
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.18";
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.split(".");
4587
- const first = parseInt(parts[0]);
4588
- if (first === 0 || first === 10 || first === 127) return false;
4589
- const second = parseInt(parts[1]);
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
- address = expandIPv6Address(address);
4597
- if (address.at(4) !== ":") return false;
4598
- const firstWord = parseInt(address.substring(0, 4), 16);
4599
- return !(firstWord >= 64512 && firstWord <= 65023 || firstWord >= 65152 && firstWord <= 65215 || firstWord === 0 || firstWord >= 65280);
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.18";
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.split(".");
4583
- const first = parseInt(parts[0]);
4584
- if (first === 0 || first === 10 || first === 127) return false;
4585
- const second = parseInt(parts[1]);
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
- address = expandIPv6Address(address);
4593
- if (address.at(4) !== ":") return false;
4594
- const firstWord = parseInt(address.substring(0, 4), 16);
4595
- return !(firstWord >= 64512 && firstWord <= 65023 || firstWord >= 65152 && firstWord <= 65215 || firstWord === 0 || firstWord >= 65280);
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-BkQQUB49.cjs");
2
+ const require_request = require("./request-CuxFKHwd.cjs");
3
3
  const require_link = require("./link-B6ZWBZhf.cjs");
4
- const require_url = require("./url-SR63sukb.cjs");
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-BVBXquAZ.mjs";
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-DftNtd3d.mjs";
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,7 +1,7 @@
1
1
  import process from "node:process";
2
2
  //#region deno.json
3
3
  var name = "@fedify/vocab-runtime";
4
- var version = "2.0.18";
4
+ var version = "2.0.19";
5
5
  //#endregion
6
6
  //#region src/request.ts
7
7
  /**
@@ -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.18";
6
+ var version = "2.0.19";
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-BkQQUB49.cjs");
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-BVBXquAZ.mjs";
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
+ });