1
0
Fork 0
forked from svrjs/svrjs

Cleaned up SVR.JS code even more...

This commit is contained in:
Dorian Niemiec 2023-09-03 11:08:16 +02:00
parent 8dd707c44d
commit bd7098c2c6
2 changed files with 138 additions and 110 deletions

View file

@ -3,7 +3,7 @@
"port": 80, "port": 80,
"pubport": 80, "pubport": 80,
"page404": "404.html", "page404": "404.html",
"timestamp": 1693729629944, "timestamp": 1693730077326,
"blacklist": [], "blacklist": [],
"nonStandardCodes": [], "nonStandardCodes": [],
"enableCompression": true, "enableCompression": true,

56
svr.js
View file

@ -1193,7 +1193,6 @@ try {
if (vnum === undefined) vnum = 0; if (vnum === undefined) vnum = 0;
if (process.isBun) vnum = 64; if (process.isBun) vnum = 64;
// NOTE: One of regexes used here is reported as vulnerable according to Devina ReDOS checker, but it's actually just FALSE POSITIVE.
function sanitizeURL(resource) { function sanitizeURL(resource) {
if (resource == "*") return "*"; if (resource == "*") return "*";
if (resource == "") return ""; if (resource == "") return "";
@ -1228,6 +1227,8 @@ function sanitizeURL(resource) {
function fixNodeMojibakeURL(string) { function fixNodeMojibakeURL(string) {
var encoded = ""; var encoded = "";
//Encode URLs
Buffer.from(string, "latin1").forEach(function (value) { Buffer.from(string, "latin1").forEach(function (value) {
if(value > 127) { if(value > 127) {
encoded += "%" + (value < 16 ? "0" : "") + value.toString(16).toUpperCase(); encoded += "%" + (value < 16 ? "0" : "") + value.toString(16).toUpperCase();
@ -1235,6 +1236,8 @@ function fixNodeMojibakeURL(string) {
encoded += String.fromCodePoint(value); encoded += String.fromCodePoint(value);
} }
}); });
//Upper case the URL encodings
return encoded.replace(/%[0-9a-f-A-F]{2}/g, function (match) { return encoded.replace(/%[0-9a-f-A-F]{2}/g, function (match) {
return match.toUpperCase(); return match.toUpperCase();
}); });
@ -1300,6 +1303,7 @@ function LOG(s) {
} }
} }
// Server console function
var serverconsole = { var serverconsole = {
climessage: function (msg) { climessage: function (msg) {
if (msg.indexOf("\n") != -1) { if (msg.indexOf("\n") != -1) {
@ -1571,6 +1575,7 @@ if (!disableMods) {
} }
// SHA256 function
function sha256(s) { function sha256(s) {
if (crypto.__disabled__ === undefined) { if (crypto.__disabled__ === undefined) {
var hash = crypto.createHash("SHA256"); var hash = crypto.createHash("SHA256");
@ -1761,6 +1766,7 @@ function isIndexOfForbiddenPath(decodedHref, match) {
} }
// Set up forbidden paths
var forbiddenPaths = {}; var forbiddenPaths = {};
forbiddenPaths.config = getInitializePath("./config.json"); forbiddenPaths.config = getInitializePath("./config.json");
@ -1907,6 +1913,7 @@ if (!cluster.isPrimary) {
} else { } else {
server2.on("connect", connhandler); server2.on("connect", connhandler);
} }
server2.on("error", function (err) { server2.on("error", function (err) {
attmtsRedir--; attmtsRedir--;
if (cluster.isPrimary === undefined && attmtsRedir >= 0) { if (cluster.isPrimary === undefined && attmtsRedir >= 0) {
@ -2745,6 +2752,7 @@ if (!cluster.isPrimary) {
if (a != "transfer-encoding" && a != "connection" && a != "keep-alive" && a != "upgrade") return res.setHeaderNodeApi(a, b); if (a != "transfer-encoding" && a != "connection" && a != "keep-alive" && a != "upgrade") return res.setHeaderNodeApi(a, b);
return false; return false;
}; };
// Set HTTP/1.x headers // Set HTTP/1.x headers
if (!req.headers.host) req.headers.host = req.headers[":authority"]; if (!req.headers.host) req.headers.host = req.headers[":authority"];
(req.headers[":path"] == undefined ? (function () {})() : req.url = req.headers[":path"]); (req.headers[":path"] == undefined ? (function () {})() : req.url = req.headers[":path"]);
@ -2829,6 +2837,7 @@ if (!cluster.isPrimary) {
return; return;
} }
// Set up X-Forwarded-For
var reqport = ""; var reqport = "";
var reqip = ""; var reqip = "";
var oldport = ""; var oldport = "";
@ -2876,6 +2885,8 @@ if (!cluster.isPrimary) {
res.end(); res.end();
} }
/*
UNUSED CODE:
// function responseEndGzip(d) { // function responseEndGzip(d) {
// if (d === undefined) d = fd; // if (d === undefined) d = fd;
// zlib.gzip(head + d + foot, function (err, buff) { // zlib.gzip(head + d + foot, function (err, buff) {
@ -2899,6 +2910,7 @@ if (!cluster.isPrimary) {
// } // }
// }); // });
// } // }
*/
// Error descriptions // Error descriptions
var serverErrorDescs = { var serverErrorDescs = {
@ -2973,7 +2985,6 @@ if (!cluster.isPrimary) {
} }
// Determine error file // Determine error file
function getErrorFileName(list, callback, _i) { function getErrorFileName(list, callback, _i) {
function medCallback(p) { function medCallback(p) {
if(p) callback(p); if(p) callback(p);
@ -3037,7 +3048,6 @@ if (!cluster.isPrimary) {
} }
getErrorFileName(errorPages, function(errorFile) { getErrorFileName(errorPages, function(errorFile) {
// Generate error stack if not provided // Generate error stack if not provided
if (Object.prototype.toString.call(stack) === "[object Error]") stack = generateErrorStack(stack); if (Object.prototype.toString.call(stack) === "[object Error]") stack = generateErrorStack(stack);
if (stack === undefined) stack = generateErrorStack(new Error("Unknown error")); if (stack === undefined) stack = generateErrorStack(new Error("Unknown error"));
@ -3254,10 +3264,13 @@ if (!cluster.isPrimary) {
// MOD EXCECUTION FUNCTION // MOD EXCECUTION FUNCTION
function modExecute(mods, ffinals) { function modExecute(mods, ffinals) {
// Prepare modFunction
var modFunction = ffinals; var modFunction = ffinals;
for (var i = mods.length - 1; i >= 0; i--) { for (var i = mods.length - 1; i >= 0; i--) {
modFunction = mods[i].callback(req, res, serverconsole, responseEnd, href, ext, uobject, search, "index.html", users, page404, head, foot, fd, modFunction, configJSON, callServerError, getCustomHeaders, origHref, redirect, parsePostData); modFunction = mods[i].callback(req, res, serverconsole, responseEnd, href, ext, uobject, search, "index.html", users, page404, head, foot, fd, modFunction, configJSON, callServerError, getCustomHeaders, origHref, redirect, parsePostData);
} }
// Execute modfunction
modFunction(); modFunction();
} }
@ -3273,6 +3286,8 @@ if (!cluster.isPrimary) {
} else { } else {
vresCalled = true; vresCalled = true;
} }
/*
UNUSED CODE
// function responseEndGzip(d) { // function responseEndGzip(d) {
// if (d === undefined) d = fd; // if (d === undefined) d = fd;
// zlib.gzip(head + d + foot, function (err, buff) { // zlib.gzip(head + d + foot, function (err, buff) {
@ -3296,6 +3311,7 @@ if (!cluster.isPrimary) {
// } // }
// }); // });
// } // }
*/
function responseEnd(d) { function responseEnd(d) {
if (d === undefined) d = fd; if (d === undefined) d = fd;
@ -4005,6 +4021,7 @@ if (!cluster.isPrimary) {
try { try {
// scan blacklist // scan blacklist
if (blacklist.check(reqip) && href != "/favicon.ico") { if (blacklist.check(reqip) && href != "/favicon.ico") {
// Return client blocked message
var bheaders = getCustomHeaders(); var bheaders = getCustomHeaders();
bheaders["Content-Type"] = "text/html; charset=utf8"; bheaders["Content-Type"] = "text/html; charset=utf8";
res.writeHead(403, "Client blocked", bheaders); res.writeHead(403, "Client blocked", bheaders);
@ -4014,25 +4031,29 @@ if (!cluster.isPrimary) {
} }
if (req.url == "*") { if (req.url == "*") {
// Handle "*" URL
if (req.method == "OPTIONS") { if (req.method == "OPTIONS") {
// Respond with list of methods
var hdss = getCustomHeaders(); var hdss = getCustomHeaders();
hdss["Allow"] = "GET, POST, HEAD, OPTIONS"; hdss["Allow"] = "GET, POST, HEAD, OPTIONS";
res.writeHead(204, "No Content", hdss); res.writeHead(204, "No Content", hdss);
res.end(); res.end();
return; return;
} else { } else {
// SVR.JS doesn't understand that request, throw a 400 error
callServerError(400); callServerError(400);
return; return;
} }
} }
if (req.method == "CONNECT") { if (req.method == "CONNECT") {
// CONNECT requests should be handled in "connect" event.
callServerError(501); callServerError(501);
serverconsole.errmessage("CONNECT requests aren't supported. Your JS runtime probably doesn't support 'connect' handler for HTTP library."); serverconsole.errmessage("CONNECT requests aren't supported. Your JS runtime probably doesn't support 'connect' handler for HTTP library.");
return; return;
} }
//SANITIZE URL // Sanitize URL
var sanitizedHref = sanitizeURL(href); var sanitizedHref = sanitizeURL(href);
// Check if URL is "dirty" // Check if URL is "dirty"
@ -4071,7 +4092,6 @@ if (!cluster.isPrimary) {
// Handle redirects to HTTPS // Handle redirects to HTTPS
if(secure && !fromMain && !disableNonEncryptedServer && !disableToHTTPSRedirect) { if(secure && !fromMain && !disableNonEncryptedServer && !disableToHTTPSRedirect) {
var hostx = req.headers.host; var hostx = req.headers.host;
if (hostx === undefined) { if (hostx === undefined) {
serverconsole.errmessage("Bad request!"); serverconsole.errmessage("Bad request!");
@ -4093,6 +4113,7 @@ if (!cluster.isPrimary) {
} catch (err) { } catch (err) {
// URL parse error... // URL parse error...
} }
if (urlp.host == "localhost" || urlp.host == "localhost:" + port.toString() || urlp.host == "127.0.0.1" || urlp.host == "127.0.0.1:" + port.toString() || urlp.host == "::1" || urlp.host == "::1:" + port.toString()) { if (urlp.host == "localhost" || urlp.host == "localhost:" + port.toString() || urlp.host == "127.0.0.1" || urlp.host == "127.0.0.1:" + port.toString() || urlp.host == "::1" || urlp.host == "::1:" + port.toString()) {
urlp.protocol = "https:"; urlp.protocol = "https:";
if (sport == 443) { if (sport == 443) {
@ -4143,9 +4164,9 @@ if (!cluster.isPrimary) {
} catch (err) { } catch (err) {
// Leave URL as it is... // Leave URL as it is...
} }
redirect(lloc + requestURL); redirect(lloc + requestURL);
return; return;
} }
// Handle redirects to addresses with www. // Handle redirects to addresses with www.
@ -4264,31 +4285,31 @@ if (!cluster.isPrimary) {
var nonscodeIndex = -1; var nonscodeIndex = -1;
var authIndex = -1; var authIndex = -1;
var regexI = []; var regexI = [];
// Scan for non-standard codes
if (!isProxy && nonStandardCodes != undefined) { if (!isProxy && nonStandardCodes != undefined) {
for (var i = 0; i < nonStandardCodes.length; i++) { for (var i = 0; i < nonStandardCodes.length; i++) {
if(matchHostname(nonStandardCodes[i].host)) { if(matchHostname(nonStandardCodes[i].host)) {
var isMatch = false; var isMatch = false;
if (nonStandardCodes[i].regex) { if (nonStandardCodes[i].regex) {
// Regex match
var createdRegex = createRegex(nonStandardCodes[i].regex, true); var createdRegex = createRegex(nonStandardCodes[i].regex, true);
isMatch = req.url.match(createdRegex) || href.match(createdRegex); isMatch = req.url.match(createdRegex) || href.match(createdRegex);
regexI.push(createdRegex); regexI.push(createdRegex);
} else { } else {
// Non-regex match
isMatch = nonStandardCodes[i].url == href || (os.platform() == "win32" && nonStandardCodes[i].url.toLowerCase() == href.toLowerCase()); isMatch = nonStandardCodes[i].url == href || (os.platform() == "win32" && nonStandardCodes[i].url.toLowerCase() == href.toLowerCase());
} }
if (isMatch) { if (isMatch) {
if (nonStandardCodes[i].scode == 401) { if (nonStandardCodes[i].scode == 401) {
// HTTP authentication
if (authIndex == -1) { if (authIndex == -1) {
authIndex = i; authIndex = i;
} }
} else { } else {
if (nonscodeIndex == -1) { if (nonscodeIndex == -1) {
if ((nonStandardCodes[i].scode == 403 || nonStandardCodes[i].scode == 451) && nonStandardCodes[i].users !== undefined) { if ((nonStandardCodes[i].scode == 403 || nonStandardCodes[i].scode == 451) && nonStandardCodes[i].users !== undefined) {
var toBreakLoop = false; if (nonStandardCodes[i].users.check(reqip)) nonscodeIndex = i;
if (nonStandardCodes[i].users.check(reqip)) {
nonscodeIndex = i;
toBreakLoop = true;
}
if (toBreakLoop) break;
} else { } else {
nonscodeIndex = i; nonscodeIndex = i;
} }
@ -4336,6 +4357,7 @@ if (!cluster.isPrimary) {
if (authIndex > -1) { if (authIndex > -1) {
var authcode = nonStandardCodes[authIndex]; var authcode = nonStandardCodes[authIndex];
// Function to check if passwords match
function checkIfPasswordMatches(list, password, callback, _i) { function checkIfPasswordMatches(list, password, callback, _i) {
if(!_i) _i = 0; if(!_i) _i = 0;
var cb = function (hash) { var cb = function (hash) {
@ -4474,8 +4496,10 @@ if (!cluster.isPrimary) {
} }
} }
if (authcode.disableBruteProtection) { if (authcode.disableBruteProtection) {
// Don't brute-force protect it, just do HTTP authentication
authorizedCallback(false); authorizedCallback(false);
} else if (!process.send) { } else if (!process.send) {
// Query data from JS object database
if (!bruteForceDb[reqip] || !bruteForceDb[reqip].lastAttemptDate || (new Date() - 300000 >= bruteForceDb[reqip].lastAttemptDate)) { if (!bruteForceDb[reqip] || !bruteForceDb[reqip].lastAttemptDate || (new Date() - 300000 >= bruteForceDb[reqip].lastAttemptDate)) {
if (bruteForceDb[reqip] && bruteForceDb[reqip].invalidAttempts >= 10) bruteForceDb[reqip] = { if (bruteForceDb[reqip] && bruteForceDb[reqip].invalidAttempts >= 10) bruteForceDb[reqip] = {
invalidAttempts: 5 invalidAttempts: 5
@ -4488,6 +4512,7 @@ if (!cluster.isPrimary) {
} else { } else {
var listenerEmitted = false; var listenerEmitted = false;
// Listen for brute-force protection response
function authMessageListener(message) { function authMessageListener(message) {
if (listenerEmitted) return; if (listenerEmitted) return;
if (message == "\x14AUTHA" + reqip || message == "\x14AUTHD" + reqip) { if (message == "\x14AUTHA" + reqip || message == "\x14AUTHD" + reqip) {
@ -4789,7 +4814,7 @@ function start(init) {
if (disableMods) serverconsole.locwarnmessage("SVR.JS is running without mods and server-side JavaScript enabled. Web applications may not work as expected"); if (disableMods) serverconsole.locwarnmessage("SVR.JS is running without mods and server-side JavaScript enabled. Web applications may not work as expected");
console.log(); console.log();
//Display mod errors // Display mod and server-side JavaScript errors
if(process.isPrimary || process.isPrimary === undefined) { if(process.isPrimary || process.isPrimary === undefined) {
modLoadingErrors.forEach(function(modLoadingError) { modLoadingErrors.forEach(function(modLoadingError) {
serverconsole.locwarnmessage("There was a problem while loading a \"" + modLoadingError.modName + "\" mod."); serverconsole.locwarnmessage("There was a problem while loading a \"" + modLoadingError.modName + "\" mod.");
@ -4810,6 +4835,7 @@ function start(init) {
else serverconsole.locmessage("Node.JS version: " + process.version); else serverconsole.locmessage("Node.JS version: " + process.version);
var CPUs = os.cpus(); var CPUs = os.cpus();
if (CPUs.length > 0) serverconsole.locmessage("CPU: " + (CPUs.length > 1 ? CPUs.length + "x " : "") + CPUs[0].model); if (CPUs.length > 0) serverconsole.locmessage("CPU: " + (CPUs.length > 1 ? CPUs.length + "x " : "") + CPUs[0].model);
// Throw errors // Throw errors
if (vnum < 64) throw new Error("SVR.JS requires Node.JS 10.0.0 and newer, but your Node.JS version isn't supported by SVR.JS."); if (vnum < 64) throw new Error("SVR.JS requires Node.JS 10.0.0 and newer, but your Node.JS version isn't supported by SVR.JS.");
if (configJSONRErr) throw new Error("Can't read SVR.JS configuration file: " + configJSONRErr.message); if (configJSONRErr) throw new Error("Can't read SVR.JS configuration file: " + configJSONRErr.message);
@ -4822,6 +4848,7 @@ function start(init) {
if (netIPs.indexOf(listenAddress) > -1) throw new Error("SVR.JS can't listen on subnet address."); if (netIPs.indexOf(listenAddress) > -1) throw new Error("SVR.JS can't listen on subnet address.");
} }
} }
// Information about starting the server // Information about starting the server
if (!(secure && disableNonEncryptedServer)) serverconsole.locmessage("Starting HTTP server at " + (typeof port == "number" ? (listenAddress ? ((listenAddress.indexOf(":") > -1 ? "[" + listenAddress + "]" : listenAddress)) + ":" : "[::]:") : "") + port.toString() + "..."); if (!(secure && disableNonEncryptedServer)) serverconsole.locmessage("Starting HTTP server at " + (typeof port == "number" ? (listenAddress ? ((listenAddress.indexOf(":") > -1 ? "[" + listenAddress + "]" : listenAddress)) + ":" : "[::]:") : "") + port.toString() + "...");
if (secure) serverconsole.locmessage("Starting HTTPS server at " + (typeof sport == "number" ? (sListenAddress ? ((sListenAddress.indexOf(":") > -1 ? "[" + sListenAddress + "]" : sListenAddress)) + ":" : "[::]:") : "") + sport.toString() + "..."); if (secure) serverconsole.locmessage("Starting HTTPS server at " + (typeof sport == "number" ? (sListenAddress ? ((sListenAddress.indexOf(":") > -1 ? "[" + sListenAddress + "]" : sListenAddress)) + ":" : "[::]:") : "") + sport.toString() + "...");
@ -5574,7 +5601,8 @@ if (cluster.isPrimary || cluster.isPrimary === undefined) {
} }
}); });
} }
//Call start
// Start SVR.JS!
try { try {
start(true); start(true);
} catch (err) { } catch (err) {