forked from svrjs/svrjs
Add URL rewriting middleware, and lint out some other middleware.
This commit is contained in:
parent
a49dba73fe
commit
2481e3ed58
4 changed files with 200 additions and 10 deletions
|
@ -106,7 +106,8 @@ let middleware = [
|
|||
require("./middleware/core.js"),
|
||||
require("./middleware/urlSanitizer.js"),
|
||||
require("./middleware/redirects.js"),
|
||||
require("./middleware/webRootPostfixes.js")
|
||||
require("./middleware/webRootPostfixes.js"),
|
||||
require("./middleware/rewriteURL.js")
|
||||
];
|
||||
|
||||
function addMiddleware(mw) {
|
||||
|
|
184
src/middleware/rewriteURL.js
Normal file
184
src/middleware/rewriteURL.js
Normal 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();
|
||||
});
|
||||
};
|
|
@ -2,15 +2,16 @@ const sanitizeURL = require("../utils/urlSanitizer.js");
|
|||
const url = require("url");
|
||||
|
||||
module.exports = (req, res, logFacilities, config, next) => {
|
||||
let href = req.parsedURL.pathname + req.parsedURL.search;
|
||||
|
||||
// Sanitize URL
|
||||
let sanitizedHref = sanitizeURL(href, config.allowDoubleSlashes);
|
||||
let sanitizedHref = sanitizeURL(
|
||||
req.parsedURL.pathname,
|
||||
config.allowDoubleSlashes,
|
||||
);
|
||||
let preparedReqUrl =
|
||||
req.parsedURL.pathname + req.parsedURL.search + req.parsedURL.hash;
|
||||
|
||||
// Check if URL is "dirty"
|
||||
if (href != sanitizedHref && !req.isProxy) {
|
||||
if (req.parsedURL.pathname != sanitizedHref && !req.isProxy) {
|
||||
let sanitizedURL = new url.Url();
|
||||
sanitizedURL.path = null;
|
||||
sanitizedURL.href = null;
|
||||
|
|
|
@ -95,20 +95,24 @@ module.exports = (req, res, logFacilities, config, next) => {
|
|||
return;
|
||||
}
|
||||
|
||||
let href = req.parsedURL.pathname + req.parsedURL.search;
|
||||
|
||||
var sHref = sanitizeURL(href, config.allowDoubleSlashes);
|
||||
var sHref = sanitizeURL(
|
||||
req.parsedURL.pathname,
|
||||
config.allowDoubleSlashes,
|
||||
);
|
||||
var preparedReqUrl2 =
|
||||
req.parsedURL.pathname + req.parsedURL.search + req.parsedURL.hash;
|
||||
|
||||
if (
|
||||
req.url != preparedReqUrl2 ||
|
||||
sHref != href.replace(/\/\.(?=\/|$)/g, "/").replace(/\/+/g, "/")
|
||||
sHref !=
|
||||
req.parsedURL.pathname
|
||||
.replace(/\/\.(?=\/|$)/g, "/")
|
||||
.replace(/\/+/g, "/")
|
||||
) {
|
||||
res.error(403);
|
||||
logFacilities.errmessage("Content blocked.");
|
||||
return;
|
||||
} else if (sHref != href) {
|
||||
} else if (sHref != req.parsedURL.pathname) {
|
||||
var rewrittenAgainURL = new url.Url();
|
||||
rewrittenAgainURL.path = null;
|
||||
rewrittenAgainURL.href = null;
|
||||
|
|
Reference in a new issue