replace ipaddr.js
This commit is contained in:
9
package-lock.json
generated
9
package-lock.json
generated
@@ -30,7 +30,6 @@
|
||||
"domrobot-client": "^3.2.2",
|
||||
"ejs": "^3.1.10",
|
||||
"express": "^4.21.2",
|
||||
"ipaddr.js": "^2.2.0",
|
||||
"jose": "^5.9.6",
|
||||
"jsdom": "^26.0.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
@@ -5362,14 +5361,6 @@
|
||||
"version": "1.3.8",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ipaddr.js": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz",
|
||||
"integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
"dev": true,
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
"domrobot-client": "^3.2.2",
|
||||
"ejs": "^3.1.10",
|
||||
"express": "^4.21.2",
|
||||
"ipaddr.js": "^2.2.0",
|
||||
"jose": "^5.9.6",
|
||||
"jsdom": "^26.0.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
|
||||
@@ -16,7 +16,7 @@ const assert = require('assert'),
|
||||
constants = require('./constants.js'),
|
||||
debug = require('debug')('box:directoryserver'),
|
||||
eventlog = require('./eventlog.js'),
|
||||
ipaddr = require('ipaddr.js'),
|
||||
ipaddr = require('./ipaddr.js'),
|
||||
groups = require('./groups.js'),
|
||||
ldap = require('ldapjs'),
|
||||
path = require('path'),
|
||||
@@ -56,7 +56,6 @@ async function validateConfig(config) {
|
||||
for (const line of allowlist.split('\n')) {
|
||||
if (!line || line.startsWith('#')) continue;
|
||||
const rangeOrIP = line.trim();
|
||||
// this checks for IPv4 and IPv6
|
||||
if (!ipaddr.isValid(rangeOrIP) && !ipaddr.isValidCIDR(rangeOrIP)) throw new BoxError(BoxError.BAD_FIELD, `${rangeOrIP} is not a valid IP or range`);
|
||||
gotOne = true;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ const apps = require('./apps.js'),
|
||||
dashboard = require('./dashboard.js'),
|
||||
debug = require('debug')('box:dns'),
|
||||
domains = require('./domains.js'),
|
||||
ipaddr = require('ipaddr.js'),
|
||||
ipaddr = require('./ipaddr.js'),
|
||||
mail = require('./mail.js'),
|
||||
mailServer = require('./mailserver.js'),
|
||||
network = require('./network.js'),
|
||||
@@ -141,7 +141,7 @@ async function checkDnsRecords(subdomain, domain) {
|
||||
const ipv6Records = await getDnsRecords(subdomain, domain, 'AAAA');
|
||||
|
||||
// if empty OR exactly one record with the ip, we don't need to overwrite
|
||||
if (ipv6Records.length !== 0 && (ipv6Records.length !== 1 || ipaddr.parse(ipv6Records[0]).toRFC5952String() !== ipv6)) return { needsOverwrite: true };
|
||||
if (ipv6Records.length !== 0 && (ipv6Records.length !== 1 || !ipaddr.isEqual(ipv6Records[0], ipv6))) return { needsOverwrite: true };
|
||||
}
|
||||
|
||||
return { needsOverwrite: false }; // one record exists and in sync
|
||||
|
||||
@@ -16,7 +16,6 @@ const assert = require('assert'),
|
||||
debug = require('debug')('box:dns/cloudflare'),
|
||||
dig = require('../dig.js'),
|
||||
dns = require('../dns.js'),
|
||||
ipaddr = require('ipaddr.js'),
|
||||
safe = require('safetydance'),
|
||||
superagent = require('../superagent.js'),
|
||||
waitForDns = require('./waitfordns.js'),
|
||||
@@ -144,10 +143,8 @@ async function upsert(domainObject, location, type, values) {
|
||||
};
|
||||
|
||||
if (i >= records.length) { // create a new record
|
||||
// cloudflare will error if proxied is set for wrong record type or IP
|
||||
if (type === 'A' || type === 'AAAA' || type === 'CNAME') {
|
||||
const isUnicast = ipaddr.parse(value).range() === 'unicast';
|
||||
data.proxied = isUnicast ? !!domainConfig.defaultProxyStatus : false; // only set at install time
|
||||
data.proxied = !!domainConfig.defaultProxyStatus; // note that cloudflare will error if proxied is set for wrong record type or IP. only set at install time
|
||||
}
|
||||
|
||||
debug(`upsert: Adding new record fqdn: ${fqdn}, zoneName: ${zoneName} proxied: ${data.proxied}`);
|
||||
|
||||
62
src/ipaddr.js
Normal file
62
src/ipaddr.js
Normal file
@@ -0,0 +1,62 @@
|
||||
'use strict';
|
||||
|
||||
exports = module.exports = {
|
||||
isValid,
|
||||
isValidCIDR,
|
||||
isEqual,
|
||||
includes,
|
||||
};
|
||||
|
||||
const assert = require('assert'),
|
||||
net = require('net');
|
||||
|
||||
function isValid(ip) {
|
||||
assert.strictEqual(typeof ip, 'string');
|
||||
|
||||
const type = net.isIP(ip);
|
||||
return type === 4 || type === 6;
|
||||
}
|
||||
|
||||
function isValidCIDR(cidr) {
|
||||
assert.strictEqual(typeof cidr, 'string');
|
||||
|
||||
const parts = cidr.split('/');
|
||||
if (parts.length !== 2) return false;
|
||||
|
||||
const [ ip, prefixString ] = parts;
|
||||
const type = net.isIP(ip);
|
||||
if (type === 0) return false;
|
||||
|
||||
const prefix = Number.parseInt(prefixString, 10);
|
||||
if (!Number.isInteger(prefix) || prefix < 0 || (prefix > (type === 4 ? 32 : 128)) || String(prefix) !== prefixString) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function isEqual(ip1, ip2) {
|
||||
assert.strictEqual(typeof ip1, 'string');
|
||||
assert.strictEqual(typeof ip2, 'string');
|
||||
|
||||
if (ip1 === ip2) return true;
|
||||
|
||||
const type1 = net.isIP(ip1), type2 = net.isIP(ip2);
|
||||
if (type1 === 0 || type2 === 0) return false; // otherwise, it will throw invalid socket address below
|
||||
|
||||
// use blocklist to compare since strings may not be in RFC 5952 format
|
||||
const blockList = new net.BlockList();
|
||||
blockList.addAddress(ip1, `ipv${type1}`);
|
||||
return blockList.check(ip2, `ipv${type2}`);
|
||||
}
|
||||
|
||||
function includes(cidr, ip) {
|
||||
assert.strictEqual(typeof cidr, 'string');
|
||||
assert.strictEqual(typeof ip, 'string');
|
||||
|
||||
const type = net.isIP(ip);
|
||||
const [ subnet, prefix ] = cidr.split('/');
|
||||
const subnetType = net.isIP(subnet);
|
||||
|
||||
const blockList = new net.BlockList();
|
||||
blockList.addSubnet(subnet, parseInt(prefix, 10), `ipv${subnetType}`);
|
||||
return blockList.check(ip, `ipv${type}`);
|
||||
}
|
||||
@@ -27,7 +27,7 @@ const assert = require('assert'),
|
||||
constants = require('./constants.js'),
|
||||
cron = require('./cron.js'),
|
||||
fs = require('fs'),
|
||||
ipaddr = require('ipaddr.js'),
|
||||
ipaddr = require('./ipaddr.js'),
|
||||
path = require('path'),
|
||||
paths = require('./paths.js'),
|
||||
safe = require('safetydance'),
|
||||
@@ -74,20 +74,16 @@ async function setBlocklist(blocklist, auditSource) {
|
||||
assert.strictEqual(typeof blocklist, 'string');
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
|
||||
const parsedIp = ipaddr.process(auditSource.ip); // will demangle IPv4 mapped IPv6
|
||||
|
||||
let count = 0;
|
||||
for (const line of blocklist.split('\n')) {
|
||||
if (!line || line.startsWith('#')) continue;
|
||||
const rangeOrIP = line.trim();
|
||||
// this checks for IPv4 and IPv6
|
||||
if (!ipaddr.isValid(rangeOrIP) && !ipaddr.isValidCIDR(rangeOrIP)) throw new BoxError(BoxError.BAD_FIELD, `${rangeOrIP} is not a valid IP or range`);
|
||||
|
||||
if (rangeOrIP.indexOf('/') === -1) {
|
||||
if (auditSource.ip === rangeOrIP) throw new BoxError(BoxError.BAD_FIELD, `${rangeOrIP} includes client IP. Cannot block yourself`);
|
||||
if (ipaddr.isEqual(rangeOrIP, auditSource.ip)) throw new BoxError(BoxError.BAD_FIELD, `IP ${rangeOrIP} is the client IP. Cannot block yourself`);
|
||||
} else {
|
||||
const parsedRange = ipaddr.parseCIDR(rangeOrIP); // returns [addr, range]
|
||||
if (parsedRange[0].kind() === parsedIp.kind() && parsedIp.match(parsedRange)) throw new BoxError(BoxError.BAD_FIELD, `${rangeOrIP} includes client IP. Cannot block yourself`);
|
||||
if (ipaddr.includes(rangeOrIP, auditSource.ip)) throw new BoxError(BoxError.BAD_FIELD, `range ${rangeOrIP} includes client IP. Cannot block yourself`);
|
||||
}
|
||||
++count;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ const acme2 = require('./acme2.js'),
|
||||
domains = require('./domains.js'),
|
||||
ejs = require('ejs'),
|
||||
eventlog = require('./eventlog.js'),
|
||||
ipaddr = require('ipaddr.js'),
|
||||
ipaddr = require('./ipaddr.js'),
|
||||
fs = require('fs'),
|
||||
Location = require('./location.js'),
|
||||
mailServer = require('./mailserver.js'),
|
||||
@@ -783,7 +783,6 @@ async function setTrustedIps(trustedIps) {
|
||||
for (const line of trustedIps.split('\n')) {
|
||||
if (!line || line.startsWith('#')) continue;
|
||||
const rangeOrIP = line.trim();
|
||||
// this checks for IPv4 and IPv6
|
||||
if (!ipaddr.isValid(rangeOrIP) && !ipaddr.isValidCIDR(rangeOrIP)) throw new BoxError(BoxError.BAD_FIELD, `${rangeOrIP} is not a valid IP or range`);
|
||||
trustedIpsConfig += `set_real_ip_from ${rangeOrIP};\n`;
|
||||
}
|
||||
|
||||
113
src/test/ipaddr-test.js
Normal file
113
src/test/ipaddr-test.js
Normal file
@@ -0,0 +1,113 @@
|
||||
'use strict';
|
||||
|
||||
/* global it, describe */
|
||||
|
||||
const expect = require('expect.js'),
|
||||
ipaddr = require('../ipaddr.js');
|
||||
|
||||
describe('ipaddr', function () {
|
||||
describe('IPv4', function () {
|
||||
const goodIPv4s = [ '1.2.3.4', '0.235.255.123' ];
|
||||
const badIPv4s = [ '1.2.3', '1.2.3.256', '-1.2.3.4', '1e2.5.6.7', 'x.7.8.9', '1..2.4' ];
|
||||
|
||||
for (const goodIPv4 of goodIPv4s) {
|
||||
it(`isValid returns true ${goodIPv4}`, () => expect(ipaddr.isValid(goodIPv4)).to.be(true));
|
||||
}
|
||||
|
||||
for (const badIPv4 of badIPv4s) {
|
||||
it(`isValid returns false ${badIPv4}`, () => expect(ipaddr.isValid(badIPv4)).to.be(false));
|
||||
}
|
||||
|
||||
const goodCIDRs = [
|
||||
'192.168.1.0/24'
|
||||
];
|
||||
|
||||
const badCIDRs = [
|
||||
'192.168.1.0/-1',
|
||||
'192.168.1.0/',
|
||||
'192.168.1.0/33',
|
||||
'192.168.1.0/1e2'
|
||||
];
|
||||
|
||||
for (const goodCIDR of goodCIDRs) {
|
||||
it(`isValidCIDR returns true ${goodCIDR}`, () => expect(ipaddr.isValidCIDR(goodCIDR)).to.be(true));
|
||||
}
|
||||
|
||||
for (const badCIDR of badCIDRs) {
|
||||
it(`isValidCIDR returns false ${badCIDR}`, () => expect(ipaddr.isValidCIDR(badCIDR)).to.be(false));
|
||||
}
|
||||
|
||||
it('isEqual', function () {
|
||||
expect(ipaddr.isEqual('1.2.3.4', '1.2.3.4')).to.be(true);
|
||||
expect(ipaddr.isEqual('1.2.3.4', '1.2.3.5')).to.be(false);
|
||||
expect(ipaddr.isEqual('1.2.3.4', '1.2.3.')).to.be(false);
|
||||
});
|
||||
|
||||
it('includes', function () {
|
||||
expect(ipaddr.includes('1.2.3.0/8', '1.2.3.4')).to.be(true);
|
||||
expect(ipaddr.includes('1.2.0.0/16', '1.3.0.0')).to.be(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('IPv6', function () {
|
||||
const goodIPv6s = [
|
||||
'2001:0db8:85a3:0000:0000:8a2e:0370:7334',
|
||||
'::1',
|
||||
'2001:db8::ff00:42:8329',
|
||||
'::ffff:1.2.3.4',
|
||||
'2001:db8:85a3::8a2e:370:7334',
|
||||
'2001:0db8:85a3::',
|
||||
'2000::',
|
||||
'2002:c0a8:101::42',
|
||||
'::a:b'
|
||||
];
|
||||
const badIPv6s = [
|
||||
'2001:db8::85a3::8a2e:370:7334', // too many ::
|
||||
'2001:db8::85a3:8g2e:370:7334', // invalid hex
|
||||
'12345::1', // segment too long
|
||||
'::a:b:c:d:e:f:f:f:f', // too many segments
|
||||
':192:168:0:1',
|
||||
];
|
||||
|
||||
for (const goodIPv6 of goodIPv6s) {
|
||||
it(`isValid returns true ${goodIPv6}`, () => expect(ipaddr.isValid(goodIPv6)).to.be(true));
|
||||
}
|
||||
|
||||
for (const badIPv6 of badIPv6s) {
|
||||
it(`isValid returns false ${badIPv6}`, () => expect(ipaddr.isValid(badIPv6)).to.be(false));
|
||||
}
|
||||
|
||||
const goodCIDRs = [
|
||||
'2001:db8::/32',
|
||||
'::/128'
|
||||
];
|
||||
|
||||
const badCIDRs = [
|
||||
'2001:db8::/129',
|
||||
'::a/abc'
|
||||
];
|
||||
|
||||
for (const goodCIDR of goodCIDRs) {
|
||||
it(`isValidCIDR returns true ${goodCIDR}`, () => expect(ipaddr.isValidCIDR(goodCIDR)).to.be(true));
|
||||
}
|
||||
|
||||
for (const badCIDR of badCIDRs) {
|
||||
it(`isValidCIDR returns false ${badCIDR}`, () => expect(ipaddr.isValidCIDR(badCIDR)).to.be(false));
|
||||
}
|
||||
|
||||
it('isEqual', function () {
|
||||
expect(ipaddr.isEqual('2001:0db8:85a3:0000:0000:8a2e:0370:7334', '2001:0db8:85a3:0000:0000:8a2e:0370:7334')).to.be(true); // same
|
||||
expect(ipaddr.isEqual('2001:0db8:85a3:0000:0000:8a2e:0370:7334', '2001:0db8:85a3::0000:8a2e:0370:7334')).to.be(true); // shorthand
|
||||
expect(ipaddr.isEqual('2001:db8:85A3:0000:0000:8a2e:0370:7334', '2001:0db8:85a3::0000:8a2e:370:7334')).to.be(true); // casing change and no 0 prefix
|
||||
expect(ipaddr.isEqual('2001:db8:85A3:0000:0000:8a2e:0370:7334', '1.2.3.4')).to.be(false);
|
||||
expect(ipaddr.isEqual('::ffff:1.2.3.4', '1.2.3.4')).to.be(true); // ipv6 mapped ipv4
|
||||
expect(ipaddr.isEqual('2002:0db8:85a3:0000:0000:8a2e:0370:7334', '2001:0db8:85a3:0000:0000:8a2e:0370:7334')).to.be(false);
|
||||
});
|
||||
|
||||
it('includes', function () {
|
||||
expect(ipaddr.includes('2001:0db8:85a3::/64', '2001:0db8:85a3:0000:0000:8a2e:0370:7334')).to.be(true);
|
||||
expect(ipaddr.includes('2001:0db8:85a3::/64', '2002:0db8:85a3:0000:0000:8a2e:0370:7334')).to.be(false);
|
||||
expect(ipaddr.includes('::ffff:0:0/96', '1.2.3.4')).to.be(true); // ipv6 mapped ipv4
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -34,7 +34,7 @@ const assert = require('assert'),
|
||||
BoxError = require('./boxerror.js'),
|
||||
database = require('./database.js'),
|
||||
hat = require('./hat.js'),
|
||||
ipaddr = require('ipaddr.js'),
|
||||
ipaddr = require('./ipaddr.js'),
|
||||
safe = require('safetydance'),
|
||||
uuid = require('uuid');
|
||||
|
||||
@@ -104,7 +104,6 @@ function parseIpRanges(ipRanges) {
|
||||
// each line can have comma separated list. this complexity is because we changed the UI to take a line input instead of textarea
|
||||
for (const entry of line.split(',')) {
|
||||
const rangeOrIP = entry.trim();
|
||||
// this checks for IPv4 and IPv6
|
||||
if (!ipaddr.isValid(rangeOrIP) && !ipaddr.isValidCIDR(rangeOrIP)) throw new BoxError(BoxError.BAD_FIELD, `${rangeOrIP} is not a valid IP or range`);
|
||||
result.push(rangeOrIP);
|
||||
}
|
||||
@@ -209,8 +208,6 @@ function isIpAllowedSync(token, ip) {
|
||||
assert.strictEqual(typeof token, 'object');
|
||||
assert.strictEqual(typeof ip, 'string');
|
||||
|
||||
const parsedIp = ipaddr.process(ip); // will demangle IPv4-mapped IPv6
|
||||
|
||||
let allowedIpRanges = gParsedRangesCache.get(token.id); // returns undefined if not found
|
||||
if (!allowedIpRanges) {
|
||||
allowedIpRanges = parseIpRanges(token.allowedIpRanges || '');
|
||||
@@ -219,10 +216,9 @@ function isIpAllowedSync(token, ip) {
|
||||
|
||||
for (const ipOrRange of allowedIpRanges) {
|
||||
if (!ipOrRange.includes('/')) {
|
||||
if (ip === ipOrRange) return true;
|
||||
if (ipaddr.isEqual(ipOrRange, ip)) return true;
|
||||
} else {
|
||||
const parsedRange = ipaddr.parseCIDR(ipOrRange); // returns [addr, range]
|
||||
if (parsedRange[0].kind() === parsedIp.kind() && parsedIp.match(parsedRange)) return true;
|
||||
if (ipaddr.includes(ipOrRange, ip)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user