From 2043d7ee29a4b10a0fc294db9520973fc550e913 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 18 Sep 2026 07:32:53 +0000 Subject: bump dependencies --- Gemfile.lock | 2 +- package-lock.json | 26 +++++++++++----------- src/assets/js/bundle.js | 58 ++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 64 insertions(+), 22 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 2b5fe72..c4b6c29 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -12,7 +12,7 @@ GEM eventmachine (>= 0.12.9) http_parser.rb (~> 0) eventmachine (1.2.7) - faraday (2.14.3) + faraday (2.14.4) faraday-net_http (>= 2.0, < 3.5) json logger diff --git a/package-lock.json b/package-lock.json index 80350f4..6f3fe57 100644 --- a/package-lock.json +++ b/package-lock.json @@ -131,9 +131,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -1034,9 +1034,9 @@ } }, "node_modules/ip-address": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.0.tgz", - "integrity": "sha512-BGFsyJd5mpXp3rK6jIdADLNgpJUK1jnjzvYF8lK+VyDab9JAmqN0YOKDdP17HlgKb2+ehPgDc8EtnRLbGCAMhA==", + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.2.tgz", + "integrity": "sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==", "license": "MIT", "engines": { "node": ">= 12" @@ -1077,12 +1077,12 @@ } }, "node_modules/is-core-module": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", - "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.17.0.tgz", + "integrity": "sha512-J/vG0zBCbIKOQFfufSwyXdMrsohyJIUNkrnmo6WZGzoM7tr/lsbfW5b2BvisL6zsyMzK9UxV9L6c7AoFbyXHOA==", "license": "MIT", "dependencies": { - "hasown": "^2.0.3" + "hasown": "^2.0.4" }, "engines": { "node": ">= 0.4" @@ -2057,9 +2057,9 @@ "license": "MIT" }, "node_modules/which-typed-array": { - "version": "1.1.22", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz", - "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==", + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.23.tgz", + "integrity": "sha512-JMh8aK+1B/0bk/YNupICmH5MgCq6yNLKYQUfsZtSDLLCCmxUkHjUY+oOlIa90lO7lHN1woAfpenLdlKpXy9o+A==", "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", diff --git a/src/assets/js/bundle.js b/src/assets/js/bundle.js index 8327238..5764765 100644 --- a/src/assets/js/bundle.js +++ b/src/assets/js/bundle.js @@ -52,8 +52,20 @@ function isInSubnet(address) { * {@link isInSubnet} when classifying a single address — notably when the * address came from untrusted input and the result backs a trust-boundary * decision such as an SSRF allow/deny filter. + * + * An address of one family is never inside a network of the other, so an + * `Address4` against an `Address6` (or the reverse) is `false`. To compare + * across families, convert first: `Address6.fromAddress4()`, `to4()`, or + * `toAddress4Nat64()`. */ function isHostInSubnet(address) { + // mask() is a bit string of the family's width, and the leading bits of a + // 32-bit string can coincide with those of a 128-bit one (a00::1 and + // 10.0.0.0/8 both mask to 00001010), so the widths must agree before the + // strings are compared. + if (this.binaryZeroPad().length !== address.binaryZeroPad().length) { + return false; + } return this.mask(address.subnetMask) === address.mask(); } /** @@ -205,7 +217,9 @@ class Address4 { */ this.isCorrect = isCorrect4; /** - * Returns true if the given address is in the subnet of the current address + * Returns true if the given address is in the subnet of the current address. + * An `Address6` is never in the subnet of an `Address4`; convert with + * `to4()` or `Address6.fromAddress4()` to compare across families. * @returns {boolean} */ this.isInSubnet = common.isInSubnet; @@ -215,6 +229,8 @@ class Address4 { * when classifying a single address, so the answer doesn't change with the * CIDR suffix the caller happened to write — notably when the address came * from untrusted input and the result backs a trust-boundary decision. + * An `Address6` is never in the subnet of an `Address4`; convert with + * `to4()` or `Address6.fromAddress4()` to compare across families. * @returns {boolean} */ this.isHostInSubnet = common.isHostInSubnet; @@ -229,6 +245,13 @@ class Address4 { } address = address.replace(constants.RE_SUBNET_STRING, ''); } + // Four three-digit octets and three dots: the longest well-formed address + // is 15 characters. Longer input is rejected before parsing, as Address6 + // does at its own limit. + const longest = constants.GROUPS * 4 - 1; + if (address.length > longest) { + throw new address_error_1.AddressError(`IPv4 addresses are at most ${longest} characters.`); + } this.addressMinusSuffix = address; this.parsedAddress = this.parse(address); } @@ -370,16 +393,20 @@ class Address4 { return Address4.fromHex(integer.toString(16).padStart(8, '0')); } /** - * Return an address from in-addr.arpa form + * Return an address from in-addr.arpa form: the four octets reversed, with + * or without the `.in-addr.arpa` suffix and root dot, in any case. Throws + * `AddressError` unless the reversed labels form a valid IPv4 address, so + * `fromArpa(x.reverseForm())` round-trips {@link reverseForm}. * @param {string} arpaFormAddress - an 'in-addr.arpa' form ipv4 address * @returns {Adress4} * @example - * var address = Address4.fromArpa(42.2.0.192.in-addr.arpa.) + * var address = Address4.fromArpa('42.2.0.192.in-addr.arpa.') * address.correctForm(); // '192.0.2.42' */ static fromArpa(arpaFormAddress) { - // remove ending ".in-addr.arpa." or just "." - const leader = arpaFormAddress.replace(/(\.in-addr\.arpa)?\.$/, ''); + // remove an ending ".in-addr.arpa", in any case and with or without the + // root dot, as Address6.fromArpa does for ".ip6.arpa" + const leader = arpaFormAddress.replace(/(\.in-addr\.arpa)?\.?$/i, ''); const address = leader.split('.').reverse().join('.'); return new Address4(address); } @@ -830,7 +857,9 @@ class Address6 { this.zone = ''; // #region Attributes /** - * Returns true if the given address is in the subnet of the current address + * Returns true if the given address is in the subnet of the current address. + * An `Address4` is never in the subnet of an `Address6`; convert with + * `Address6.fromAddress4()` or `to4()` to compare across families. * @returns {boolean} */ this.isInSubnet = common.isInSubnet; @@ -840,6 +869,8 @@ class Address6 { * when classifying a single address, so the answer doesn't change with the * CIDR suffix the caller happened to write — notably when the address came * from untrusted input and the result backs a trust-boundary decision. + * An `Address4` is never in the subnet of an `Address6`; convert with + * `Address6.fromAddress4()` or `to4()` to compare across families. * @returns {boolean} */ this.isHostInSubnet = common.isHostInSubnet; @@ -878,6 +909,16 @@ class Address6 { this.zone = zone[0]; address = address.replace(constants6.RE_ZONE_STRING, ''); } + // The longest well-formed address is all but the last two groups written + // as four hex digits with their colons, then a 15-character dotted quad: + // 5 * (groups - 2) + 15, which is 45 for eight groups, the same line + // CPython's ipaddress module draws. Rejecting longer input here keeps the + // parse diagnostics, which wrap every offending character in a span, + // proportional to an address rather than to whatever was passed in. + const longest = this.groups * 5 + 5; + if (address.length > longest) { + throw new address_error_1.AddressError(`IPv6 addresses are at most ${longest} characters.`); + } this.addressMinusSuffix = address; this.parsedAddress = this.parse(this.addressMinusSuffix); } @@ -1089,8 +1130,9 @@ class Address6 { * Address6.fromArpa('8.b.d.0.1.0.0.2.ip6.arpa.').networkForm(); // '2001:db8::/32' */ static fromArpa(arpaFormAddress) { - // remove an ending ".ip6.arpa", with or without the root dot - const nibbles = arpaFormAddress.replace(/(\.ip6\.arpa)?\.?$/, ''); + // remove an ending ".ip6.arpa", in any case and with or without the root + // dot + const nibbles = arpaFormAddress.replace(/(\.ip6\.arpa)?\.?$/i, ''); if (!/^[0-9a-f](\.[0-9a-f]){0,31}$/i.test(nibbles)) { throw new address_error_1.AddressError("Invalid 'ip6.arpa' form."); } -- cgit v1.3