UNPKG

@haxtheweb/haxcms-nodejs

Version:

HAXcms single and multisite nodejs server, api, and administration

245 lines (238 loc) 9.94 kB
"use strict"; function filter_var(input, filter, options) { // http://kevin.vanzonneveld.net // + original by: Brett Zamir (http://brett-zamir.me) // + improved by: Rafa?&#x201A; Kukawski (http://kukawski.pl) // - depends on: addslashes // - depends on: htmlspecialchars // - depends on: strip_tags // * example 1: filter_var('true', 'FILTER_VALIDATE_BOOLEAN'); // * returns 1: true function is(val, type) { if (val == null) { return type === "null"; } if (type === "primitive") { return val !== Object(val); } var actual = typeof val; if (actual === "object") { return { "[object Array]": "array", "[object RegExp]": "regex" }[Object.prototype.toString.call(val)] || "object"; } if (actual === "number") { if (isNaN(val)) { return type === "nan"; } if (!isFinite(val)) { return "inf"; } } return type === actual; } function str2regex(str) {} function isPrimitive(val) { return val !== Object(val); } var supportedFilters = { FILTER_VALIDATE_INT: 257, FILTER_VALIDATE_BOOLEAN: 258, FILTER_VALIDATE_FLOAT: 259, FILTER_VALIDATE_REGEXP: 272, FILTER_VALIDATE_URL: 273, FILTER_VALIDATE_EMAIL: 274, FILTER_VALIDATE_IP: 275, FILTER_SANITIZE_STRING: 513, FILTER_SANITIZE_STRIPPED: 513, FILTER_SANITIZE_ENCODED: 514, FILTER_SANITIZE_SPECIAL_CHARS: 515, FILTER_UNSAFE_RAW: 516, FILTER_DEFAULT: 516, FILTER_SANITIZE_EMAIL: 517, FILTER_SANITIZE_URL: 518, FILTER_SANITIZE_NUMBER_INT: 519, FILTER_SANITIZE_NUMBER_FLOAT: 520, FILTER_SANITIZE_MAGIC_QUOTES: 521, // TODO: doesn't exist on my server. Add constant value FILTER_SANITIZE_FULL_SPECIAL_CHARS: -1, FILTER_CALLBACK: 1024 }; var supportedFlags = { FILTER_FLAG_ALLOW_OCTAL: 1, FILTER_FLAG_ALLOW_HEX: 2, FILTER_FLAG_STRIP_LOW: 4, FILTER_FLAG_STRIP_HIGH: 8, FILTER_FLAG_ENCODE_LOW: 16, FILTER_FLAG_ENCODE_HIGH: 32, FILTER_FLAG_ENCODE_AMP: 64, FILTER_FLAG_NO_ENCODE_QUOTES: 128, FILTER_FLAG_ALLOW_FRACTION: 4096, FILTER_FLAG_ALLOW_THOUSAND: 8192, FILTER_FLAG_ALLOW_SCIENTIFIC: 16384, FILTER_FLAG_PATH_REQUIRED: 262144, FILTER_FLAG_QUERY_REQUIRED: 524288, FILTER_FLAG_IPV4: 1048576, FILTER_FLAG_IPV6: 2097152, FILTER_FLAG_NO_RES_RANGE: 4194304, FILTER_FLAG_NO_PRIV_RANGE: 8388608, FILTER_NULL_ON_FAILURE: 134217728 }; if (is(filter, "null")) { filter = supportedFilters.FILTER_DEFAULT; } else if (is(filter, "string")) { filter = supportedFilters[filter]; } var flags = 0; if (is(options, "number")) { flags = options; } else if (is(options, "string")) { flags = supportedFlags[options] || 0; } else if (is(options, "object") && is(options.flags, "number")) { flags = options.flags; } var opts = {}; if (is(options, "object")) { opts = options.options || {}; } // it looks like the FILTER_NULL_ON_FAILURE is used across all filters, not only FILTER_VALIDATE_BOOLEAN // thus the failure var var failure = flags & supportedFlags.FILTER_NULL_ON_FAILURE ? null : false; if (!is(filter, "number")) { // no numeric filter, return return failure; } // Shortcut for non-primitive values. All are failures if (!isPrimitive(input)) { return failure; } // if input is string, trim whitespace TODO: make a dependency on trim()? var data = is(input, "string") ? input.replace(/(^\s+)|(\s+$)/g, '') : input; switch (filter) { case supportedFilters.FILTER_VALIDATE_BOOLEAN: return /^(?:1|true|yes|on)$/i.test(data) || (/^(?:0|false|no|off)$/i.test(data) ? false : failure); case supportedFilters.FILTER_VALIDATE_INT: var numValue = +data; if (!/^(?:0|[+\-]?[1-9]\d*)$/.test(data)) { if (flags & supportedFlags.FILTER_FLAG_ALLOW_HEX && /^0x[\da-f]+$/i.test(data)) { numValue = parseInt(data, 16); } else if (flags & supportedFlags.FILTER_FLAG_ALLOW_OCTAL && /^0[0-7]+$/.test(data)) { numValue = parseInt(data, 8); } else { return failure; } } var minValue = is(opts.min_range, "number") ? opts.min_range : -Infinity; var maxValue = is(opts.max_range, "number") ? opts.max_range : Infinity; if (!is(numValue, "number") || numValue % 1 || numValue < minValue || numValue > maxValue) { return failure; } return numValue; case supportedFilters.FILTER_VALIDATE_REGEXP: if (is(options.regexp, "regex")) { // FIXME: we are passing pre-processed input data (trimmed data). // check whether PHP also passess trimmed input var matches = options.regexp(data); return matches ? matches[0] : failure; } // TODO: support passing regexes as strings "#regex#is" case supportedFilters.FILTER_VALIDATE_IP: var ipv4 = /^(25[0-5]|2[0-4]\d|[01]?\d?\d)\.(25[0-5]|2[0-4]\d|[01]?\d?\d)\.(25[0-5]|2[0-4]\d|[01]?\d?\d)\.(25[0-5]|2[0-4]\d|[01]?\d?\d)$/; var ipv4privrange = /^(?:0?10|172\.0?(?:1[6-9]|2\d|3[01])|192\.168)\./; var ipv4resrange = /^(?:0?0?0\.|127\.0?0?0\.0?0?0\.0?0?1|128\.0?0?0\.|169\.254\.|191\.255\.|192\.0?0?0\.0?0?2\.|25[0-5]\.|2[34]\d\.|22[4-9]\.)/; // IPv6 regex taken from here: http://forums.intermapper.com/viewtopic.php?t=452 var ipv6 = /^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?$/; var mode = supportedFlags.FILTER_FLAG_IPV4 | supportedFlags.FILTER_FLAG_IPV6; if (flags !== 0) { mode &= flags; } if (mode & supportedFlags.FILTER_FLAG_IPV4) { var ip = ipv4.test(input); if (ip) { if (flags & supportedFlags.FILTER_FLAG_NO_PRIV_RANGE && privrange.test(data)) { return failure; } if (flags & supportedFlags.FILTER_FLAG_NO_RES_RANGE && resrange.test(data)) { return failure; } return input; } } if (mode & supportedFlags.FILTER_FLAG_IPV6) { var ip = ipv6.test(input); if (ip) { // TODO: check ipv6 ranges return input; } } return failure; case supportedFilters.FILTER_CALLBACK: var fn = opts; if (is(fn, "string")) { fn = this.window[fn]; } if (is(fn, "function")) { return fn(input); } return failure; case supportedFilters.FILTER_SANITIZE_NUMBER_INT: return ("" + input).replace(/[^\d+\-]/g, ""); case supportedFilters.FILTER_SANITIZE_NUMBER_FLOAT: return ('' + input).replace(/[^\deE.,+\-]/g, '').replace(/[eE.,]/g, function (m) { return { '.': filter & supportedFilters.FILTER_FLAG_ALLOW_FRACTION ? '.' : '', ',': filter & supportedFilters.FILTER_FLAG_ALLOW_THOUSAND ? ',' : '', 'e': filter & supportedFilters.FILTER_FLAG_ALLOW_SCIENTIFIC ? 'e' : '', 'E': filter & supportedFilters.FILTER_FLAG_ALLOW_SCIENTIFIC ? 'e' : '' }[m]; }); /*case supportedFilters.FILTER_SANITIZE_MAGIC_QUOTES: return this.addslashes(input); // ('' + input).replace(/[\\"']/g, '\\$&').replace(/\u0000/g, '\\0')*/ case supportedFilters.FILTER_SANITIZE_URL: return ("" + data).replace(/[^a-zA-Z\d$\-_.+!*'(),{}|\\\^~\[\]`<>#%";\/?:@&=]/g, ''); case supportedFilters.FILTER_SANITIZE_EMAIL: return ("" + data).replace(/[^a-zA-Z\d!#$%&'*+\-\/=?\^_`{|}~@.\[\]]/g, ''); case supportedFilters.FILTER_DEFAULT: // is alias of FILTER_UNSAFE_RAW // fall-through case supportedFilters.FILTER_UNSAFE_RAW: data = input + ""; if (flags & supportedFlags.FILTER_FLAG_ENCODE_AMP) { data = data.replace(/&/g, "&#38"); } if ((supportedFlags.FILTER_FLAG_ENCODE_LOW | supportedFlags.FILTER_FLAG_STRIP_LOW | supportedFlags.FILTER_FLAG_ENCODE_HIGH | supportedFlags.FILTER_FLAG_STRIP_HIGH) & flags) { data = data.replace(/[\s\S]/g, function (c) { var charCode = c.charCodeAt(0); if (charCode < 32) { return flags & supportedFlags.FILTER_FLAG_STRIP_LOW ? "" : flags & supportedFlags.FILTER_FLAG_ENCODE_LOW ? "&#" + charCode : c; } else if (charCode > 127) { return flags & supportedFlags.FILTER_FLAG_STRIP_HIGH ? "" : flags & supportedFlags.FILTER_FLAG_ENCODE_HIGH ? "&#" + charCode : c; } return c; }); } return data; case supportedFilters.FILTER_SANITIZE_STRING: if (data !== null) { return escapeHtml(data); } return null; default: console.error(`Filter missing: ${filter}`); } return false; } function escapeHtml(text = '') { var map = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;' }; return text.replace(/[&<>"']/g, function (m) { return map[m]; }); } module.exports = filter_var;