From af906c05a8fc021d02025e6c3bb30a638f80d713 Mon Sep 17 00:00:00 2001 From: Lily He Date: Fri, 28 Jul 2017 15:41:05 -0400 Subject: [PATCH] sanitizeAll validates req.query parameters, export async runAllChecks function --- sanitizer/sanitizeAll.js | 51 ++++++++- test/unit/sanitizer/sanitizeAll.js | 172 ++++++++++++++++++++--------- 2 files changed, 166 insertions(+), 57 deletions(-) diff --git a/sanitizer/sanitizeAll.js b/sanitizer/sanitizeAll.js index f6af363e..dafa1cb8 100644 --- a/sanitizer/sanitizeAll.js +++ b/sanitizer/sanitizeAll.js @@ -1,3 +1,5 @@ +const async = require('async'); + function sanitize( req, sanitizers, cb ){ // init an object to store clean (sanitized) input parameters if not initialized req.clean = req.clean || {}; @@ -8,10 +10,10 @@ function sanitize( req, sanitizers, cb ){ // source of input parameters // (in this case from the GET querystring params) - var params = req.query || {}; + const params = req.query || {}; for (var s in sanitizers) { - var sanity = sanitizers[s]( params, req.clean ); + var sanity = sanitizers[s].sanitize( params, req.clean ); // if errors occurred then set them // on the req object. @@ -25,10 +27,51 @@ function sanitize( req, sanitizers, cb ){ req.warnings = req.warnings.concat( sanity.warnings ); } } + return cb( undefined, req.clean ); +} + +// Adds to schemaKeys every acceptable parameter passed through API call +function checkParameters(req, sanitizers, cb) { + // source of input parameters + // (in this case from the GET querystring params) + const params = req.query || {}; + const goodParameters = {}; + + for (var s in sanitizers) { - // @todo remove these args, they do not need to be passed out + // checks if there is a function that returns valid params + if (typeof sanitizers[s].expected === 'function'){ + /** func returns {array} ex: [{ name: 'text' }, { name: 'parsed_text' }] */ + for (let t in sanitizers[s].expected()) { + /** {object} prop */ + const prop = sanitizers[s].expected()[t]; + if (prop.hasOwnProperty('name')){ + // adds name of valid parameter + goodParameters[prop.name] = prop.name; + } + } + } + } + // If there are any unexpected parameters, add a warning to messages + for (let p in params) { + if (!goodParameters.hasOwnProperty(p)){ + req.warnings = req.warnings.concat('Invalid Parameter: ' + p); + } + } return cb( undefined, req.clean ); } +// runs both sanitize and checkParameters functions in async parallel +function runAllChecks (req, sanitizers, cb) { + async.parallel([ + sanitize.bind(null, req, sanitizers), + checkParameters.bind(null, req, sanitizers) + ], cb); +} + // export function -module.exports = sanitize; +module.exports = { + sanitize: sanitize, + checkParameters: checkParameters, + runAllChecks: runAllChecks +}; diff --git a/test/unit/sanitizer/sanitizeAll.js b/test/unit/sanitizer/sanitizeAll.js index bcddba05..5e81aada 100644 --- a/test/unit/sanitizer/sanitizeAll.js +++ b/test/unit/sanitizer/sanitizeAll.js @@ -1,26 +1,31 @@ var sanitizeAll = require('../../../sanitizer/sanitizeAll'); +const Joi = require('joi'); module.exports.tests = {}; module.exports.tests.all = function(test, common) { test('req.clean/errors/warnings should be initialized when they are not', function(t) { var req = {}; - var sanitizers = [ - function() { - req.clean.a = 'first sanitizer'; - return { - errors: ['error 1', 'error 2'], - warnings: ['warning 1', 'warning 2'] - }; + var sanitizers = { + 'first': { + sanitize: function(){ + req.clean.a = 'first sanitizer'; + return { + errors: ['error 1', 'error 2'], + warnings: ['warning 1', 'warning 2'] + }; + } }, - function() { - req.clean.b = 'second sanitizer'; - return { - errors: ['error 3'], - warnings: ['warning 3'] - }; + 'second': { + sanitize: function() { + req.clean.b = 'second sanitizer'; + return { + errors: ['error 3'], + warnings: ['warning 3'] + }; + } } - ]; + }; var expected_req = { clean: { @@ -31,7 +36,7 @@ module.exports.tests.all = function(test, common) { warnings: ['warning 1', 'warning 2', 'warning 3'] }; - sanitizeAll(req, sanitizers, function(){ + sanitizeAll.sanitize(req, sanitizers, function (){ t.deepEquals(req, expected_req); t.end(); }); @@ -47,22 +52,26 @@ module.exports.tests.all = function(test, common) { warnings: ['pre-existing warning'] }; - var sanitizers = [ - function() { - req.clean.a = 'first sanitizer'; - return { - errors: ['error 1', 'error 2'], - warnings: ['warning 1', 'warning 2'] - }; + var sanitizers = { + 'first': { + sanitize: function(){ + req.clean.a = 'first sanitizer'; + return { + errors: ['error 1', 'error 2'], + warnings: ['warning 1', 'warning 2'] + }; + } }, - function() { - req.clean.b = 'second sanitizer'; - return { - errors: ['error 3'], - warnings: ['warning 3'] - }; + 'second': { + sanitize: function() { + req.clean.b = 'second sanitizer'; + return { + errors: ['error 3'], + warnings: ['warning 3'] + }; + } } - ]; + }; var expected_req = { clean: { @@ -74,7 +83,7 @@ module.exports.tests.all = function(test, common) { warnings: ['pre-existing warning', 'warning 1', 'warning 2', 'warning 3'] }; - sanitizeAll(req, sanitizers, function(){ + sanitizeAll.sanitize(req, sanitizers, function () { t.deepEquals(req, expected_req); t.end(); }); @@ -84,33 +93,35 @@ module.exports.tests.all = function(test, common) { test('req.query should be passed to individual sanitizers when available', function(t) { var req = { query: { - value: 'query value' + value: 'query' } }; - var sanitizers = [ - function(params) { - req.clean.query = params; - return { - errors: [], - warnings: [] - }; + var sanitizers = { + 'first': { + sanitize: function(params) { + req.clean.query = params; + return { + errors: [], + warnings: [] + }; + } } - ]; + }; var expected_req = { query: { - value: 'query value' + value: 'query' }, clean: { query: { - value: 'query value' + value: 'query' } }, errors: [], warnings: [] }; - sanitizeAll(req, sanitizers, function(){ + sanitizeAll.sanitize(req, sanitizers, function () { t.deepEquals(req, expected_req); t.end(); }); @@ -119,18 +130,20 @@ module.exports.tests.all = function(test, common) { test('an empty object should be passed to individual sanitizers when req.query is unavailable', function(t) { var req = {}; - var sanitizers = [ - function(params) { - if (Object.keys(params).length === 0) { - req.clean.empty_object_was_passed = true; + var sanitizers = { + 'first': { + sanitize: function(params) { + if (Object.keys(params).length === 0) { + req.clean.empty_object_was_passed = true; + } + + return { + errors: [], + warnings: [] + }; } - - return { - errors: [], - warnings: [] - }; } - ]; + }; var expected_req = { clean: { @@ -140,13 +153,66 @@ module.exports.tests.all = function(test, common) { warnings: [] }; - sanitizeAll(req, sanitizers, function(){ + sanitizeAll.sanitize(req, sanitizers, function () { t.deepEquals(req, expected_req); t.end(); }); }); + test('unexpected parameters should throw warning', function(t) { + var req = { + query: { + unknown_value: 'query value' + }, + errors: [], + warnings: [] + }; + var sanitizers = { + 'first': { + expected: function _expected () { + // add value as a valid parameter for joi + return [{ + name: 'value' + }]; + } + } + }; + + sanitizeAll.checkParameters(req, sanitizers, function () { + t.equals(req.errors.length, 0); + t.deepEquals(req.warnings[0], 'Invalid Parameter: unknown_value'); + t.end(); + }); + + }); + + test('expected parameters should not throw warning', function(t) { + var req = { + query: { + value: 'query value' + }, + errors: [], + warnings: [] + }; + var sanitizers = { + 'first': { + expected: function _expected () { + // add value as a valid parameter for joi + return [{ + name: 'value' + }]; + } + } + }; + + sanitizeAll.checkParameters(req, sanitizers, function () { + t.equals(req.errors.length, 0); + t.equals(req.warnings.length, 0); + t.end(); + }); + }); + }; module.exports.all = function (tape, common) {