koa-content-filter
Version:
A simple but powerful content-filter. Also provides protection against NoSQL (like MongoDB) injection attacks on Node.js
93 lines (88 loc) • 3.92 kB
JavaScript
module.exports = function filter(options) {
options = options || {};
var checkNames = options.checkNames || true;
var typeList = options.typeList || ['object', 'function', 'string'];
var urlBlackList = options.urlBlackList || ['$', '{'];
var bodyBlackList = options.bodyBlackList || ['$'];
var methodList = options.methodList || ['GET', 'POST', 'PUT', 'DELETE'];
var urlMessage = options.urlMessage || 'A forbidden expression has been found in URL: ';
var bodyMessage = options.bodyMessage || 'A forbidden expression has been found in form data: ';
var appendFound = options.appendFound || true;
var caseSensitive = (options.caseSensitive === false) ? false : true;
var dispatchToErrorHandler = (options.dispatchToErrorHandler === true) ? true : false;
var errorStatus = 403;
var errorCode = "FORBIDDEN_CONTENT";
return function filter(ctx, next) {
/* Only examine the valid methodList */
if (methodList.indexOf(ctx.request.method) === -1) {
return next();
}
var found = null;
/* Examining the URL */
if (caseSensitive) {
for (var i = 0; i < urlBlackList.length; i++) {
/* The URL in the ctx.request might be handled by using 'ctx.req.params.id' for 'address/users/:id' by a programmer.
Because of this don't use ctx.req.query object instead of ctx.originalUrl value */
if (ctx.originalUrl.indexOf(urlBlackList[i]) !== -1) {
found = urlBlackList[i];
break;
}
}
} else {
/* If caseSensitive is `false` convert the originalURL value and bodyBlackList items into lowercase strings then examine them */
var url = ctx.originalUrl.toLowerCase();
for (var i = 0; i < urlBlackList.length; i++) {
if (url.indexOf(urlBlackList[i].toLowerCase()) !== -1) {
found = urlBlackList[i];
break;
}
}
}
if (found) {
if (dispatchToErrorHandler) {
throw new Error({ status: errorStatus, code: errorCode, message: urlMessage + (appendFound ? found : "") });
} else {
return ctx.response.body = { status: errorStatus, code: errorCode, message: urlMessage + (appendFound ? found : "") };
}
}
/* Examining the ctx.request.body object If there is a ctx.request.body object it must be checked */
if (ctx.request.body && Object.keys(ctx.request.body).length) {
// // hrstart is used for to calculate the elapsed time
// // https://ponsenodejs.org/api/process.html#process_process_hrtime
// var hrstart = process.hrtime()
var str = JSON.stringify(ctx.request.body);
/* If caseSensitive is `true` search for bodyBlackList items in combined body string */
if (caseSensitive) {
for (var i = 0; i < bodyBlackList.length; i++) {
if (str.indexOf(bodyBlackList[i]) !== -1) {
found = bodyBlackList[i];
break;
}
}
} else {
/* If caseSensitive is `false` convert the string and bodyBlackList items into lowercase strings then examine them */
str = str.toLowerCase();
for (var i = 0; i < bodyBlackList.length; i++) {
if (str.indexOf(bodyBlackList[i].toLowerCase()) !== -1) {
found = bodyBlackList[i];
break;
}
}
}
// // hrend is used for to calculate the elapsed time
// var hrend = process.hrtime(hrstart)
// console.log('Execution time (hr): %ds %dms', hrend[0], hrend[1]/1000000)
if (found) {
if (dispatchToErrorHandler) {
throw new Error({ status: errorStatus, code: errorCode, message: bodyMessage + (appendFound ? found : "") });
} else {
return ctx.response.body = { status: errorStatus, code: errorCode, message: bodyMessage + (appendFound ? found : "") };
}
} else {
return next();
}
} else {
return next();
}
};
};