123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- var validator = require('validator');
- var _ = require('lodash');
- var utils = require('./utils');
- const { checkSchema, validationResult } = require('../check');
- const chainCreator = require('./chain-creator');
- const getCustomMethods = require('./custom-methods');
- const contextRunner = require('./legacy-runner');
- // validators and sanitizers not prefixed with is/to
- var additionalSanitizers = ['trim', 'ltrim', 'rtrim', 'escape', 'unescape', 'stripLow', 'whitelist', 'blacklist', 'normalizeEmail'];
- var allLocations = ['params', 'query', 'body', 'headers', 'cookies'];
- /**
- * Adds validation methods to request object via express middleware
- *
- * @method expressValidator
- * @param {object} options
- * @return {function} middleware
- */
- var expressValidator = function(options) {
- options = options || {};
- var defaults = {
- customValidators: {},
- customSanitizers: {},
- errorFormatter: function(param, msg, value, location) {
- return {
- location,
- param: param,
- msg: msg,
- value: value
- };
- }
- };
- _.defaults(options, defaults);
- /**
- * Initializes a sanitizer
- *
- * @class
- * @param {(string|string[])} param path to property to sanitize
- * @param {[type]} req request to sanitize
- * @param {[string]} locations request property to find value
- */
- function Sanitizer(param, req, locations) {
- this.values = locations.map(function(location) {
- return _.get(req[location], param);
- });
- this.req = req;
- this.param = param;
- this.locations = locations;
- utils.mapAndExtend(
- getCustomMethods(req, options).sanitizers,
- Sanitizer.prototype,
- utils.makeSanitizer
- );
- return this;
- }
- /**
- * validate an object using a schema, using following format:
- *
- * {
- * paramName: {
- * validatorName: true,
- * validator2Name: true
- * }
- * }
- *
- * Pass options or a custom error message:
- *
- * {
- * paramName: {
- * validatorName: {
- * options: ['', ''],
- * errorMessage: 'An Error Message'
- * }
- * }
- * }
- *
- * @method validateSchema
- * @param {Object} schema schema of validations
- * @param {Request} req request to attach validation errors
- * @param {string} loc request property to find value (body, params, query, etc.)
- * @return {object[]} array of errors
- */
- function validateSchema(schema, req, loc, contexts) {
- checkSchema(schema, loc, (field, locations, message) => chainCreator(
- field,
- locations,
- message,
- contexts,
- getCustomMethods(req, options)
- ));
- }
- /**
- * Error formatter delegator to the legacy format
- * @param {*} error
- */
- function errorFormatter({ param, msg, value, location }) {
- return options.errorFormatter(param, msg, value, location);
- }
- // _.set sanitizers as prototype methods on corresponding chains
- _.forEach(validator, function(method, methodName) {
- if (methodName.match(/^to/) || _.includes(additionalSanitizers, methodName)) {
- Sanitizer.prototype[methodName] = utils.makeSanitizer(methodName, validator);
- }
- });
- utils.mapAndExtend(options.customSanitizers, Sanitizer.prototype, utils.makeSanitizer);
- return function(req, res, next) {
- const contexts = [];
- const runContexts = () => contextRunner(contexts, req);
- var locations = ['body', 'params', 'query', 'cookies'];
- // Extend existing validators. Fixes bug #341
- const customMethods = getCustomMethods(req, options);
- req._validationErrors = [];
- req._asyncValidationErrors = [];
- req.validationErrors = function(mapped) {
- runContexts();
- var result = validationResult(req).formatWith(errorFormatter);
- if (result.isEmpty()) {
- return false;
- }
- return mapped ? result.mapped() : result.array();
- };
- req.asyncValidationErrors = function(mapped) {
- runContexts();
- return Promise.all(req._asyncValidationErrors).then(() => {
- if (req._validationErrors.length > 0) {
- return Promise.reject(req.validationErrors(mapped, true));
- }
- return Promise.resolve();
- });
- };
- req.getValidationResult = function() {
- runContexts();
- return Promise.all(req._asyncValidationErrors).then(() => {
- return validationResult(req).formatWith(errorFormatter);
- });
- };
- locations.forEach(function(location) {
- /**
- * @name req.sanitizeQuery
- * @see sanitize
- * @param param
- */
- /**
- * @name req.sanitizeParams
- * @see sanitize
- * @param param
- */
- /**
- * @name req.sanitizeBody
- * @see sanitize
- * @param param
- */
- req['sanitize' + _.capitalize(location)] = function(param) {
- return new Sanitizer(param, req, [location]);
- };
- });
- req.sanitizeHeaders = function(param) {
- if (param === 'referrer') {
- param = 'referer';
- }
- return new Sanitizer(param.toLowerCase(), req, ['headers']);
- };
- req.sanitize = function(param) {
- return new Sanitizer(param, req, locations);
- };
- locations.forEach(function(location) {
- /**
- * @name req.checkQuery
- * @see check
- * @param param
- * @param [failMsg]
- */
- /**
- * @name req.checkParams
- * @see check
- * @param param
- * @param [failMsg]
- */
- /**
- * @name req.checkBody
- * @see check
- * @param param
- * @param [failMsg]
- */
- /**
- * @name req.checkCookies
- * @see check
- * @param param
- * @param [failMsg]
- */
- req['check' + _.capitalize(location)] = function(param, failMsg) {
- if (_.isPlainObject(param)) {
- return validateSchema(param, req, [location], contexts);
- }
- return chainCreator(param, location, failMsg, contexts, customMethods);
- };
- });
- req.checkHeaders = function(param, failMsg) {
- if (_.isPlainObject(param)) {
- return validateSchema(param, req, ['headers'], contexts);
- }
- if (param === 'referrer') {
- param = 'referer';
- }
- return chainCreator(param.toLowerCase(), 'headers', failMsg, contexts, customMethods);
- };
- req.check = function(param, failMsg) {
- if (_.isPlainObject(param)) {
- return validateSchema(param, req, ['params', 'query', 'body', 'headers', 'cookies'], contexts);
- }
- return chainCreator(param, allLocations, failMsg, contexts, customMethods);
- };
- req.filter = req.sanitize;
- req.assert = req.check;
- req.validate = req.check;
- next();
- };
- };
- module.exports = expressValidator;
- module.exports.validator = validator;
- module.exports.utils = utils;
|