1
0
Fork 0
forked from svrjs/svrjs

Update to SVR.JS 4.0.0-beta2

This commit is contained in:
Dorian Niemiec 2024-08-29 06:55:08 +02:00
parent 827e1efd4e
commit 920d942016
9 changed files with 91 additions and 94 deletions

View file

@ -5,7 +5,7 @@
"scripts": {
"build": "node esbuild.config.js",
"dev": "npm run build && npm run start",
"lint": "eslint --no-error-on-unmatched-pattern src/**/*.js src/*.js tests/**/*.test.js tests/**/*.js utils/**/*.js",
"lint": "eslint --no-error-on-unmatched-pattern src/**/*.js src/*.js tests/**/*.test.js tests/**/*.js tests/*.test.js tests/*.js utils/**/*.js utils/*.js",
"lint:fix": "npm run lint -- --fix",
"start": "node dist/svr.js",
"test": "jest"

View file

@ -22,57 +22,50 @@ function getInitializePath(to) {
}
}
// Function to check if URL path name is a forbidden path.
function isForbiddenPath(decodedHref, match) {
const forbiddenPath = forbiddenPaths[match];
if (!forbiddenPath) return false;
const isWin32 = os.platform() === "win32";
const decodedHrefLower = isWin32 ? decodedHref.toLowerCase() : null;
if (typeof forbiddenPath === "string") {
return (
decodedHref === forbiddenPath ||
(os.platform() === "win32" &&
decodedHref.toLowerCase() === forbiddenPath.toLowerCase())
);
return isWin32
? decodedHrefLower === forbiddenPath.toLowerCase()
: decodedHref === forbiddenPath;
}
if (typeof forbiddenPath === "object") {
return forbiddenPath.some((forbiddenPathSingle) => {
return (
decodedHref === forbiddenPathSingle ||
(os.platform() === "win32" &&
decodedHref.toLowerCase() === forbiddenPathSingle.toLowerCase())
);
});
return isWin32
? forbiddenPath.some((path) => decodedHrefLower === path.toLowerCase())
: forbiddenPath.includes(decodedHref);
}
return false;
}
// Function to check if URL path name is index of one of defined forbidden paths.
function isIndexOfForbiddenPath(decodedHref, match) {
const forbiddenPath = forbiddenPaths[match];
if (!forbiddenPath) return false;
const isWin32 = os.platform() === "win32";
const decodedHrefLower = isWin32 ? decodedHref.toLowerCase() : null;
if (typeof forbiddenPath === "string") {
return (
decodedHref === forbiddenPath ||
decodedHref.indexOf(forbiddenPath + "/") === 0 ||
(os.platform() === "win32" &&
(decodedHref.toLowerCase() === forbiddenPath.toLowerCase() ||
decodedHref
.toLowerCase()
.indexOf(forbiddenPath.toLowerCase() + "/") === 0))
);
const forbiddenPathLower = isWin32 ? forbiddenPath.toLowerCase() : null;
return isWin32
? decodedHrefLower.indexOf(forbiddenPathLower) == 0
: decodedHref.indexOf(forbiddenPath) == 0;
}
if (typeof forbiddenPath === "object") {
return forbiddenPath.some((forbiddenPathSingle) => {
return (
decodedHref === forbiddenPathSingle ||
decodedHref.indexOf(forbiddenPathSingle + "/") === 0 ||
(os.platform() === "win32" &&
(decodedHref.toLowerCase() === forbiddenPathSingle.toLowerCase() ||
decodedHref
.toLowerCase()
.indexOf(forbiddenPathSingle.toLowerCase() + "/") === 0))
);
});
return isWin32
? forbiddenPath.some(
(path) => decodedHrefLower.indexOf(path.toLowerCase()) == 0,
)
: forbiddenPath.some((path) => decodedHref.indexOf(path) == 0);
}
return false;
}

View file

@ -13,6 +13,7 @@ module.exports = (legacyMod) => {
let middleware = (req, res, logFacilities, config, next) => {
let ext = req.parsedURL.pathname.match(/[^/]\.([^.]+)$/);
if (!ext) ext = "";
else ext = ext[1].toLowerCase();
// Function to parse incoming POST data from the request
const parsePostData = (options, callback) => {

View file

@ -1,17 +1,17 @@
function matchHostname(hostname, reqHostname) {
if (typeof hostname == "undefined" || hostname == "*") {
if (typeof hostname === "undefined" || hostname === "*") {
return true;
} else if (reqHostname && hostname.indexOf("*.") == 0 && hostname != "*.") {
} else if (reqHostname && hostname.indexOf("*.") == 0 && hostname !== "*.") {
const hostnamesRoot = hostname.substring(2);
if (
reqHostname == hostnamesRoot ||
reqHostname === hostnamesRoot ||
(reqHostname.length > hostnamesRoot.length &&
reqHostname.indexOf("." + hostnamesRoot) ==
reqHostname.indexOf("." + hostnamesRoot) ===
reqHostname.length - hostnamesRoot.length - 1)
) {
return true;
}
} else if (reqHostname && reqHostname == hostname) {
} else if (reqHostname && reqHostname === hostname) {
return true;
}
return false;

View file

@ -1,26 +1,25 @@
function sizify(bytes, addI) {
if (bytes == 0) return "0";
if (bytes === 0) return "0";
if (bytes < 0) bytes = -bytes;
const prefixes = ["", "K", "M", "G", "T", "P", "E", "Z", "Y", "R", "Q"];
let prefixIndex = Math.floor(
Math.log2 ? Math.log2(bytes) / 10 : Math.log(bytes) / (Math.log(2) * 10),
const prefixIndex = Math.min(
Math.floor(Math.log2(bytes) / 10),
prefixes.length - 1,
);
if (prefixIndex >= prefixes.length - 1) prefixIndex = prefixes.length - 1;
let prefixIndexTranslated = Math.pow(2, 10 * prefixIndex);
let decimalPoints =
2 -
Math.floor(
Math.log10
? Math.log10(bytes / prefixIndexTranslated)
: Math.log(bytes / prefixIndexTranslated) / Math.log(10),
);
if (decimalPoints < 0) decimalPoints = 0;
return (
const prefixIndexTranslated = Math.pow(2, 10 * prefixIndex);
const decimalPoints = Math.max(
2 - Math.floor(Math.log10(bytes / prefixIndexTranslated)),
0,
);
const size =
Math.ceil((bytes / prefixIndexTranslated) * Math.pow(10, decimalPoints)) /
Math.pow(10, decimalPoints) +
prefixes[prefixIndex] +
(prefixIndex > 0 && addI ? "i" : "")
);
Math.pow(10, decimalPoints);
const prefix = prefixes[prefixIndex];
const suffix = prefixIndex > 0 && addI ? "i" : "";
return size + prefix + suffix;
}
module.exports = sizify;

View file

@ -1,21 +1,16 @@
// Node.JS mojibake URL fixing function
function fixNodeMojibakeURL(string) {
var encoded = "";
//Encode URLs
Buffer.from(string, "latin1").forEach((value) => {
if (value > 127) {
encoded +=
"%" + (value < 16 ? "0" : "") + value.toString(16).toUpperCase();
} else {
encoded += String.fromCodePoint(value);
}
});
//Upper case the URL encodings
return encoded.replace(/%[0-9a-f-A-F]{2}/g, (match) => {
return match.toUpperCase();
});
return Buffer.from(string, "latin1")
.reduce((result, value) => {
if (value > 127) {
result +=
"%" + (value < 16 ? "0" : "") + value.toString(16).toUpperCase();
} else {
result += String.fromCharCode(value);
}
return result;
}, "")
.replace(/%[0-9a-f]{2}/gi, (match) => match.toUpperCase());
}
module.exports = fixNodeMojibakeURL;

View file

@ -1,6 +1,6 @@
const url = require("url");
// SVR.JS URL parser function (needed only for SVR.JS 2.x and 3.x mods)
// SVR.JS URL parser function (compatible with legacy Node.JS URL parsing function)
function parseURL(uri, prepend) {
// Replace newline characters with its respective URL encodings
uri = uri.replace(/\r/g, "%0D").replace(/\n/g, "%0A");
@ -13,7 +13,7 @@ function parseURL(uri, prepend) {
// Parse the URL using regular expression
let parsedURI = uri.match(
/^(?:([^:]+:)(\/\/)?)?(?:([^@/?#*]+)@)?([^:/?#*]+|\[[^*]\/]\])?(?::([0-9]+))?(\*|\/[^?#]*)?(\?[^#]*)?(#[\S\s]*)?/,
/^(?:([^:]+:)(\/\/)?)?(?:([^@/?#*]+)@)?([^:/?#*]+|\[[^*\]/]\])?(?::([0-9]+))?(\*|\/[^?#]*)?(\?[^#]*)?(#[\S\s]*)?/,
);
// Match 1: protocol
// Match 2: slashes after protocol

View file

@ -1,43 +1,50 @@
// SVR.JS path sanitizer function
function sanitizeURL(resource, allowDoubleSlashes) {
if (resource == "*" || resource == "") return resource;
if (resource === "*" || resource === "") return resource;
// Remove null characters
resource = resource.replace(/%00|\0/g, "");
// Check if URL is malformed (e.g. %c0%af or %u002f or simply %as)
if (resource.match(/%(?:c[01]|f[ef]|(?![0-9a-f]{2}).{2}|.{0,1}$)/i))
throw new URIError("URI malformed");
// Decode URL-encoded characters while preserving certain characters
resource = resource.replace(/%([0-9a-f]{2})/gi, (match, hex) => {
const decodedChar = String.fromCharCode(parseInt(hex, 16));
return /(?!["<>^`{|}?#%])[!-~]/.test(decodedChar) ? decodedChar : "%" + hex;
return /[!$&-;=@-\]_a-z~]/.test(decodedChar) ? decodedChar : match;
});
// Encode certain characters
resource = resource.replace(/[<>^`{|}]]/g, (character) => {
resource = resource.replace(/[<>^`{|}]/g, (character) => {
const charCode = character.charCodeAt(0);
return (
"%" + (charCode < 16 ? "0" : "") + charCode.toString(16).toUpperCase()
);
});
let sanitizedResource = resource;
// Ensure the resource starts with a slash
if (resource[0] != "/") sanitizedResource = "/" + sanitizedResource;
if (resource[0] !== "/") resource = "/" + resource;
// Convert backslashes to slashes and handle duplicate slashes
sanitizedResource = sanitizedResource
resource = resource
.replace(/\\/g, "/")
.replace(allowDoubleSlashes ? /\/{3,}/g : /\/+/g, "/");
// Handle relative navigation (e.g., "/./", "/../", "../", "./"), also remove trailing dots in paths
sanitizedResource = sanitizedResource
// Handle relative navigation (e.g., "/./", "/../", "../", "./") and remove trailing dots in paths
resource = resource
.replace(/\/\.(?:\.{2,})?(?=\/|$)/g, "")
.replace(/([^./])\.+(?=\/|$)/g, "$1");
while (sanitizedResource.match(/\/(?!\.\.\/)[^/]+\/\.\.(?=\/|$)/)) {
sanitizedResource = sanitizedResource.replace(
/\/(?!\.\.\/)[^/]+\/\.\.(?=\/|$)/g,
"",
);
// Remove remaining "../"
while (resource.match(/\/(?!\.\.\/)[^/]+\/\.\.(?=\/|$)/)) {
resource = resource.replace(/\/(?!\.\.\/)[^/]+\/\.\.(?=\/|$)/g, "");
}
sanitizedResource = sanitizedResource.replace(/\/\.\.(?=\/|$)/g, "");
if (sanitizedResource.length == 0) return "/";
else return sanitizedResource;
resource = resource.replace(/\/\.\.(?=\/|$)/g, "");
// If the result has length of 0, return "/", else return the sanitized URL
if (resource.length == 0) return "/";
else return resource;
}
module.exports = sanitizeURL;

View file

@ -1,9 +1,11 @@
{
"version": "4.0.0-beta1",
"version": "4.0.0-beta2",
"name": "SVR.JS",
"documentationURL": "https://svrjs.org/docs/tentative",
"statisticsServerCollectEndpoint": "https://statistics.svrjs.org/collect.svr",
"changes": [
"First SVR.JS 4.0.0 beta version"
"Fixed the bug with \"ext\" variable for .tar.gz mods.",
"Fixed the regular expression in the URL parser.",
"Optimized many functions"
]
}