1
0
Fork 0
forked from svrjs/svrjs

Lint out the codebase.

This commit is contained in:
Dorian Niemiec 2024-08-24 08:07:31 +02:00
parent 1929641ba7
commit a49dba73fe
8 changed files with 374 additions and 276 deletions

View file

@ -1,9 +1,11 @@
const http = require("http");
const fs = require("fs");
const net = require("net");
const generateErrorStack = require("../utils/generateErrorStack.js");
const serverHTTPErrorDescs = require("../res/httpErrorDescriptions.js");
const fixNodeMojibakeURL = require("../utils/urlMojibakeFixer.js");
const getOS = require("../utils/getOS.js");
const ipMatch = require("../utils/ipMatch.js");
const svrjsInfo = require("../../svrjs.json");
const version = svrjsInfo.version;
@ -301,9 +303,9 @@ module.exports = (req, res, logFacilities, config, next) => {
res.responseEnd = (body) => {
// If body is Buffer, then it is converted to String anyway.
res.write(head + body + foot);
res.write(res.head + body + res.foot);
res.end();
}
};
// Server error calling method
res.error = (errorCode, extName, stack, ch) => {
@ -670,8 +672,16 @@ module.exports = (req, res, logFacilities, config, next) => {
};
try {
res.head = fs.existsSync("./.head") ? fs.readFileSync("./.head").toString() : (fs.existsSync("./head.html") ? fs.readFileSync("./head.html").toString() : ""); // header
res.foot = fs.existsSync("./.foot") ? fs.readFileSync("./.foot").toString() : (fs.existsSync("./foot.html") ? fs.readFileSync("./foot.html").toString() : ""); // footer
res.head = fs.existsSync("./.head")
? fs.readFileSync("./.head").toString()
: fs.existsSync("./head.html")
? fs.readFileSync("./head.html").toString()
: ""; // header
res.foot = fs.existsSync("./.foot")
? fs.readFileSync("./.foot").toString()
: fs.existsSync("./foot.html")
? fs.readFileSync("./foot.html").toString()
: ""; // footer
} catch (err) {
res.error(500, err);
}

View file

@ -3,7 +3,12 @@ module.exports = (req, res, logFacilities, config, next) => {
let fromMain = !(config.secure && !req.socket.encrypted);
// Handle redirects to HTTPS
if (config.secure && !fromMain && !config.disableNonEncryptedServer && !config.disableToHTTPSRedirect) {
if (
config.secure &&
!fromMain &&
!config.disableNonEncryptedServer &&
!config.disableToHTTPSRedirect
) {
var hostx = req.headers.host;
if (hostx === undefined) {
logFacilities.errmessage("Host header is missing.");
@ -17,7 +22,13 @@ module.exports = (req, res, logFacilities, config, next) => {
return;
}
var isPublicServer = !(req.socket.realRemoteAddress ? req.socket.realRemoteAddress : req.socket.remoteAddress).match(/^(?:localhost$|::1$|f[c-d][0-9a-f]{2}:|(?:::ffff:)?(?:(?:127|10)\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}|192\.168\.[0-9]{1,3}\.[0-9]{1,3}|172\.(?:1[6-9]|2[0-9]|3[0-1])\.[0-9]{1,3}\.[0-9]{1,3})$)/i);
var isPublicServer = !(
req.socket.realRemoteAddress
? req.socket.realRemoteAddress
: req.socket.remoteAddress
).match(
/^(?:localhost$|::1$|f[c-d][0-9a-f]{2}:|(?:::ffff:)?(?:(?:127|10)\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}|192\.168\.[0-9]{1,3}\.[0-9]{1,3}|172\.(?:1[6-9]|2[0-9]|3[0-1])\.[0-9]{1,3}\.[0-9]{1,3})$)/i,
);
var destinationPort = 0;
@ -26,13 +37,21 @@ module.exports = (req, res, logFacilities, config, next) => {
var hostPort = parsedHostx[2] ? parseInt(parsedHostx[2]) : 80;
if (isNaN(hostPort)) hostPort = 80;
if (hostPort == config.port || (config.port == config.pubport && !isPublicServer)) {
if (
hostPort == config.port ||
(config.port == config.pubport && !isPublicServer)
) {
destinationPort = config.sport;
} else {
destinationPort = config.spubport;
}
redirect("https://" + hostname + (destinationPort == 443 ? "" : (":" + destinationPort)) + req.url);
res.redirect(
"https://" +
hostname +
(destinationPort == 443 ? "" : ":" + destinationPort) +
req.url,
);
return;
}
@ -40,13 +59,23 @@ module.exports = (req, res, logFacilities, config, next) => {
if (config.wwwredirect) {
let hostname = req.headers.host.split(":");
let hostport = null;
if (hostname.length > 1 && (hostname[0] != "[" || hostname[hostname.length - 1] != "]")) hostport = hostname.pop();
if (
hostname.length > 1 &&
(hostname[0] != "[" || hostname[hostname.length - 1] != "]")
)
hostport = hostname.pop();
hostname = hostname.join(":");
if (hostname == domain && hostname.indexOf("www.") != 0) {
res.redirect((req.socket.encrypted ? "https" : "http") + "://www." + hostname + (hostport ? ":" + hostport : "") + req.url.replace(/\/+/g, "/"));
if (hostname == config.domain && hostname.indexOf("www.") != 0) {
res.redirect(
(req.socket.encrypted ? "https" : "http") +
"://www." +
hostname +
(hostport ? ":" + hostport : "") +
req.url.replace(/\/+/g, "/"),
);
return;
}
}
next();
}
};

View file

@ -1,4 +1,3 @@
const { emitWarning } = require("process");
const sanitizeURL = require("../utils/urlSanitizer.js");
const url = require("url");

View file

@ -1,3 +1,4 @@
const url = require("url");
const createRegex = require("../utils/createRegex.js");
const ipMatch = require("../utils/ipMatch.js");
const sanitizeURL = require("../utils/urlSanitizer.js");
@ -28,13 +29,24 @@ module.exports = (req, res, logFacilities, config, next) => {
// Add web root postfixes
if (!req.isProxy) {
let preparedReqUrl3 = (config.allowPostfixDoubleSlashes ? (req.parsedURL.pathname.replace(/\/+/,"/") + req.parsedURL.search + req.parsedURL.hash) : req.url);
let preparedReqUrl3 = config.allowPostfixDoubleSlashes
? req.parsedURL.pathname.replace(/\/+/, "/") +
req.parsedURL.search +
req.parsedURL.hash
: req.url;
let urlWithPostfix = preparedReqUrl3;
let postfixPrefix = "";
config.wwwrootPostfixPrefixesVHost.every(function (currentPostfixPrefix) {
if (preparedReqUrl3.indexOf(currentPostfixPrefix) == 0) {
if (currentPostfixPrefix.match(/\/+$/)) postfixPrefix = currentPostfixPrefix.replace(/\/+$/, "");
else if (urlWithPostfix.length == currentPostfixPrefix.length || urlWithPostfix[currentPostfixPrefix.length] == "?" || urlWithPostfix[currentPostfixPrefix.length] == "/" || urlWithPostfix[currentPostfixPrefix.length] == "#") postfixPrefix = currentPostfixPrefix;
if (currentPostfixPrefix.match(/\/+$/))
postfixPrefix = currentPostfixPrefix.replace(/\/+$/, "");
else if (
urlWithPostfix.length == currentPostfixPrefix.length ||
urlWithPostfix[currentPostfixPrefix.length] == "?" ||
urlWithPostfix[currentPostfixPrefix.length] == "/" ||
urlWithPostfix[currentPostfixPrefix.length] == "#"
)
postfixPrefix = currentPostfixPrefix;
else return true;
urlWithPostfix = urlWithPostfix.substring(postfixPrefix.length);
return false;
@ -43,15 +55,28 @@ module.exports = (req, res, logFacilities, config, next) => {
}
});
config.wwwrootPostfixesVHost.every(function (postfixEntry) {
if (matchHostname(postfixEntry.host) && ipMatch(postfixEntry.ip, req.socket ? req.socket.localAddress : undefined) && !(postfixEntry.skipRegex && preparedReqUrl3.match(createRegex(postfixEntry.skipRegex)))) {
urlWithPostfix = postfixPrefix + "/" + postfixEntry.postfix + urlWithPostfix;
if (
matchHostname(postfixEntry.host) &&
ipMatch(
postfixEntry.ip,
req.socket ? req.socket.localAddress : undefined,
) &&
!(
postfixEntry.skipRegex &&
preparedReqUrl3.match(createRegex(postfixEntry.skipRegex))
)
) {
urlWithPostfix =
postfixPrefix + "/" + postfixEntry.postfix + urlWithPostfix;
return false;
} else {
return true;
}
});
if (urlWithPostfix != preparedReqUrl3) {
logFacilities.resmessage("Added web root postfix: " + req.url + " => " + urlWithPostfix);
logFacilities.resmessage(
"Added web root postfix: " + req.url + " => " + urlWithPostfix,
);
req.url = urlWithPostfix;
try {
req.parsedURL = new URL(
@ -72,10 +97,14 @@ module.exports = (req, res, logFacilities, config, next) => {
let href = req.parsedURL.pathname + req.parsedURL.search;
var sHref = sanitizeURL(href, allowDoubleSlashes);
var preparedReqUrl2 = req.parsedURL.pathname + req.parsedURL.search + req.parsedURL.hash;
var sHref = sanitizeURL(href, config.allowDoubleSlashes);
var preparedReqUrl2 =
req.parsedURL.pathname + req.parsedURL.search + req.parsedURL.hash;
if (req.url != preparedReqUrl2 || sHref != href.replace(/\/\.(?=\/|$)/g, "/").replace(/\/+/g, "/")) {
if (
req.url != preparedReqUrl2 ||
sHref != href.replace(/\/\.(?=\/|$)/g, "/").replace(/\/+/g, "/")
) {
res.error(403);
logFacilities.errmessage("Content blocked.");
return;
@ -90,7 +119,9 @@ module.exports = (req, res, logFacilities, config, next) => {
rewrittenAgainURL.protocol = null;
rewrittenAgainURL.slashes = null;
rewrittenAgainURL = url.format(rewrittenAgainURL);
logFacilities.resmessage("URL sanitized: " + req.url + " => " + rewrittenAgainURL);
logFacilities.resmessage(
"URL sanitized: " + req.url + " => " + rewrittenAgainURL,
);
req.url = rewrittenAgainURL;
try {
req.parsedURL = new URL(

View file

@ -1,12 +1,13 @@
const os = require("os");
function createRegex(regex, isPath) {
var regexStrMatch = regex.match(/^\/((?:\\.|[^\/\\])*)\/([a-zA-Z0-9]*)$/);
const regexStrMatch = regex.match(/^\/((?:\\.|[^\/\\])*)\/([a-zA-Z0-9]*)$/);
if (!regexStrMatch) throw new Error("Invalid regular expression: " + regex);
var searchString = regexStrMatch[1];
var modifiers = regexStrMatch[2];
if (isPath && !modifiers.match(/i/i) && os.platform() == "win32") modifiers += "i";
const searchString = regexStrMatch[1];
let modifiers = regexStrMatch[2];
if (isPath && !modifiers.match(/i/i) && os.platform() == "win32")
modifiers += "i";
return new RegExp(searchString, modifiers);
}
}
module.exports = createRegex;
module.exports = createRegex;

View file

@ -10,19 +10,23 @@ function ipMatch(IP1, IP2) {
// Function to expand IPv6 address to full format
function expandIPv6Address(address) {
var fullAddress = "";
var expandedAddress = "";
var validGroupCount = 8;
var validGroupSize = 4;
let fullAddress = "";
let expandedAddress = "";
let validGroupCount = 8;
let validGroupSize = 4;
var ipv4 = "";
var extractIpv4 = /([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/;
var 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})/;
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)) {
var oldGroups = address.match(extractIpv4);
for (var i = 1; i < oldGroups.length; i++) {
ipv4 += ("00" + (parseInt(oldGroups[i], 10).toString(16))).slice(-2) + (i == 2 ? ":" : "");
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);
}
@ -30,9 +34,9 @@ function ipMatch(IP1, IP2) {
if (address.indexOf("::") == -1) {
fullAddress = address;
} else {
var sides = address.split("::");
var groupsPresent = 0;
sides.forEach(function (side) {
const sides = address.split("::");
let groupsPresent = 0;
sides.forEach((side) => {
groupsPresent += side.split(":").length;
});
fullAddress += sides[0] + ":";
@ -41,12 +45,12 @@ function ipMatch(IP1, IP2) {
}
fullAddress += sides[1];
}
var groups = fullAddress.split(":");
for (var i = 0; i < validGroupCount; i++) {
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];
expandedAddress += i != validGroupCount - 1 ? groups[i] + ":" : groups[i];
}
return expandedAddress;
}
@ -73,6 +77,6 @@ function ipMatch(IP1, IP2) {
// Check if processed IPs are equal
if (IP1 == IP2) return true;
else return false;
}
}
module.exports = ipMatch;
module.exports = ipMatch;

View file

@ -1,75 +1,77 @@
const createRegex = require('../../src/utils/createRegex');
const os = require('os');
const createRegex = require("../../src/utils/createRegex");
const os = require("os");
jest.mock('os', () => ({
jest.mock("os", () => ({
platform: jest.fn(),
}));
describe('createRegex', () => {
describe("createRegex", () => {
beforeEach(() => {
os.platform.mockReset();
});
test('should throw an error for invalid regular expression', () => {
expect(() => createRegex('invalid/regex', false)).toThrow('Invalid regular expression: invalid/regex');
test("should throw an error for invalid regular expression", () => {
expect(() => createRegex("invalid/regex", false)).toThrow(
"Invalid regular expression: invalid/regex",
);
});
test('should create a regular expression without modifiers', () => {
const regex = createRegex('/test/', false);
test("should create a regular expression without modifiers", () => {
const regex = createRegex("/test/", false);
expect(regex).toBeInstanceOf(RegExp);
expect(regex.source).toBe('test');
expect(regex.flags).toBe('');
expect(regex.source).toBe("test");
expect(regex.flags).toBe("");
});
test('should create a regular expression with modifiers', () => {
const regex = createRegex('/test/gi', false);
test("should create a regular expression with modifiers", () => {
const regex = createRegex("/test/gi", false);
expect(regex).toBeInstanceOf(RegExp);
expect(regex.source).toBe('test');
expect(regex.flags).toBe('gi');
expect(regex.source).toBe("test");
expect(regex.flags).toBe("gi");
});
test('should add "i" modifier for paths on Windows', () => {
os.platform.mockReturnValue('win32');
const regex = createRegex('/test/', true);
os.platform.mockReturnValue("win32");
const regex = createRegex("/test/", true);
expect(regex).toBeInstanceOf(RegExp);
expect(regex.source).toBe('test');
expect(regex.flags).toBe('i');
expect(regex.source).toBe("test");
expect(regex.flags).toBe("i");
});
test('should not add "i" modifier for paths on non-Windows platforms', () => {
os.platform.mockReturnValue('linux');
const regex = createRegex('/test/', true);
os.platform.mockReturnValue("linux");
const regex = createRegex("/test/", true);
expect(regex).toBeInstanceOf(RegExp);
expect(regex.source).toBe('test');
expect(regex.flags).toBe('');
expect(regex.source).toBe("test");
expect(regex.flags).toBe("");
});
test('should not add "i" modifier if already present', () => {
os.platform.mockReturnValue('win32');
const regex = createRegex('/test/i', true);
os.platform.mockReturnValue("win32");
const regex = createRegex("/test/i", true);
expect(regex).toBeInstanceOf(RegExp);
expect(regex.source).toBe('test');
expect(regex.flags).toBe('i');
expect(regex.source).toBe("test");
expect(regex.flags).toBe("i");
});
test('should handle escaped characters in the search string', () => {
const regex = createRegex('/test\\/path/', false);
test("should handle escaped characters in the search string", () => {
const regex = createRegex("/test\\/path/", false);
expect(regex).toBeInstanceOf(RegExp);
expect(regex.source).toBe('test\\/path');
expect(regex.flags).toBe('');
expect(regex.source).toBe("test\\/path");
expect(regex.flags).toBe("");
});
test('should handle multiple modifiers', () => {
const regex = createRegex('/test/gim', false);
test("should handle multiple modifiers", () => {
const regex = createRegex("/test/gim", false);
expect(regex).toBeInstanceOf(RegExp);
expect(regex.source).toBe('test');
expect(regex.flags).toBe('gim');
expect(regex.source).toBe("test");
expect(regex.flags).toBe("gim");
});
test('should handle empty search string', () => {
const regex = createRegex('/^$/', false);
test("should handle empty search string", () => {
const regex = createRegex("/^$/", false);
expect(regex).toBeInstanceOf(RegExp);
expect(regex.source).toBe('^$');
expect(regex.flags).toBe('');
expect(regex.source).toBe("^$");
expect(regex.flags).toBe("");
});
});

View file

@ -1,60 +1,82 @@
const ipMatch = require('../../src/utils/ipMatch');
const ipMatch = require("../../src/utils/ipMatch");
describe('ipMatch', () => {
test('should return true if IP1 is empty', () => {
expect(ipMatch('', '192.168.1.1')).toBe(true);
describe("ipMatch", () => {
test("should return true if IP1 is empty", () => {
expect(ipMatch("", "192.168.1.1")).toBe(true);
});
test('should return false if IP2 is empty', () => {
expect(ipMatch('192.168.1.1', '')).toBe(false);
test("should return false if IP2 is empty", () => {
expect(ipMatch("192.168.1.1", "")).toBe(false);
});
test('should return true if both IPs are empty', () => {
expect(ipMatch('', '')).toBe(true);
test("should return true if both IPs are empty", () => {
expect(ipMatch("", "")).toBe(true);
});
test('should return true if both IPs are the same IPv4 address', () => {
expect(ipMatch('192.168.1.1', '192.168.1.1')).toBe(true);
test("should return true if both IPs are the same IPv4 address", () => {
expect(ipMatch("192.168.1.1", "192.168.1.1")).toBe(true);
});
test('should return false if IPs are different IPv4 addresses', () => {
expect(ipMatch('192.168.1.1', '192.168.1.2')).toBe(false);
test("should return false if IPs are different IPv4 addresses", () => {
expect(ipMatch("192.168.1.1", "192.168.1.2")).toBe(false);
});
test('should normalize IPv4 addresses with leading zeros', () => {
expect(ipMatch('192.168.001.001', '192.168.1.1')).toBe(true);
test("should normalize IPv4 addresses with leading zeros", () => {
expect(ipMatch("192.168.001.001", "192.168.1.1")).toBe(true);
});
test('should return true if both IPs are the same IPv6 address', () => {
expect(ipMatch('2001:0db8:85a3:0000:0000:8a2e:0370:7334', '2001:db8:85a3::8a2e:370:7334')).toBe(true);
test("should return true if both IPs are the same IPv6 address", () => {
expect(
ipMatch(
"2001:0db8:85a3:0000:0000:8a2e:0370:7334",
"2001:db8:85a3::8a2e:370:7334",
),
).toBe(true);
});
test('should return false if IPs are different IPv6 addresses', () => {
expect(ipMatch('2001:0db8:85a3:0000:0000:8a2e:0370:7334', '2001:db8:85a3::8a2e:370:7335')).toBe(false);
test("should return false if IPs are different IPv6 addresses", () => {
expect(
ipMatch(
"2001:0db8:85a3:0000:0000:8a2e:0370:7334",
"2001:db8:85a3::8a2e:370:7335",
),
).toBe(false);
});
test('should expand IPv6 addresses with "::"', () => {
expect(ipMatch('::1', '0:0:0:0:0:0:0:1')).toBe(true);
expect(ipMatch("::1", "0:0:0:0:0:0:0:1")).toBe(true);
});
test('should handle IPv6 addresses with embedded IPv4 addresses', () => {
expect(ipMatch('::ffff:192.168.1.1', '192.168.1.1')).toBe(true);
test("should handle IPv6 addresses with embedded IPv4 addresses", () => {
expect(ipMatch("::ffff:192.168.1.1", "192.168.1.1")).toBe(true);
});
test('should handle "localhost" as IPv6 loopback address', () => {
expect(ipMatch('localhost', '::1')).toBe(true);
expect(ipMatch("localhost", "::1")).toBe(true);
});
test('should handle mixed case IP addresses', () => {
expect(ipMatch('192.168.1.1', '192.168.1.1')).toBe(true);
expect(ipMatch('2001:DB8:85A3::8A2E:370:7334', '2001:db8:85a3::8a2e:370:7334')).toBe(true);
test("should handle mixed case IP addresses", () => {
expect(ipMatch("192.168.1.1", "192.168.1.1")).toBe(true);
expect(
ipMatch("2001:DB8:85A3::8A2E:370:7334", "2001:db8:85a3::8a2e:370:7334"),
).toBe(true);
});
test('should handle IPv6 addresses with leading zeros', () => {
expect(ipMatch('2001:0db8:85a3:0000:0000:8a2e:0370:7334', '2001:db8:85a3::8a2e:370:7334')).toBe(true);
test("should handle IPv6 addresses with leading zeros", () => {
expect(
ipMatch(
"2001:0db8:85a3:0000:0000:8a2e:0370:7334",
"2001:db8:85a3::8a2e:370:7334",
),
).toBe(true);
});
test('should handle IPv6 addresses with mixed case and leading zeros', () => {
expect(ipMatch('2001:0DB8:85A3:0000:0000:8A2E:0370:7334', '2001:db8:85a3::8a2e:370:7334')).toBe(true);
test("should handle IPv6 addresses with mixed case and leading zeros", () => {
expect(
ipMatch(
"2001:0DB8:85A3:0000:0000:8A2E:0370:7334",
"2001:db8:85a3::8a2e:370:7334",
),
).toBe(true);
});
});