Skip to content

Commit 265bda4

Browse files
committed
net: fix BlockList.fromJSON for IPv4-mapped IPv6 rules
BlockList.toJSON() serializes IPv4-mapped IPv6 addresses in mixed dotted-quad form (e.g. "::ffff:192.0.2.128"), but the fromJSON() rule parser's IPv6 regexes excluded "." and capped the length at 39, so such rules were silently corrupted or dropped on a toJSON()/fromJSON() round-trip. Allow "." and raise the cap to 45 (the longest mixed form). Signed-off-by: Daijiro Wachi <daijiro.wachi@gmail.com>
1 parent c612f35 commit 265bda4

2 files changed

Lines changed: 40 additions & 3 deletions

File tree

‎lib/internal/blocklist.js‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ class BlockList {
199199
// - 2001:db8:85a3::8a2e:370:7334/64 (compressed)
200200
// - 2001:db8:85a3::192.0.2.128/64 (mixed)
201201
const ipv6SubnetMatch = item.match(
202-
/Subnet: IPv6 ([0-9a-fA-F:]{1,39})\/([0-9]{1,3})/i,
202+
/Subnet: IPv6 ([0-9a-fA-F:.]{1,45})\/([0-9]{1,3})/i,
203203
);
204204
if (ipv6SubnetMatch) {
205205
const { 1: network, 2: prefix } = ipv6SubnetMatch;
@@ -212,7 +212,7 @@ class BlockList {
212212
// - 2001:0db8:85a3:0000:0000:8a2e:0370:7334 (full)
213213
// - 2001:db8:85a3::8a2e:370:7334 (compressed)
214214
// - 2001:db8:85a3::192.0.2.128 (mixed)
215-
const ipv6AddressMatch = item.match(/Address: IPv6 ([0-9a-fA-F:]{1,39})/i);
215+
const ipv6AddressMatch = item.match(/Address: IPv6 ([0-9a-fA-F:.]{1,45})/i);
216216
if (ipv6AddressMatch) {
217217
const { 1: address } = ipv6AddressMatch;
218218
this.addAddress(address, 'ipv6');
@@ -224,7 +224,7 @@ class BlockList {
224224
// - 2001:0db8:85a3:0000:0000:8a2e:0370:7334-2001:0db8:85a3:0000:0000:8a2e:0370:7335 (full)
225225
// - 2001:db8:85a3::8a2e:370:7334-2001:db8:85a3::8a2e:370:7335 (compressed)
226226
// - 2001:db8:85a3::192.0.2.128-2001:db8:85a3::192.0.2.129 (mixed)
227-
const ipv6RangeMatch = item.match(/Range: IPv6 ([0-9a-fA-F:]{1,39})-([0-9a-fA-F:]{1,39})/i);
227+
const ipv6RangeMatch = item.match(/Range: IPv6 ([0-9a-fA-F:.]{1,45})-([0-9a-fA-F:.]{1,45})/i);
228228
if (ipv6RangeMatch) {
229229
const { 1: start, 2: end } = ipv6RangeMatch;
230230
this.addRange(start, end, 'ipv6');

‎test/parallel/test-blocklist.js‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,3 +359,40 @@ const util = require('util');
359359
assert.strictEqual(test5.check(i[0], i[1]), i[2]);
360360
});
361361
}
362+
363+
{
364+
// IPv4-mapped / mixed-notation IPv6 rules must survive a toJSON/fromJSON
365+
// round-trip. toJSON() emits these addresses in mixed dotted-quad form
366+
// (e.g. "::ffff:192.0.2.128"), so the rule parser must accept the "." used
367+
// in that notation.
368+
const bl = new BlockList();
369+
bl.addAddress('::ffff:192.0.2.128', 'ipv6');
370+
bl.addSubnet('::ffff:10.0.0.0', 120, 'ipv6');
371+
bl.addRange('::ffff:172.16.0.1', '::ffff:172.16.0.10', 'ipv6');
372+
373+
const expected = [
374+
'Address: IPv6 ::ffff:192.0.2.128',
375+
'Range: IPv6 ::ffff:172.16.0.1-::ffff:172.16.0.10',
376+
'Subnet: IPv6 ::ffff:10.0.0.0/120',
377+
];
378+
assert.deepStrictEqual(bl.toJSON().sort(), expected);
379+
380+
const restored = new BlockList();
381+
restored.fromJSON(bl.toJSON());
382+
383+
// The restored rules must be identical (lossless round-trip).
384+
assert.deepStrictEqual(restored.toJSON().sort(), expected);
385+
386+
// And it must still match the same addresses as the original.
387+
[
388+
['::ffff:192.0.2.128', true], // the single address
389+
['::ffff:192.0.2.129', false],
390+
['::ffff:10.0.0.50', true], // inside the subnet
391+
['::ffff:10.0.1.0', false],
392+
['::ffff:172.16.0.5', true], // inside the range
393+
['::ffff:172.16.0.20', false],
394+
].forEach(([address, result]) => {
395+
assert.strictEqual(bl.check(address, 'ipv6'), result);
396+
assert.strictEqual(restored.check(address, 'ipv6'), result);
397+
});
398+
}

0 commit comments

Comments
 (0)