forked from svrjs/svrjs
Update to SVR.JS 4.0.0-beta2
This commit is contained in:
parent
827e1efd4e
commit
920d942016
9 changed files with 91 additions and 94 deletions
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
|
Reference in a new issue