1
0
Fork 0
forked from svrjs/svrjs

Move parts of middleware that checks for forbidden paths to separate utils file, and add tests for them.

This commit is contained in:
Dorian Niemiec 2024-08-25 07:13:41 +02:00
parent 983133017f
commit 5fab894caf
4 changed files with 245 additions and 80 deletions

View file

@ -13,6 +13,7 @@ try {
}
process.dirname = __dirname;
process.filename = __filename;
// TODO: after implementing clustering in new SVR.JS
//process.singleThreaded = false;

View file

@ -1,81 +1,11 @@
const os = require("os");
const path = require("path");
// Function to get URL path for use in forbidden path adding.
function getInitializePath(to) {
const cwd = process.cwd();
if (os.platform() == "win32") {
to = to.replace(/\//g, "\\");
if (to[0] == "\\") to = cwd.split("\\")[0] + to;
}
const absoluteTo = path.isAbsolute(to)
? to
: process.dirname + (os.platform() == "win32" ? "\\" : "/") + to;
if (os.platform() == "win32" && cwd[0] != absoluteTo[0]) return "";
const relative = path.relative(cwd, absoluteTo);
if (os.platform() == "win32") {
return "/" + relative.replace(/\\/g, "/");
} else {
return "/" + relative;
}
}
// Function to check if URL path name is a forbidden path.
function isForbiddenPath(decodedHref, match) {
const forbiddenPath = forbiddenPaths[match];
if (!forbiddenPath) return false;
if (typeof forbiddenPath === "string") {
return (
decodedHref === forbiddenPath ||
(os.platform() === "win32" &&
decodedHref.toLowerCase() === forbiddenPath.toLowerCase())
);
}
if (typeof forbiddenPath === "object") {
return forbiddenPath.some(function (forbiddenPathSingle) {
return (
decodedHref === forbiddenPathSingle ||
(os.platform() === "win32" &&
decodedHref.toLowerCase() === forbiddenPathSingle.toLowerCase())
);
});
}
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;
if (typeof forbiddenPath === "string") {
return (
decodedHref === forbiddenPath ||
decodedHref.indexOf(forbiddenPath + "/") === 0 ||
(os.platform() === "win32" &&
(decodedHref.toLowerCase() === forbiddenPath.toLowerCase() ||
decodedHref
.toLowerCase()
.indexOf(forbiddenPath.toLowerCase() + "/") === 0))
);
}
if (typeof forbiddenPath === "object") {
return forbiddenPath.some(function (forbiddenPathSingle) {
return (
decodedHref === forbiddenPathSingle ||
decodedHref.indexOf(forbiddenPathSingle + "/") === 0 ||
(os.platform() === "win32" &&
(decodedHref.toLowerCase() === forbiddenPathSingle.toLowerCase() ||
decodedHref
.toLowerCase()
.indexOf(forbiddenPathSingle.toLowerCase() + "/") === 0))
);
});
}
return false;
}
// Set up forbidden paths
var forbiddenPaths = {};
// WARNING: This middleware is optimized for production SVR.JS, and may not work correctly for development SVR.JS.
// Use "npm run dev" to test SVR.JS web server itself.
const {
getInitializePath,
isForbiddenPath,
isIndexOfForbiddenPath,
forbiddenPaths,
} = require("../utils/forbiddenPaths.js");
forbiddenPaths.config = getInitializePath("./config.json");
forbiddenPaths.certificates = [];
@ -96,8 +26,8 @@ if (process.serverConfig.secure) {
forbiddenPaths.svrjs = getInitializePath(
"./" +
(process.dirname[process.dirname.length - 1] != "/"
? __filename.replace(process.dirname + "/", "")
: __filename.replace(process.dirname, "")),
? process.filename.replace(process.dirname + "/", "")
: process.filename.replace(process.dirname, "")),
);
forbiddenPaths.serverSideScripts = [];
if (process.serverConfig.useWebRootServerSideScript) {

View file

@ -0,0 +1,87 @@
const os = require("os");
const path = require("path");
// Function to get URL path for use in forbidden path adding.
function getInitializePath(to) {
const isWin32 = os.platform() == "win32";
const pathModS = isWin32 ? path.win32 : path.posix; // pathModS needed just for the test suite
const cwd = process.cwd();
if (isWin32) {
to = to.replace(/\//g, "\\");
if (to[0] == "\\") to = cwd.split("\\")[0] + to;
}
const absoluteTo = pathModS.isAbsolute(to)
? to
: process.dirname + (isWin32 ? "\\" : "/") + to;
if (isWin32 && cwd[0] != absoluteTo[0]) return "";
const relative = pathModS.relative(cwd, absoluteTo);
if (isWin32) {
return "/" + relative.replace(/\\/g, "/");
} else {
return "/" + relative;
}
}
// Function to check if URL path name is a forbidden path.
function isForbiddenPath(decodedHref, match) {
const forbiddenPath = forbiddenPaths[match];
if (!forbiddenPath) return false;
if (typeof forbiddenPath === "string") {
return (
decodedHref === forbiddenPath ||
(os.platform() === "win32" &&
decodedHref.toLowerCase() === forbiddenPath.toLowerCase())
);
}
if (typeof forbiddenPath === "object") {
return forbiddenPath.some(function (forbiddenPathSingle) {
return (
decodedHref === forbiddenPathSingle ||
(os.platform() === "win32" &&
decodedHref.toLowerCase() === forbiddenPathSingle.toLowerCase())
);
});
}
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;
if (typeof forbiddenPath === "string") {
return (
decodedHref === forbiddenPath ||
decodedHref.indexOf(forbiddenPath + "/") === 0 ||
(os.platform() === "win32" &&
(decodedHref.toLowerCase() === forbiddenPath.toLowerCase() ||
decodedHref
.toLowerCase()
.indexOf(forbiddenPath.toLowerCase() + "/") === 0))
);
}
if (typeof forbiddenPath === "object") {
return forbiddenPath.some(function (forbiddenPathSingle) {
return (
decodedHref === forbiddenPathSingle ||
decodedHref.indexOf(forbiddenPathSingle + "/") === 0 ||
(os.platform() === "win32" &&
(decodedHref.toLowerCase() === forbiddenPathSingle.toLowerCase() ||
decodedHref
.toLowerCase()
.indexOf(forbiddenPathSingle.toLowerCase() + "/") === 0))
);
});
}
return false;
}
// Set up forbidden paths
let forbiddenPaths = {};
module.exports = {
getInitializePath: getInitializePath,
isForbiddenPath: isForbiddenPath,
isIndexOfForbiddenPath: isIndexOfForbiddenPath,
forbiddenPaths: forbiddenPaths,
};

View file

@ -0,0 +1,147 @@
const {
getInitializePath,
isForbiddenPath,
isIndexOfForbiddenPath,
forbiddenPaths,
} = require("../../src/utils/forbiddenPaths");
const os = require("os");
const path = require("path");
jest.mock("os", () => ({
platform: jest.fn(),
}));
describe("Forbidden paths handling", () => {
beforeEach(() => {
os.platform.mockReset();
forbiddenPaths.config = getInitializePath("./config.json");
forbiddenPaths.serverSideScriptDirectories = [];
forbiddenPaths.serverSideScriptDirectories.push(
getInitializePath("./node_modules"),
);
forbiddenPaths.serverSideScriptDirectories.push(
getInitializePath("./mods"),
);
process.cwd = () => "/usr/lib/mocksvrjs";
process.dirname = "/usr/lib/mocksvrjs";
process.filename = "/usr/lib/mocksvrjs/svr.js";
});
describe("getInitializePath", () => {
test("should return the correct path on Unix", () => {
os.platform.mockReturnValue("linux");
expect(getInitializePath("./config.json")).toBe("/config.json");
});
test("should return the correct path on Windows", () => {
process.cwd = () => "C:\\mocksvrjs";
process.dirname = "C:\\mocksvrjs";
process.filename = "C:\\mocksvrjs\\svr.js";
os.platform.mockReturnValue("win32");
expect(getInitializePath("./config.json")).toBe("/config.json");
});
test("should handle absolute paths on Unix", () => {
os.platform.mockReturnValue("linux");
expect(getInitializePath("/absolute/path")).toBe(
"/../../../absolute/path",
);
});
test("should handle absolute paths on Windows", () => {
process.cwd = () => "C:\\mocksvrjs";
process.dirname = "C:\\mocksvrjs";
process.filename = "C:\\mocksvrjs\\svr.js";
os.platform.mockReturnValue("win32");
expect(getInitializePath("C:\\absolute\\path")).toBe("/../absolute/path");
});
test("should handle relative paths on Unix", () => {
os.platform.mockReturnValue("linux");
expect(getInitializePath("./relative/path")).toBe("/relative/path");
});
test("should handle relative paths on Windows", () => {
process.cwd = () => "C:\\mocksvrjs";
process.dirname = "C:\\mocksvrjs";
process.filename = "C:\\mocksvrjs\\svr.js";
os.platform.mockReturnValue("win32");
expect(getInitializePath("./relative\\path")).toBe("/relative/path");
});
});
describe("isForbiddenPath", () => {
test("should return true if the path is forbidden", () => {
os.platform.mockReturnValue("linux");
expect(isForbiddenPath("/config.json", "config")).toBe(true);
});
test("should return false if the path is not forbidden", () => {
os.platform.mockReturnValue("linux");
expect(isForbiddenPath("/notconfig.json", "config")).toBe(false);
});
test("should handle case insensitivity on Windows", () => {
process.cwd = () => "C:\\mocksvrjs";
process.dirname = "C:\\mocksvrjs";
process.filename = "C:\\mocksvrjs\\svr.js";
os.platform.mockReturnValue("win32");
expect(isForbiddenPath("/CONFIG.JSON", "config")).toBe(true);
});
test("should handle array of forbidden paths", () => {
os.platform.mockReturnValue("linux");
expect(
isForbiddenPath("/node_modules", "serverSideScriptDirectories"),
).toBe(true);
expect(isForbiddenPath("/mods", "serverSideScriptDirectories")).toBe(
true,
);
expect(
isForbiddenPath("/notforbidden", "serverSideScriptDirectories"),
).toBe(false);
});
});
describe("isIndexOfForbiddenPath", () => {
test("should return true if the path is an index of a forbidden path", () => {
os.platform.mockReturnValue("linux");
expect(isIndexOfForbiddenPath("/config.json", "config")).toBe(true);
expect(
isIndexOfForbiddenPath("/node_modules/", "serverSideScriptDirectories"),
).toBe(true);
});
test("should return false if the path is not an index of a forbidden path", () => {
os.platform.mockReturnValue("linux");
expect(isIndexOfForbiddenPath("/notconfig.json", "config")).toBe(false);
expect(
isIndexOfForbiddenPath("/notforbidden/", "serverSideScriptDirectories"),
).toBe(false);
});
test("should handle case insensitivity on Windows", () => {
process.cwd = () => "C:\\mocksvrjs";
process.dirname = "C:\\mocksvrjs";
process.filename = "C:\\mocksvrjs\\svr.js";
os.platform.mockReturnValue("win32");
expect(isIndexOfForbiddenPath("/CONFIG.JSON", "config")).toBe(true);
expect(
isIndexOfForbiddenPath("/NODE_MODULES/", "serverSideScriptDirectories"),
).toBe(true);
});
test("should handle array of forbidden paths", () => {
os.platform.mockReturnValue("linux");
expect(
isIndexOfForbiddenPath("/node_modules/", "serverSideScriptDirectories"),
).toBe(true);
expect(
isIndexOfForbiddenPath("/mods/", "serverSideScriptDirectories"),
).toBe(true);
expect(
isIndexOfForbiddenPath("/notforbidden/", "serverSideScriptDirectories"),
).toBe(false);
});
});
});