1
0
Fork 0
forked from svrjs/svrjs

Add URL rewriting middleware, and lint out some other middleware.

This commit is contained in:
Dorian Niemiec 2024-08-24 08:22:49 +02:00
parent a49dba73fe
commit 2481e3ed58
4 changed files with 200 additions and 10 deletions

View file

@ -106,7 +106,8 @@ let middleware = [
require("./middleware/core.js"), require("./middleware/core.js"),
require("./middleware/urlSanitizer.js"), require("./middleware/urlSanitizer.js"),
require("./middleware/redirects.js"), require("./middleware/redirects.js"),
require("./middleware/webRootPostfixes.js") require("./middleware/webRootPostfixes.js"),
require("./middleware/rewriteURL.js")
]; ];
function addMiddleware(mw) { function addMiddleware(mw) {

View file

@ -0,0 +1,184 @@
const fs = require("fs");
const url = require("url");
const createRegex = require("../utils/createRegex.js");
const ipMatch = require("../utils/ipMatch.js");
const sanitizeURL = require("../utils/urlSanitizer.js");
module.exports = (req, res, logFacilities, config, next) => {
try {
decodeURIComponent(req.parsedURL.pathname);
} catch (err) {
res.error(400);
}
const matchHostname = (hostname) => {
if (typeof hostname == "undefined" || hostname == "*") {
return true;
} else if (
req.headers.host &&
hostname.indexOf("*.") == 0 &&
hostname != "*."
) {
const hostnamesRoot = hostname.substring(2);
if (
req.headers.host == hostnamesRoot ||
(req.headers.host.length > hostnamesRoot.length &&
req.headers.host.indexOf("." + hostnamesRoot) ==
req.headers.host.length - hostnamesRoot.length - 1)
) {
return true;
}
} else if (req.headers.host && req.headers.host == hostname) {
return true;
}
return false;
};
// Handle URL rewriting
const rewriteURL = (address, map, callback, _fileState, _mapBegIndex) => {
let rewrittenURL = address;
let doCallback = true;
if (!req.isProxy) {
for (let i = _mapBegIndex ? _mapBegIndex : 0; i < map.length; i++) {
let mapEntry = map[i];
if (
req.parsedUrl.pathname != "/" &&
(mapEntry.isNotDirectory || mapEntry.isNotFile) &&
!_fileState
) {
fs.stat(
"." + decodeURIComponent(req.parsedUrl.pathname),
(err, stats) => {
var _fileState = 3;
if (err) {
_fileState = 3;
} else if (stats.isDirectory()) {
_fileState = 2;
} else if (stats.isFile()) {
_fileState = 1;
} else {
_fileState = 3;
}
rewriteURL(address, map, callback, _fileState, i);
},
);
doCallback = false;
break;
}
let tempRewrittenURL = rewrittenURL;
if (!mapEntry.allowDoubleSlashes) {
address = address.replace(/\/+/g, "/");
tempRewrittenURL = address;
}
if (
matchHostname(mapEntry.host) &&
ipMatch(
mapEntry.ip,
req.socket ? req.socket.localAddress : undefined,
) &&
address.match(createRegex(mapEntry.definingRegex)) &&
!(mapEntry.isNotDirectory && _fileState == 2) &&
!(mapEntry.isNotFile && _fileState == 1)
) {
rewrittenURL = tempRewrittenURL;
try {
mapEntry.replacements.forEach(function (replacement) {
rewrittenURL = rewrittenURL.replace(
createRegex(replacement.regex),
replacement.replacement,
);
});
if (mapEntry.append) rewrittenURL += mapEntry.append;
} catch (err) {
doCallback = false;
callback(err, null);
}
break;
}
}
}
if (doCallback) callback(null, rewrittenURL);
};
// Rewrite URLs
rewriteURL(req.url, config.rewriteMap, function (err, rewrittenURL) {
if (err) {
res.error(500, err);
return;
}
if (rewrittenURL != req.url) {
logFacilities.resmessage(
"URL rewritten: " + req.url + " => " + rewrittenURL,
);
req.url = rewrittenURL;
try {
req.parsedURL = new URL(
req.url,
"http" +
(req.socket.encrypted ? "s" : "") +
"://" +
(req.headers.host
? req.headers.host
: config.domain
? config.domain
: "unknown.invalid"),
);
} catch (err) {
res.error(400, err);
return;
}
var sHref = sanitizeURL(
req.parsedURL.pathname,
config.allowDoubleSlashes,
);
var preparedReqUrl2 =
req.parsedURL.pathname + req.parsedURL.search + req.parsedURL.hash;
if (
req.url != preparedReqUrl2 ||
sHref !=
req.parsedURL.pathname
.replace(/\/\.(?=\/|$)/g, "/")
.replace(/\/+/g, "/")
) {
res.error(403);
logFacilities.errmessage("Content blocked.");
return;
} else if (sHref != req.parsedURL.pathname) {
var rewrittenAgainURL = new url.Url();
rewrittenAgainURL.path = null;
rewrittenAgainURL.href = null;
rewrittenAgainURL.pathname = sHref;
rewrittenAgainURL.hostname = null;
rewrittenAgainURL.host = null;
rewrittenAgainURL.port = null;
rewrittenAgainURL.protocol = null;
rewrittenAgainURL.slashes = null;
rewrittenAgainURL = url.format(rewrittenAgainURL);
logFacilities.resmessage(
"URL sanitized: " + req.url + " => " + rewrittenAgainURL,
);
req.url = rewrittenAgainURL;
try {
req.parsedURL = new URL(
req.url,
"http" +
(req.socket.encrypted ? "s" : "") +
"://" +
(req.headers.host
? req.headers.host
: config.domain
? config.domain
: "unknown.invalid"),
);
} catch (err) {
res.error(400, err);
return;
}
}
}
next();
});
};

View file

@ -2,15 +2,16 @@ const sanitizeURL = require("../utils/urlSanitizer.js");
const url = require("url"); const url = require("url");
module.exports = (req, res, logFacilities, config, next) => { module.exports = (req, res, logFacilities, config, next) => {
let href = req.parsedURL.pathname + req.parsedURL.search;
// Sanitize URL // Sanitize URL
let sanitizedHref = sanitizeURL(href, config.allowDoubleSlashes); let sanitizedHref = sanitizeURL(
req.parsedURL.pathname,
config.allowDoubleSlashes,
);
let preparedReqUrl = let preparedReqUrl =
req.parsedURL.pathname + req.parsedURL.search + req.parsedURL.hash; req.parsedURL.pathname + req.parsedURL.search + req.parsedURL.hash;
// Check if URL is "dirty" // Check if URL is "dirty"
if (href != sanitizedHref && !req.isProxy) { if (req.parsedURL.pathname != sanitizedHref && !req.isProxy) {
let sanitizedURL = new url.Url(); let sanitizedURL = new url.Url();
sanitizedURL.path = null; sanitizedURL.path = null;
sanitizedURL.href = null; sanitizedURL.href = null;

View file

@ -95,20 +95,24 @@ module.exports = (req, res, logFacilities, config, next) => {
return; return;
} }
let href = req.parsedURL.pathname + req.parsedURL.search; var sHref = sanitizeURL(
req.parsedURL.pathname,
var sHref = sanitizeURL(href, config.allowDoubleSlashes); config.allowDoubleSlashes,
);
var preparedReqUrl2 = var preparedReqUrl2 =
req.parsedURL.pathname + req.parsedURL.search + req.parsedURL.hash; req.parsedURL.pathname + req.parsedURL.search + req.parsedURL.hash;
if ( if (
req.url != preparedReqUrl2 || req.url != preparedReqUrl2 ||
sHref != href.replace(/\/\.(?=\/|$)/g, "/").replace(/\/+/g, "/") sHref !=
req.parsedURL.pathname
.replace(/\/\.(?=\/|$)/g, "/")
.replace(/\/+/g, "/")
) { ) {
res.error(403); res.error(403);
logFacilities.errmessage("Content blocked."); logFacilities.errmessage("Content blocked.");
return; return;
} else if (sHref != href) { } else if (sHref != req.parsedURL.pathname) {
var rewrittenAgainURL = new url.Url(); var rewrittenAgainURL = new url.Url();
rewrittenAgainURL.path = null; rewrittenAgainURL.path = null;
rewrittenAgainURL.href = null; rewrittenAgainURL.href = null;