mirror of
https://github.com/zebrajr/node.git
synced 2025-12-06 00:20:08 +01:00
net: update net.blocklist to allow file save and file management
PR-URL: https://github.com/nodejs/node/pull/58087 Fixes: https://github.com/nodejs/node/issues/56252 Reviewed-By: Ethan Arrowood <ethan@arrowood.dev> Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
parent
eff504ff12
commit
4de0197441
|
|
@ -181,6 +181,38 @@ added:
|
|||
* `value` {any} Any JS value
|
||||
* Returns `true` if the `value` is a `net.BlockList`.
|
||||
|
||||
### `blockList.fromJSON(value)`
|
||||
|
||||
> Stability: 1 - Experimental
|
||||
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
```js
|
||||
const blockList = new net.BlockList();
|
||||
const data = [
|
||||
'Subnet: IPv4 192.168.1.0/24',
|
||||
'Address: IPv4 10.0.0.5',
|
||||
'Range: IPv4 192.168.2.1-192.168.2.10',
|
||||
'Range: IPv4 10.0.0.1-10.0.0.10',
|
||||
];
|
||||
blockList.fromJSON(data);
|
||||
blockList.fromJSON(JSON.stringify(data));
|
||||
```
|
||||
|
||||
* `value` Blocklist.rules
|
||||
|
||||
### `blockList.toJSON()`
|
||||
|
||||
> Stability: 1 - Experimental
|
||||
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* Returns Blocklist.rules
|
||||
|
||||
## Class: `net.SocketAddress`
|
||||
|
||||
<!-- YAML
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
'use strict';
|
||||
|
||||
const {
|
||||
ArrayIsArray,
|
||||
Boolean,
|
||||
JSONParse,
|
||||
NumberParseInt,
|
||||
ObjectSetPrototypeOf,
|
||||
Symbol,
|
||||
} = primordials;
|
||||
|
|
@ -32,6 +35,7 @@ const { owner_symbol } = internalBinding('symbols');
|
|||
|
||||
const {
|
||||
ERR_INVALID_ARG_VALUE,
|
||||
ERR_INVALID_ARG_TYPE,
|
||||
} = require('internal/errors').codes;
|
||||
|
||||
const { validateInt32, validateString } = require('internal/validators');
|
||||
|
|
@ -139,10 +143,130 @@ class BlockList {
|
|||
return Boolean(this[kHandle].check(address[kSocketAddressHandle]));
|
||||
}
|
||||
|
||||
/*
|
||||
* @param {string[]} data
|
||||
* @example
|
||||
* const data = [
|
||||
* // IPv4 examples
|
||||
* 'Subnet: IPv4 192.168.1.0/24',
|
||||
* 'Address: IPv4 10.0.0.5',
|
||||
* 'Range: IPv4 192.168.2.1-192.168.2.10',
|
||||
* 'Range: IPv4 10.0.0.1-10.0.0.10',
|
||||
*
|
||||
* // IPv6 examples
|
||||
* 'Subnet: IPv6 2001:0db8:85a3:0000:0000:8a2e:0370:7334/64',
|
||||
* 'Address: IPv6 2001:0db8:85a3:0000:0000:8a2e:0370:7334',
|
||||
* 'Range: IPv6 2001:0db8:85a3:0000:0000:8a2e:0370:7334-2001:0db8:85a3:0000:0000:8a2e:0370:7335',
|
||||
* 'Subnet: IPv6 2001:db8:1234::/48',
|
||||
* 'Address: IPv6 2001:db8:1234::1',
|
||||
* 'Range: IPv6 2001:db8:1234::1-2001:db8:1234::10'
|
||||
* ];
|
||||
*/
|
||||
#parseIPInfo(data) {
|
||||
for (const item of data) {
|
||||
if (item.includes('IPv4')) {
|
||||
const subnetMatch = item.match(
|
||||
/Subnet: IPv4 (\d{1,3}(?:\.\d{1,3}){3})\/(\d{1,2})/,
|
||||
);
|
||||
if (subnetMatch) {
|
||||
const { 1: network, 2: prefix } = subnetMatch;
|
||||
this.addSubnet(network, NumberParseInt(prefix));
|
||||
continue;
|
||||
}
|
||||
const addressMatch = item.match(/Address: IPv4 (\d{1,3}(?:\.\d{1,3}){3})/);
|
||||
if (addressMatch) {
|
||||
const { 1: address } = addressMatch;
|
||||
this.addAddress(address);
|
||||
continue;
|
||||
}
|
||||
|
||||
const rangeMatch = item.match(
|
||||
/Range: IPv4 (\d{1,3}(?:\.\d{1,3}){3})-(\d{1,3}(?:\.\d{1,3}){3})/,
|
||||
);
|
||||
if (rangeMatch) {
|
||||
const { 1: start, 2: end } = rangeMatch;
|
||||
this.addRange(start, end);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// IPv6 parsing with support for compressed addresses
|
||||
if (item.includes('IPv6')) {
|
||||
// IPv6 subnet pattern: supports both full and compressed formats
|
||||
// Examples:
|
||||
// - 2001:0db8:85a3:0000:0000:8a2e:0370:7334/64 (full)
|
||||
// - 2001:db8:85a3::8a2e:370:7334/64 (compressed)
|
||||
// - 2001:db8:85a3::192.0.2.128/64 (mixed)
|
||||
const ipv6SubnetMatch = item.match(
|
||||
/Subnet: IPv6 ([0-9a-fA-F:]{1,39})\/([0-9]{1,3})/i,
|
||||
);
|
||||
if (ipv6SubnetMatch) {
|
||||
const { 1: network, 2: prefix } = ipv6SubnetMatch;
|
||||
this.addSubnet(network, NumberParseInt(prefix), 'ipv6');
|
||||
continue;
|
||||
}
|
||||
|
||||
// IPv6 address pattern: supports both full and compressed formats
|
||||
// Examples:
|
||||
// - 2001:0db8:85a3:0000:0000:8a2e:0370:7334 (full)
|
||||
// - 2001:db8:85a3::8a2e:370:7334 (compressed)
|
||||
// - 2001:db8:85a3::192.0.2.128 (mixed)
|
||||
const ipv6AddressMatch = item.match(/Address: IPv6 ([0-9a-fA-F:]{1,39})/i);
|
||||
if (ipv6AddressMatch) {
|
||||
const { 1: address } = ipv6AddressMatch;
|
||||
this.addAddress(address, 'ipv6');
|
||||
continue;
|
||||
}
|
||||
|
||||
// IPv6 range pattern: supports both full and compressed formats
|
||||
// Examples:
|
||||
// - 2001:0db8:85a3:0000:0000:8a2e:0370:7334-2001:0db8:85a3:0000:0000:8a2e:0370:7335 (full)
|
||||
// - 2001:db8:85a3::8a2e:370:7334-2001:db8:85a3::8a2e:370:7335 (compressed)
|
||||
// - 2001:db8:85a3::192.0.2.128-2001:db8:85a3::192.0.2.129 (mixed)
|
||||
const ipv6RangeMatch = item.match(/Range: IPv6 ([0-9a-fA-F:]{1,39})-([0-9a-fA-F:]{1,39})/i);
|
||||
if (ipv6RangeMatch) {
|
||||
const { 1: start, 2: end } = ipv6RangeMatch;
|
||||
this.addRange(start, end, 'ipv6');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
toJSON() {
|
||||
return this.rules;
|
||||
}
|
||||
|
||||
fromJSON(data) {
|
||||
// The data argument must be a string, or an array of strings that
|
||||
// is JSON parseable.
|
||||
if (ArrayIsArray(data)) {
|
||||
for (const n of data) {
|
||||
if (typeof n !== 'string') {
|
||||
throw new ERR_INVALID_ARG_TYPE('data', ['string', 'string[]'], data);
|
||||
}
|
||||
}
|
||||
} else if (typeof data !== 'string') {
|
||||
throw new ERR_INVALID_ARG_TYPE('data', ['string', 'string[]'], data);
|
||||
} else {
|
||||
data = JSONParse(data);
|
||||
if (!ArrayIsArray(data)) {
|
||||
throw new ERR_INVALID_ARG_TYPE('data', ['string', 'string[]'], data);
|
||||
}
|
||||
for (const n of data) {
|
||||
if (typeof n !== 'string') {
|
||||
throw new ERR_INVALID_ARG_TYPE('data', ['string', 'string[]'], data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.#parseIPInfo(data);
|
||||
}
|
||||
|
||||
|
||||
get rules() {
|
||||
return this[kHandle].getRules();
|
||||
}
|
||||
|
||||
[kClone]() {
|
||||
const handle = this[kHandle];
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -287,3 +287,75 @@ const util = require('util');
|
|||
assert(BlockList.isBlockList(new BlockList()));
|
||||
assert(!BlockList.isBlockList({}));
|
||||
}
|
||||
|
||||
// Test exporting and importing the rule list to/from JSON
|
||||
{
|
||||
const ruleList = [
|
||||
'Address: IPv4 10.0.0.5',
|
||||
'Address: IPv6 ::',
|
||||
'Subnet: IPv4 192.168.1.0/24',
|
||||
'Subnet: IPv6 8592:757c:efae:4e45::/64',
|
||||
];
|
||||
|
||||
const test2 = new BlockList();
|
||||
const test3 = new BlockList();
|
||||
const test4 = new BlockList();
|
||||
const test5 = new BlockList();
|
||||
|
||||
const bl = new BlockList();
|
||||
bl.addAddress('10.0.0.5');
|
||||
bl.addAddress('::', 'ipv6');
|
||||
bl.addSubnet('192.168.1.0', 24);
|
||||
bl.addSubnet('8592:757c:efae:4e45::', 64, 'ipv6');
|
||||
|
||||
// Test invalid inputs (input to fromJSON must be an array of
|
||||
// string rules or a serialized json string of an array of
|
||||
// string rules.
|
||||
[
|
||||
1, null, Symbol(), [1, 2, 3], '123', [Symbol()], new Map(),
|
||||
].forEach((i) => {
|
||||
assert.throws(() => test2.fromJSON(i), {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
});
|
||||
});
|
||||
|
||||
// Invalid rules are ignored.
|
||||
test2.fromJSON(['1', '2', '3']);
|
||||
assert.deepStrictEqual(test2.rules, []);
|
||||
|
||||
// Direct output from toJSON method works
|
||||
test2.fromJSON(bl.toJSON());
|
||||
assert.deepStrictEqual(test2.rules.sort(), ruleList);
|
||||
|
||||
// JSON stringified output works
|
||||
test3.fromJSON(JSON.stringify(bl));
|
||||
assert.deepStrictEqual(test3.rules.sort(), ruleList);
|
||||
|
||||
// A raw array works
|
||||
test4.fromJSON(ruleList);
|
||||
assert.deepStrictEqual(test4.rules.sort(), ruleList);
|
||||
|
||||
// Individual rules work
|
||||
ruleList.forEach((item) => {
|
||||
test5.fromJSON([item]);
|
||||
});
|
||||
assert.deepStrictEqual(test5.rules.sort(), ruleList);
|
||||
|
||||
// Each of the created blocklists should handle the checks identically.
|
||||
[
|
||||
['10.0.0.5', 'ipv4', true],
|
||||
['10.0.0.6', 'ipv4', false],
|
||||
['::', 'ipv6', true],
|
||||
['::1', 'ipv6', false],
|
||||
['192.168.1.0', 'ipv4', true],
|
||||
['193.168.1.0', 'ipv4', false],
|
||||
['8592:757c:efae:4e45::', 'ipv6', true],
|
||||
['1111:1111:1111:1111::', 'ipv6', false],
|
||||
].forEach((i) => {
|
||||
assert.strictEqual(bl.check(i[0], i[1]), i[2]);
|
||||
assert.strictEqual(test2.check(i[0], i[1]), i[2]);
|
||||
assert.strictEqual(test3.check(i[0], i[1]), i[2]);
|
||||
assert.strictEqual(test4.check(i[0], i[1]), i[2]);
|
||||
assert.strictEqual(test5.check(i[0], i[1]), i[2]);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
const common = require('../common');
|
||||
const net = require('net');
|
||||
const assert = require('assert');
|
||||
|
||||
const blockList = new net.BlockList();
|
||||
blockList.addAddress('127.0.0.1');
|
||||
blockList.addAddress('127.0.0.2');
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user