From 3e4d853b30831220dee681968caa6a983402118a Mon Sep 17 00:00:00 2001 From: Franciny Rojas Date: Tue, 1 Sep 2026 12:05:09 -0300 Subject: [PATCH 1/2] fix: validate CIDR prefix lengths in match --- lib/ipaddr.js | 10 ++++ test/ipaddr.test.js | 110 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) diff --git a/lib/ipaddr.js b/lib/ipaddr.js index da06924..4e210ed 100644 --- a/lib/ipaddr.js +++ b/lib/ipaddr.js @@ -108,6 +108,16 @@ function matchCIDR (first, second, partSize, cidrBits) { throw new Error('ipaddr: cannot match CIDR for objects with different lengths'); } + const maxCidrBits = first.length * partSize; + + if ( + !Number.isInteger(cidrBits) || + cidrBits < 0 || + cidrBits > maxCidrBits + ) { + throw new Error('ipaddr: invalid CIDR prefix length'); + } + let part = 0; let shift; diff --git a/test/ipaddr.test.js b/test/ipaddr.test.js index ae12d1b..8b0920b 100644 --- a/test/ipaddr.test.js +++ b/test/ipaddr.test.js @@ -106,6 +106,39 @@ describe('ipaddr', () => { equal(addr.match(addr, 32), true); }) + it('rejects invalid IPv4 CIDR prefix lengths in match', () => { + const address = IPv4.parse('8.8.8.8'); + const network = IPv4.parse('192.168.1.0'); + + throws(() => { + address.match(network, -1); + }); + + throws(() => { + address.match(network, -0.5); + }); + + throws(() => { + address.match(network, NaN); + }); + + throws(() => { + address.match(network, Infinity); + }); + + throws(() => { + address.match(network, -Infinity); + }); + + throws(() => { + address.match(network, 24.9); + }); + + throws(() => { + address.match(network, 33); + }); + }) + it('parses CIDR reversible', () => { equal(parseCIDR('1.2.3.4/24').toString(), '1.2.3.4/24'); equal(parseCIDR('::1%zone/24').toString(), '::1%zone/24'); @@ -179,6 +212,39 @@ describe('ipaddr', () => { }); }) + it('rejects invalid IPv6 CIDR prefix lengths in match', () => { + const address = IPv6.parse('2001:db8::1'); + const network = IPv6.parse('2001:db8::'); + + throws(() => { + address.match(network, -1); + }); + + throws(() => { + address.match(network, -0.5); + }); + + throws(() => { + address.match(network, NaN); + }); + + throws(() => { + address.match(network, Infinity); + }); + + throws(() => { + address.match(network, -Infinity); + }); + + throws(() => { + address.match(network, 64.9); + }); + + throws(() => { + address.match(network, 129); + }); + }) + it('can construct IPv6 from 8bit parts', () => { doesNotThrow(() => { new IPv6([0x20, 0x01, 0xd, 0xb8, 0xf5, 0x3a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); @@ -566,6 +632,50 @@ describe('ipaddr', () => { equal(subnetMatch(new IPv6([0x2001, 1, 2, 3, 0, 0, 0, 1]), rangelist, false), 'dual64'); }) + it('subnetMatch rejects invalid CIDR prefix lengths', () => { + const address = IPv4.parse('8.8.8.8'); + const network = IPv4.parse('192.168.1.0'); + + throws(() => { + subnetMatch( + address, + { + internal: [ + network, + -1 + ] + }, + 'external' + ); + }); + + throws(() => { + subnetMatch( + address, + { + internal: [ + network, + NaN + ] + }, + 'external' + ); + }); + + throws(() => { + subnetMatch( + address, + { + internal: [ + network, + Infinity + ] + }, + 'external' + ); + }); + }) + it('is able to determine IP address type from byte array input', () => { equal(fromByteArray([0x7f, 0, 0, 1]).kind(), 'ipv4'); equal(fromByteArray([0x20, 0x01, 0xd, 0xb8, 0xf5, 0x3a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]).kind(), 'ipv6'); From 2e48170326f0b114d1f5b365d19bfd34e41e1800 Mon Sep 17 00:00:00 2001 From: Franciny Rojas Date: Tue, 1 Sep 2026 12:25:01 -0300 Subject: [PATCH 2/2] test: reformat CIDR prefix validation cases --- test/ipaddr.test.js | 109 +++++++------------------------------------- 1 file changed, 17 insertions(+), 92 deletions(-) diff --git a/test/ipaddr.test.js b/test/ipaddr.test.js index 8b0920b..092ccb1 100644 --- a/test/ipaddr.test.js +++ b/test/ipaddr.test.js @@ -110,33 +110,13 @@ describe('ipaddr', () => { const address = IPv4.parse('8.8.8.8'); const network = IPv4.parse('192.168.1.0'); - throws(() => { - address.match(network, -1); - }); - - throws(() => { - address.match(network, -0.5); - }); - - throws(() => { - address.match(network, NaN); - }); - - throws(() => { - address.match(network, Infinity); - }); - - throws(() => { - address.match(network, -Infinity); - }); - - throws(() => { - address.match(network, 24.9); - }); - - throws(() => { - address.match(network, 33); - }); + throws(() => address.match(network, -1)); + throws(() => address.match(network, -0.5)); + throws(() => address.match(network, NaN)); + throws(() => address.match(network, Infinity)); + throws(() => address.match(network, -Infinity)); + throws(() => address.match(network, 24.9)); + throws(() => address.match(network, 33)); }) it('parses CIDR reversible', () => { @@ -216,33 +196,13 @@ describe('ipaddr', () => { const address = IPv6.parse('2001:db8::1'); const network = IPv6.parse('2001:db8::'); - throws(() => { - address.match(network, -1); - }); - - throws(() => { - address.match(network, -0.5); - }); - - throws(() => { - address.match(network, NaN); - }); - - throws(() => { - address.match(network, Infinity); - }); - - throws(() => { - address.match(network, -Infinity); - }); - - throws(() => { - address.match(network, 64.9); - }); - - throws(() => { - address.match(network, 129); - }); + throws(() => address.match(network, -1)); + throws(() => address.match(network, -0.5)); + throws(() => address.match(network, NaN)); + throws(() => address.match(network, Infinity)); + throws(() => address.match(network, -Infinity)); + throws(() => address.match(network, 64.9)); + throws(() => address.match(network, 129)); }) it('can construct IPv6 from 8bit parts', () => { @@ -636,44 +596,9 @@ describe('ipaddr', () => { const address = IPv4.parse('8.8.8.8'); const network = IPv4.parse('192.168.1.0'); - throws(() => { - subnetMatch( - address, - { - internal: [ - network, - -1 - ] - }, - 'external' - ); - }); - - throws(() => { - subnetMatch( - address, - { - internal: [ - network, - NaN - ] - }, - 'external' - ); - }); - - throws(() => { - subnetMatch( - address, - { - internal: [ - network, - Infinity - ] - }, - 'external' - ); - }); + throws(() => subnetMatch(address, { internal: [network, -1] }, 'external')); + throws(() => subnetMatch(address, { internal: [network, NaN] }, 'external')); + throws(() => subnetMatch(address, { internal: [network, Infinity] }, 'external')); }) it('is able to determine IP address type from byte array input', () => {