lyfeyaj/poplar

View on GitHub
lib/sanitizer.js

Summary

Maintainability
A
2 hrs
Test Coverage
/*!
 * Expose `Sanitize`.
 */
module.exports = Sanitize;

/*!
 * Module dependencies.
 */
var _ = require('lodash');
var __validator__ = require('validator');
var xss = require('xss');
var debug = require('debug')('poplar:sanitizer');

var Helper = require('./helper');

/*!
 * @class A collection of sanitizers
 */
function Sanitizer() {}

// sanitizers from `__validator__`
[
  'blacklist',
  'escape',
  'ltrim',
  'normalizeEmail',
  'rtrim',
  'stripLow',
  'toBoolean',
  'toDate',
  'toFloat',
  'toInt',
  'toString',
  'trim',
  'whitelist'
].forEach(function(name) {
  if (typeof __validator__[name] === 'function') {
    Sanitizer[name] = function() {
      return __validator__[name].apply(__validator__, arguments);
    };
  }
});

// Add xss sanitizer
Sanitizer.xss = xss;

/**
 * @class
 * Sanitize all params with its corresponding sanitizer
 *
 * Below sanitizers are mostly from chriso's [validator.js](https://github.com/chriso/validator.js)
 *
 * All built-in sanitizers are listed below:

 - **blacklist(input, chars)** - remove characters that appear in the blacklist. The characters are used in a RegExp and so you will need to escape some chars, e.g. blacklist(input, '\\[\\]').
 - **escape(input)** - replace `<`, `>`, `&`, `'`, `"` and `/` with HTML entities.
 - **ltrim(input [, chars])** - trim characters from the left-side of the input.
 - **normalizeEmail(email [, options])** - canonicalize an email address. `options` is an object which defaults to `{ lowercase: true }`. With `lowercase` set to `true`, the local part of the email address is lowercased for all domains; the hostname is always lowercased and the local part of the email address is always lowercased for hosts that are known to be case-insensitive (currently only GMail). Normalization follows special rules for known providers: currently, GMail addresses have dots removed in the local part and are stripped of tags (e.g. `some.one+tag@gmail.com` becomes `someone@gmail.com`) and all `@googlemail.com` addresses are normalized to `@gmail.com`.
 - **rtrim(input [, chars])** - trim characters from the right-side of the input.
 - **stripLow(input [, keep_new_lines])** - remove characters with a numerical value < 32 and 127, mostly control characters. If `keep_new_lines` is `true`, newline characters are preserved (`\n` and `\r`, hex `0xA` and `0xD`). Unicode-safe in JavaScript.
 - **toBoolean(input [, strict])** - convert the input to a boolean. Everything except for `'0'`, `'false'` and `''` returns `true`. In strict mode only `'1'` and `'true'` return `true`.
 - **toDate(input)** - convert the input to a date, or `null` if the input is not a date.
 - **toFloat(input)** - convert the input to a float, or `NaN` if the input is not a float.
 - **toInt(input [, radix])** - convert the input to an integer, or `NaN` if the input is not an integer.
 - **toString(input)** - convert the input to a string.
 - **trim(input [, chars])** - trim characters (whitespace by default) from both sides of the input.
 - **whitelist(input, chars)** - remove characters that do not appear in the whitelist. The characters are used in a RegExp and so you will need to escape some chars, e.g. whitelist(input, '\\[\\]').
 - **xss(input, options)** - xss sanitization
 *
 * @param {Object} params
 * Parameters about to sanitizing
 *
 * Example:
 *
 * ``` javascript
 * {
 *  name: 'Felix Liu',
 *  age: 25,
 *  email: 'lyfeyaj@gmail.com'
 * }
 * ```
 *
 * @param {Object} accepts
 * Sanitizers that apply to parameters
 *
 * Example:
 *
 * ``` javascript
 * [
 *   { arg: 'name', sanitizers: { trim: true, xss: true } },
 *   { arg: 'age', sanitizers: { toInt: true }
 *   { arg: 'email', sanitizers: { trim: true }
 * ]
 * ```
 */
function Sanitize(params, accepts) {
  params = params || {};
  accepts = accepts || [];

  _.each(accepts, function(accept) {
    var name = accept.arg;
    var val = params[name];

    var sanitizers = _.extend({}, accept.sanitizes);
    if (sanitizers && _.isPlainObject(sanitizers) && !Helper.isEmpty(val)) {
      _.each(sanitizers, function(sanitizerOpts, sanitizerName) {

        // if sanitizer is a custom function, then execute it
        // else find coorespond sanitizer in built in Sanitizer
        if (_.isFunction(sanitizerOpts)) {
          try {
            val = sanitizerOpts(val, params);
          } catch (e) {
            debug('Error: \'%s\' when calling function \'%s\'', e.message, sanitizerName);
          }
        } else {

          if (!sanitizerOpts) { return; }
          var sanitizer = Sanitizer[sanitizerName];
          var args = [val];
          if (!_.isBoolean(sanitizerOpts)) {
            args.push(sanitizerOpts);
          }
          args = _.flatten(args);

          if (sanitizer && _.isFunction(sanitizer)) {
            try {
              val = sanitizer.apply(Sanitizer, args);
            } catch (e) {
              debug('Error: \'%s\' when calling function \'%s\'', e.message, sanitizerName);
            }
          } else {
            debug('Sanitizer \'%s\' is not defined', sanitizerName);
          }
        }
      });

      params[name] = val;
    }
  });
  return params;
}

/**
 * Port to add new sanitizer func without violate __validator__
 *
 * Example:
 *
 * ``` javascript
 * Sanitize.extend('removeAllBlank', function(input) {
 *   return (String(input) || '').replace(/\ /g, '');
 * });
 * ```
 */
Sanitize.extend = function (name, fn) {
  Sanitizer[name] = function() {
    return fn.apply(Sanitizer, arguments);
  };
};

/*!
 * For test usage, get method from Sanitizer
 *
 * Example:
 *
 * ``` javascript
 * Sanitize.method('blacklist');
 * ```
 */
Sanitize.method = function(name) {
  return Sanitizer[name];
};