forked from svrjs/svrjs
Add ipBlockList utility function, blocklist functionality, and read from config.json functionality.
This commit is contained in:
parent
2f836231f4
commit
89e9b35829
6 changed files with 440 additions and 28 deletions
22
src/index.js
22
src/index.js
|
@ -94,6 +94,21 @@ const cluster = require("./utils/clusterBunShim.js"); // Cluster module with shi
|
||||||
//const fixNodeMojibakeURL = require("./utils/urlMojibakeFixer.js");
|
//const fixNodeMojibakeURL = require("./utils/urlMojibakeFixer.js");
|
||||||
|
|
||||||
process.serverConfig = {};
|
process.serverConfig = {};
|
||||||
|
let configJSONRErr = undefined;
|
||||||
|
let configJSONPErr = undefined;
|
||||||
|
if (fs.existsSync(__dirname + "/config.json")) {
|
||||||
|
let configJSONf = "";
|
||||||
|
try {
|
||||||
|
configJSONf = fs.readFileSync(__dirname + "/config.json"); // Read JSON File
|
||||||
|
try {
|
||||||
|
process.serverConfig = JSON.parse(configJSONf); // Parse JSON
|
||||||
|
} catch (err2) {
|
||||||
|
configJSONPErr = err2;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
configJSONRErr = err2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: configuration from config.json
|
// TODO: configuration from config.json
|
||||||
if (process.serverConfig.users === undefined) process.serverConfig.users = [];
|
if (process.serverConfig.users === undefined) process.serverConfig.users = [];
|
||||||
|
@ -115,7 +130,6 @@ delete process.serverConfig.domian;
|
||||||
if (process.serverConfig.page404 === undefined) process.serverConfig.page404 = "404.html";
|
if (process.serverConfig.page404 === undefined) process.serverConfig.page404 = "404.html";
|
||||||
process.serverConfig.timestamp = new Date().getTime();
|
process.serverConfig.timestamp = new Date().getTime();
|
||||||
if (process.serverConfig.blacklist === undefined) process.serverConfig.blacklist = [];
|
if (process.serverConfig.blacklist === undefined) process.serverConfig.blacklist = [];
|
||||||
//process.serverConfig.blacklist = blocklist.raw; //TODO
|
|
||||||
if (process.serverConfig.nonStandardCodes === undefined) process.serverConfig.nonStandardCodes = [];
|
if (process.serverConfig.nonStandardCodes === undefined) process.serverConfig.nonStandardCodes = [];
|
||||||
if (process.serverConfig.enableCompression === undefined) process.serverConfig.enableCompression = true;
|
if (process.serverConfig.enableCompression === undefined) process.serverConfig.enableCompression = true;
|
||||||
if (process.serverConfig.customHeaders === undefined) process.serverConfig.customHeaders = {};
|
if (process.serverConfig.customHeaders === undefined) process.serverConfig.customHeaders = {};
|
||||||
|
@ -182,7 +196,7 @@ let middleware = [
|
||||||
require("./middleware/core.js"),
|
require("./middleware/core.js"),
|
||||||
require("./middleware/urlSanitizer.js"),
|
require("./middleware/urlSanitizer.js"),
|
||||||
require("./middleware/redirects.js"),
|
require("./middleware/redirects.js"),
|
||||||
// TODO: blocklist
|
require("./middleware/blocklist.js"),
|
||||||
require("./middleware/webRootPostfixes.js"),
|
require("./middleware/webRootPostfixes.js"),
|
||||||
require("./middleware/rewriteURL.js"),
|
require("./middleware/rewriteURL.js"),
|
||||||
require("./middleware/responseHeaders.js"),
|
require("./middleware/responseHeaders.js"),
|
||||||
|
@ -254,4 +268,6 @@ function requestHandler(req, res) {
|
||||||
// Create HTTP server
|
// Create HTTP server
|
||||||
http.createServer(requestHandler).listen(3000);
|
http.createServer(requestHandler).listen(3000);
|
||||||
|
|
||||||
if(wwwrootError) throw wwwrootError;
|
if(wwwrootError) throw wwwrootError;
|
||||||
|
if(configJSONRErr) throw configJSONRErr;
|
||||||
|
if(configJSONPErr) throw configJSONPErr;
|
53
src/middleware/blocklist.js
Normal file
53
src/middleware/blocklist.js
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
const ipBlockList = require("../utils/ipBlockList.js");
|
||||||
|
let blocklist = ipBlockList(process.serverConfig.blacklist);
|
||||||
|
|
||||||
|
module.exports = (req, res, logFacilities, config, next) => {
|
||||||
|
if (
|
||||||
|
blocklist.check(
|
||||||
|
req.socket.realRemoteAddress
|
||||||
|
? req.socket.realRemoteAddress
|
||||||
|
: req.socket.remoteAddress,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
// Invoke 403 Forbidden error
|
||||||
|
res.error(403);
|
||||||
|
logFacilities.errmessage("Client is in the block list.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.commands = {
|
||||||
|
block: (ip, logFacilities, passCommand) => {
|
||||||
|
if (ip == undefined || JSON.stringify(ip) == "[]") {
|
||||||
|
log("Cannot block non-existent IP.");
|
||||||
|
} else {
|
||||||
|
for (var i = 0; i < ip.length; i++) {
|
||||||
|
if (ip[i] != "localhost" && ip[i].indexOf(":") == -1) {
|
||||||
|
ip[i] = "::ffff:" + ip[i];
|
||||||
|
}
|
||||||
|
if (!blocklist.check(ip[i])) {
|
||||||
|
blocklist.add(ip[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
process.config.blacklist = blocklist.raw;
|
||||||
|
log("IPs successfully blocked.");
|
||||||
|
passCommand(args, logFacilities);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
unblock: (ip, logFacilities, passCommand) => {
|
||||||
|
if (ip == undefined || JSON.stringify(ip) == "[]") {
|
||||||
|
log("Cannot unblock non-existent IP.");
|
||||||
|
} else {
|
||||||
|
for (var i = 0; i < ip.length; i++) {
|
||||||
|
if (ip[i].indexOf(":") == -1) {
|
||||||
|
ip[i] = "::ffff:" + ip[i];
|
||||||
|
}
|
||||||
|
blocklist.remove(ip[i]);
|
||||||
|
}
|
||||||
|
process.config.blacklist = blocklist.raw;
|
||||||
|
log("IPs successfully unblocked.");
|
||||||
|
passCommand(args, logFacilities);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
|
@ -13,6 +13,20 @@ let pbkdf2Cache = [];
|
||||||
let scryptCache = [];
|
let scryptCache = [];
|
||||||
let passwordHashCacheIntervalId = -1;
|
let passwordHashCacheIntervalId = -1;
|
||||||
|
|
||||||
|
// Non-standard code object
|
||||||
|
let nonStandardCodes = [];
|
||||||
|
process.serverConfig.nonStandardCodes.forEach((nonStandardCodeRaw) => {
|
||||||
|
var newObject = {};
|
||||||
|
Object.keys(nonStandardCodeRaw).forEach((nsKey) => {
|
||||||
|
if (nsKey != "users") {
|
||||||
|
newObject[nsKey] = nonStandardCodeRaw[nsKey];
|
||||||
|
} else {
|
||||||
|
newObject["users"] = ipBlockList(nonStandardCodeRaw.users);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
nonStandardCodes.push(newObject);
|
||||||
|
});
|
||||||
|
|
||||||
if (!cluster.isPrimary) {
|
if (!cluster.isPrimary) {
|
||||||
passwordHashCacheIntervalId = setInterval(function () {
|
passwordHashCacheIntervalId = setInterval(function () {
|
||||||
pbkdf2Cache = pbkdf2Cache.filter(function (entry) {
|
pbkdf2Cache = pbkdf2Cache.filter(function (entry) {
|
||||||
|
@ -34,12 +48,12 @@ module.exports = (req, res, logFacilities, config, next) => {
|
||||||
: req.socket.remoteAddress;
|
: req.socket.remoteAddress;
|
||||||
|
|
||||||
// Scan for non-standard codes
|
// Scan for non-standard codes
|
||||||
if (!req.isProxy && config.nonStandardCodes != undefined) {
|
if (!req.isProxy && nonStandardCodes != undefined) {
|
||||||
for (let i = 0; i < config.nonStandardCodes.length; i++) {
|
for (let i = 0; i < nonStandardCodes.length; i++) {
|
||||||
if (
|
if (
|
||||||
matchHostname(config.nonStandardCodes[i].host, req.headers.host) &&
|
matchHostname(nonStandardCodes[i].host, req.headers.host) &&
|
||||||
ipMatch(
|
ipMatch(
|
||||||
config.nonStandardCodes[i].ip,
|
nonStandardCodes[i].ip,
|
||||||
req.socket ? req.socket.localAddress : undefined,
|
req.socket ? req.socket.localAddress : undefined,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
@ -48,12 +62,9 @@ module.exports = (req, res, logFacilities, config, next) => {
|
||||||
/\/+/g,
|
/\/+/g,
|
||||||
"/",
|
"/",
|
||||||
);
|
);
|
||||||
if (config.nonStandardCodes[i].regex) {
|
if (nonStandardCodes[i].regex) {
|
||||||
// Regex match
|
// Regex match
|
||||||
var createdRegex = createRegex(
|
var createdRegex = createRegex(nonStandardCodes[i].regex, true);
|
||||||
config.nonStandardCodes[i].regex,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
isMatch =
|
isMatch =
|
||||||
req.url.match(createdRegex) ||
|
req.url.match(createdRegex) ||
|
||||||
hrefWithoutDuplicateSlashes.match(createdRegex);
|
hrefWithoutDuplicateSlashes.match(createdRegex);
|
||||||
|
@ -61,13 +72,13 @@ module.exports = (req, res, logFacilities, config, next) => {
|
||||||
} else {
|
} else {
|
||||||
// Non-regex match
|
// Non-regex match
|
||||||
isMatch =
|
isMatch =
|
||||||
config.nonStandardCodes[i].url == hrefWithoutDuplicateSlashes ||
|
nonStandardCodes[i].url == hrefWithoutDuplicateSlashes ||
|
||||||
(os.platform() == "win32" &&
|
(os.platform() == "win32" &&
|
||||||
config.nonStandardCodes[i].url.toLowerCase() ==
|
nonStandardCodes[i].url.toLowerCase() ==
|
||||||
hrefWithoutDuplicateSlashes.toLowerCase());
|
hrefWithoutDuplicateSlashes.toLowerCase());
|
||||||
}
|
}
|
||||||
if (isMatch) {
|
if (isMatch) {
|
||||||
if (config.nonStandardCodes[i].scode == 401) {
|
if (nonStandardCodes[i].scode == 401) {
|
||||||
// HTTP authentication
|
// HTTP authentication
|
||||||
if (authIndex == -1) {
|
if (authIndex == -1) {
|
||||||
authIndex = i;
|
authIndex = i;
|
||||||
|
@ -75,12 +86,11 @@ module.exports = (req, res, logFacilities, config, next) => {
|
||||||
} else {
|
} else {
|
||||||
if (nonscodeIndex == -1) {
|
if (nonscodeIndex == -1) {
|
||||||
if (
|
if (
|
||||||
(config.nonStandardCodes[i].scode == 403 ||
|
(nonStandardCodes[i].scode == 403 ||
|
||||||
config.nonStandardCodes[i].scode == 451) &&
|
nonStandardCodes[i].scode == 451) &&
|
||||||
config.nonStandardCodes[i].users !== undefined
|
nonStandardCodes[i].users !== undefined
|
||||||
) {
|
) {
|
||||||
if (config.nonStandardCodes[i].users.check(reqip))
|
if (nonStandardCodes[i].users.check(reqip)) nonscodeIndex = i;
|
||||||
nonscodeIndex = i;
|
|
||||||
} else {
|
} else {
|
||||||
nonscodeIndex = i;
|
nonscodeIndex = i;
|
||||||
}
|
}
|
||||||
|
@ -93,7 +103,7 @@ module.exports = (req, res, logFacilities, config, next) => {
|
||||||
|
|
||||||
// Handle non-standard codes
|
// Handle non-standard codes
|
||||||
if (nonscodeIndex > -1) {
|
if (nonscodeIndex > -1) {
|
||||||
let nonscode = config.nonStandardCodes[nonscodeIndex];
|
let nonscode = nonStandardCodes[nonscodeIndex];
|
||||||
if (
|
if (
|
||||||
nonscode.scode == 301 ||
|
nonscode.scode == 301 ||
|
||||||
nonscode.scode == 302 ||
|
nonscode.scode == 302 ||
|
||||||
|
@ -143,7 +153,7 @@ module.exports = (req, res, logFacilities, config, next) => {
|
||||||
|
|
||||||
// Handle HTTP authentication
|
// Handle HTTP authentication
|
||||||
if (authIndex > -1) {
|
if (authIndex > -1) {
|
||||||
let authcode = config.nonStandardCodes[authIndex];
|
let authcode = nonStandardCodes[authIndex];
|
||||||
|
|
||||||
// Function to check if passwords match
|
// Function to check if passwords match
|
||||||
const checkIfPasswordMatches = (list, password, callback, _i) => {
|
const checkIfPasswordMatches = (list, password, callback, _i) => {
|
||||||
|
@ -446,8 +456,8 @@ module.exports.mainMessageListenerWrapper = (worker) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.commands = {
|
module.exports.commands = {
|
||||||
stop: (args, passCommand) => {
|
stop: (args, log, passCommand) => {
|
||||||
clearInterval(passwordHashCacheIntervalId);
|
clearInterval(passwordHashCacheIntervalId);
|
||||||
passCommand(args);
|
passCommand(args, log);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
252
src/utils/ipBlockList.js
Normal file
252
src/utils/ipBlockList.js
Normal file
|
@ -0,0 +1,252 @@
|
||||||
|
// IP Block list object
|
||||||
|
function ipBlockList(rawBlockList) {
|
||||||
|
// Initialize the instance with empty arrays
|
||||||
|
if (rawBlockList === undefined) rawBlockList = [];
|
||||||
|
var instance = {
|
||||||
|
raw: [],
|
||||||
|
rawtoPreparedMap: [],
|
||||||
|
prepared: [],
|
||||||
|
cidrs: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to normalize IPv4 address (remove leading zeros)
|
||||||
|
const normalizeIPv4Address = (address) => {
|
||||||
|
return address.replace(/(^|\.)(?:0(?!\.|$))+/g, "$1");
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to expand IPv6 address to full format
|
||||||
|
const expandIPv6Address = (address) => {
|
||||||
|
let fullAddress = "";
|
||||||
|
let expandedAddress = "";
|
||||||
|
let validGroupCount = 8;
|
||||||
|
let validGroupSize = 4;
|
||||||
|
|
||||||
|
let ipv4 = "";
|
||||||
|
const extractIpv4 =
|
||||||
|
/([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/;
|
||||||
|
const validateIpv4 =
|
||||||
|
/((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})/;
|
||||||
|
|
||||||
|
if (validateIpv4.test(address)) {
|
||||||
|
const oldGroups = address.match(extractIpv4);
|
||||||
|
for (let i = 1; i < oldGroups.length; i++) {
|
||||||
|
ipv4 +=
|
||||||
|
("00" + parseInt(oldGroups[i], 10).toString(16)).slice(-2) +
|
||||||
|
(i == 2 ? ":" : "");
|
||||||
|
}
|
||||||
|
address = address.replace(extractIpv4, ipv4);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address.indexOf("::") == -1) {
|
||||||
|
fullAddress = address;
|
||||||
|
} else {
|
||||||
|
const sides = address.split("::");
|
||||||
|
let groupsPresent = 0;
|
||||||
|
sides.forEach((side) => {
|
||||||
|
groupsPresent += side.split(":").length;
|
||||||
|
});
|
||||||
|
fullAddress += sides[0] + ":";
|
||||||
|
if (validGroupCount - groupsPresent > 1) {
|
||||||
|
fullAddress += "0000:".repeat(validGroupCount - groupsPresent);
|
||||||
|
}
|
||||||
|
fullAddress += sides[1];
|
||||||
|
}
|
||||||
|
let groups = fullAddress.split(":");
|
||||||
|
for (let i = 0; i < validGroupCount; i++) {
|
||||||
|
if (groups[i].length < validGroupSize) {
|
||||||
|
groups[i] = "0".repeat(validGroupSize - groups[i].length) + groups[i];
|
||||||
|
}
|
||||||
|
expandedAddress += i != validGroupCount - 1 ? groups[i] + ":" : groups[i];
|
||||||
|
}
|
||||||
|
return expandedAddress;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert IPv4 address to an integer representation
|
||||||
|
const ipv4ToInt = (ip) => {
|
||||||
|
const ips = ip.split(".");
|
||||||
|
return (
|
||||||
|
parseInt(ips[0]) * 16777216 +
|
||||||
|
parseInt(ips[1]) * 65536 +
|
||||||
|
parseInt(ips[2]) * 256 +
|
||||||
|
parseInt(ips[3])
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get IPv4 CIDR block limits (min and max)
|
||||||
|
const getIPv4CIDRLimits = (ip, cidrMask) => {
|
||||||
|
const ipInt = ipv4ToInt(ip);
|
||||||
|
const exp = Math.pow(2, 32 - cidrMask);
|
||||||
|
const ipMin = Math.floor(ipInt / exp) * exp;
|
||||||
|
const ipMax = ipMin + exp - 1;
|
||||||
|
return {
|
||||||
|
min: ipMin,
|
||||||
|
max: ipMax,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert IPv6 address to an array of blocks
|
||||||
|
const ipv6ToBlocks = (ip) => {
|
||||||
|
const ips = ip.split(":");
|
||||||
|
let ip2s = [];
|
||||||
|
ips.forEach((ipe) => {
|
||||||
|
ip2s.push(parseInt(ipe, 16));
|
||||||
|
});
|
||||||
|
return ip2s;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get IPv6 CIDR block limits (min and max)
|
||||||
|
const getIPv6CIDRLimits = (ip, cidrMask) => {
|
||||||
|
const ipBlocks = ipv6ToBlocks(ip);
|
||||||
|
const fieldsToDelete = Math.floor((128 - cidrMask) / 16);
|
||||||
|
const fieldMaskModify = (128 - cidrMask) % 16;
|
||||||
|
let ipBlockMin = [];
|
||||||
|
let ipBlockMax = [];
|
||||||
|
for (let i = 0; i < 8; i++) {
|
||||||
|
ipBlockMin.push(
|
||||||
|
i < 8 - fieldsToDelete
|
||||||
|
? i < 7 - fieldsToDelete
|
||||||
|
? ipBlocks[i]
|
||||||
|
: (ipBlocks[i] >> fieldMaskModify) << fieldMaskModify
|
||||||
|
: 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (let i = 0; i < 8; i++) {
|
||||||
|
ipBlockMax.push(
|
||||||
|
i < 8 - fieldsToDelete
|
||||||
|
? i < 7 - fieldsToDelete
|
||||||
|
? ipBlocks[i]
|
||||||
|
: ((ipBlocks[i] >> fieldMaskModify) << fieldMaskModify) +
|
||||||
|
Math.pow(2, fieldMaskModify) -
|
||||||
|
1
|
||||||
|
: 65535,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
min: ipBlockMin,
|
||||||
|
max: ipBlockMax,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if the IPv4 address matches the given CIDR block
|
||||||
|
const checkIfIPv4CIDRMatches = (ipInt, cidrObject) => {
|
||||||
|
if (cidrObject.v6) return false;
|
||||||
|
return ipInt >= cidrObject.min && ipInt <= cidrObject.max;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if the IPv6 address matches the given CIDR block
|
||||||
|
const checkIfIPv6CIDRMatches = (ipBlock, cidrObject) => {
|
||||||
|
if (!cidrObject.v6) return false;
|
||||||
|
for (let i = 0; i < 8; i++) {
|
||||||
|
if (ipBlock[i] < cidrObject.min[i] || ipBlock[i] > cidrObject.max[i])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to add an IP or CIDR block to the block list
|
||||||
|
instance.add = (rawValue) => {
|
||||||
|
// Add to raw block list
|
||||||
|
instance.raw.push(rawValue);
|
||||||
|
|
||||||
|
// Initialize variables
|
||||||
|
const beginIndex = instance.prepared.length;
|
||||||
|
const cidrIndex = instance.cidrs.length;
|
||||||
|
let cidrMask = null;
|
||||||
|
let isIPv6 = false;
|
||||||
|
|
||||||
|
// Check if the input contains CIDR notation
|
||||||
|
if (rawValue.indexOf("/") > -1) {
|
||||||
|
const rwArray = rawValue.split("/");
|
||||||
|
cidrMask = rwArray.pop();
|
||||||
|
rawValue = rwArray.join("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize the IP address or expand the IPv6 address
|
||||||
|
rawValue = rawValue.toLowerCase();
|
||||||
|
if (rawValue.indexOf("::ffff:") == 0) rawValue = rawValue.substring(7);
|
||||||
|
if (rawValue.indexOf(":") > -1) {
|
||||||
|
isIPv6 = true;
|
||||||
|
rawValue = expandIPv6Address(rawValue);
|
||||||
|
} else {
|
||||||
|
rawValue = normalizeIPv4Address(rawValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the IP or CIDR block to the appropriate list
|
||||||
|
if (cidrMask) {
|
||||||
|
let cidrLimits = {};
|
||||||
|
if (isIPv6) {
|
||||||
|
cidrLimits = getIPv6CIDRLimits(rawValue, cidrMask);
|
||||||
|
cidrLimits.v6 = true;
|
||||||
|
} else {
|
||||||
|
cidrLimits = getIPv4CIDRLimits(rawValue, cidrMask);
|
||||||
|
cidrLimits.v6 = false;
|
||||||
|
}
|
||||||
|
instance.cidrs.push(cidrLimits);
|
||||||
|
instance.rawtoPreparedMap.push({
|
||||||
|
cidr: true,
|
||||||
|
index: cidrIndex,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
instance.prepared.push(rawValue);
|
||||||
|
instance.rawtoPreparedMap.push({
|
||||||
|
cidr: false,
|
||||||
|
index: beginIndex,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to remove an IP or CIDR block from the block list
|
||||||
|
instance.remove = (ip) => {
|
||||||
|
const index = instance.raw.indexOf(ip);
|
||||||
|
if (index == -1) return false;
|
||||||
|
const map = instance.rawtoPreparedMap[index];
|
||||||
|
instance.raw.splice(index, 1);
|
||||||
|
instance.rawtoPreparedMap.splice(index, 1);
|
||||||
|
if (map.cidr) {
|
||||||
|
instance.cidrs.splice(map.index, 1);
|
||||||
|
} else {
|
||||||
|
instance.prepared.splice(map.index, 1);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to check if an IP is blocked by the block list
|
||||||
|
instance.check = (rawValue) => {
|
||||||
|
if (instance.raw.length == 0) return false;
|
||||||
|
let isIPv6 = false;
|
||||||
|
|
||||||
|
// Normalize or expand the IP address
|
||||||
|
rawValue = rawValue.toLowerCase();
|
||||||
|
if (rawValue == "localhost") rawValue = "::1";
|
||||||
|
if (rawValue.indexOf("::ffff:") == 0) rawValue = rawValue.substring(7);
|
||||||
|
if (rawValue.indexOf(":") > -1) {
|
||||||
|
isIPv6 = true;
|
||||||
|
rawValue = expandIPv6Address(rawValue);
|
||||||
|
} else {
|
||||||
|
rawValue = normalizeIPv4Address(rawValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the IP is in the prepared list
|
||||||
|
if (instance.prepared.indexOf(rawValue) > -1) return true;
|
||||||
|
|
||||||
|
// Check if the IP is within any CIDR block in the block list
|
||||||
|
if (instance.cidrs.length == 0) return false;
|
||||||
|
const ipParsedObject = (!isIPv6 ? ipv4ToInt : ipv6ToBlocks)(rawValue);
|
||||||
|
const checkMethod = !isIPv6
|
||||||
|
? checkIfIPv4CIDRMatches
|
||||||
|
: checkIfIPv6CIDRMatches;
|
||||||
|
|
||||||
|
return instance.cidrs.some((iCidr) => {
|
||||||
|
return checkMethod(ipParsedObject, iCidr);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add initial raw block list values to the instance
|
||||||
|
rawBlockList.forEach((rbe) => {
|
||||||
|
instance.add(rbe);
|
||||||
|
});
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ipBlockList;
|
|
@ -4,12 +4,12 @@ function ipMatch(IP1, IP2) {
|
||||||
if (!IP2) return false;
|
if (!IP2) return false;
|
||||||
|
|
||||||
// Function to normalize IPv4 address (remove leading zeros)
|
// Function to normalize IPv4 address (remove leading zeros)
|
||||||
function normalizeIPv4Address(address) {
|
const normalizeIPv4Address = (address) => {
|
||||||
return address.replace(/(^|\.)(?:0(?!\.|$))+/g, "$1");
|
return address.replace(/(^|\.)(?:0(?!\.|$))+/g, "$1");
|
||||||
}
|
};
|
||||||
|
|
||||||
// Function to expand IPv6 address to full format
|
// Function to expand IPv6 address to full format
|
||||||
function expandIPv6Address(address) {
|
const expandIPv6Address = (address) => {
|
||||||
let fullAddress = "";
|
let fullAddress = "";
|
||||||
let expandedAddress = "";
|
let expandedAddress = "";
|
||||||
let validGroupCount = 8;
|
let validGroupCount = 8;
|
||||||
|
@ -53,7 +53,7 @@ function ipMatch(IP1, IP2) {
|
||||||
expandedAddress += i != validGroupCount - 1 ? groups[i] + ":" : groups[i];
|
expandedAddress += i != validGroupCount - 1 ? groups[i] + ":" : groups[i];
|
||||||
}
|
}
|
||||||
return expandedAddress;
|
return expandedAddress;
|
||||||
}
|
};
|
||||||
|
|
||||||
// Normalize or expand IP addresses
|
// Normalize or expand IP addresses
|
||||||
IP1 = IP1.toLowerCase();
|
IP1 = IP1.toLowerCase();
|
||||||
|
|
81
tests/utils/ipBlockList.test.js
Normal file
81
tests/utils/ipBlockList.test.js
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
const ipBlockList = require("../../src/utils/ipBlockList");
|
||||||
|
|
||||||
|
describe("IP block list functionality", () => {
|
||||||
|
let blockList;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
blockList = ipBlockList([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should add and check IPv4 address", () => {
|
||||||
|
blockList.add("192.168.1.1");
|
||||||
|
expect(blockList.check("192.168.1.1")).toBe(true);
|
||||||
|
expect(blockList.check("192.168.1.2")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should add and check IPv6 address", () => {
|
||||||
|
blockList.add("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
|
||||||
|
expect(blockList.check("2001:0db8:85a3:0000:0000:8a2e:0370:7334")).toBe(
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
expect(blockList.check("2001:0db8:85a3:0000:0000:8a2e:0370:7335")).toBe(
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should add and check IPv4 CIDR block", () => {
|
||||||
|
blockList.add("192.168.1.0/24");
|
||||||
|
expect(blockList.check("192.168.1.1")).toBe(true);
|
||||||
|
expect(blockList.check("192.168.1.255")).toBe(true);
|
||||||
|
expect(blockList.check("192.168.2.1")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should add and check IPv6 CIDR block", () => {
|
||||||
|
blockList.add("2001:0db8:85a3::/64");
|
||||||
|
expect(blockList.check("2001:0db8:85a3:0000:0000:8a2e:0370:7334")).toBe(
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
expect(blockList.check("2001:0db8:85a3:0000:0000:8a2e:0370:7335")).toBe(
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
expect(blockList.check("2001:0db8:85a4:0000:0000:8a2e:0370:7334")).toBe(
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should remove IPv4 address", () => {
|
||||||
|
blockList.add("192.168.1.1");
|
||||||
|
expect(blockList.check("192.168.1.1")).toBe(true);
|
||||||
|
blockList.remove("192.168.1.1");
|
||||||
|
expect(blockList.check("192.168.1.1")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should remove IPv6 address", () => {
|
||||||
|
blockList.add("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
|
||||||
|
expect(blockList.check("2001:0db8:85a3:0000:0000:8a2e:0370:7334")).toBe(
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
blockList.remove("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
|
||||||
|
expect(blockList.check("2001:0db8:85a3:0000:0000:8a2e:0370:7334")).toBe(
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should remove IPv4 CIDR block", () => {
|
||||||
|
blockList.add("192.168.1.0/24");
|
||||||
|
expect(blockList.check("192.168.1.1")).toBe(true);
|
||||||
|
blockList.remove("192.168.1.0/24");
|
||||||
|
expect(blockList.check("192.168.1.1")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should remove IPv6 CIDR block", () => {
|
||||||
|
blockList.add("2001:0db8:85a3::/64");
|
||||||
|
expect(blockList.check("2001:0db8:85a3:0000:0000:8a2e:0370:7334")).toBe(
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
blockList.remove("2001:0db8:85a3::/64");
|
||||||
|
expect(blockList.check("2001:0db8:85a3:0000:0000:8a2e:0370:7334")).toBe(
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
Reference in a new issue