| /*! |
| * Copyright (c) 2010 Chris O'Hara <cohara87@gmail.com> |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining |
| * a copy of this software and associated documentation files (the |
| * "Software"), to deal in the Software without restriction, including |
| * without limitation the rights to use, copy, modify, merge, publish, |
| * distribute, sublicense, and/or sell copies of the Software, and to |
| * permit persons to whom the Software is furnished to do so, subject to |
| * the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be |
| * included in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
| * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
| * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| |
| (function(exports) { |
| |
| var entities = { |
| ' ': '\u00a0', |
| '¡': '\u00a1', |
| '¢': '\u00a2', |
| '£': '\u00a3', |
| '¤': '\u20ac', |
| '¥': '\u00a5', |
| '¦': '\u0160', |
| '§': '\u00a7', |
| '¨': '\u0161', |
| '©': '\u00a9', |
| 'ª': '\u00aa', |
| '«': '\u00ab', |
| '¬': '\u00ac', |
| '­': '\u00ad', |
| '®': '\u00ae', |
| '¯': '\u00af', |
| '°': '\u00b0', |
| '±': '\u00b1', |
| '²': '\u00b2', |
| '³': '\u00b3', |
| '´': '\u017d', |
| 'µ': '\u00b5', |
| '¶': '\u00b6', |
| '·': '\u00b7', |
| '¸': '\u017e', |
| '¹': '\u00b9', |
| 'º': '\u00ba', |
| '»': '\u00bb', |
| '¼': '\u0152', |
| '½': '\u0153', |
| '¾': '\u0178', |
| '¿': '\u00bf', |
| 'À': '\u00c0', |
| 'Á': '\u00c1', |
| 'Â': '\u00c2', |
| 'Ã': '\u00c3', |
| 'Ä': '\u00c4', |
| 'Å': '\u00c5', |
| 'Æ': '\u00c6', |
| 'Ç': '\u00c7', |
| 'È': '\u00c8', |
| 'É': '\u00c9', |
| 'Ê': '\u00ca', |
| 'Ë': '\u00cb', |
| 'Ì': '\u00cc', |
| 'Í': '\u00cd', |
| 'Î': '\u00ce', |
| 'Ï': '\u00cf', |
| 'Ð': '\u00d0', |
| 'Ñ': '\u00d1', |
| 'Ò': '\u00d2', |
| 'Ó': '\u00d3', |
| 'Ô': '\u00d4', |
| 'Õ': '\u00d5', |
| 'Ö': '\u00d6', |
| '×': '\u00d7', |
| 'Ø': '\u00d8', |
| 'Ù': '\u00d9', |
| 'Ú': '\u00da', |
| 'Û': '\u00db', |
| 'Ü': '\u00dc', |
| 'Ý': '\u00dd', |
| 'Þ': '\u00de', |
| 'ß': '\u00df', |
| 'à': '\u00e0', |
| 'á': '\u00e1', |
| 'â': '\u00e2', |
| 'ã': '\u00e3', |
| 'ä': '\u00e4', |
| 'å': '\u00e5', |
| 'æ': '\u00e6', |
| 'ç': '\u00e7', |
| 'è': '\u00e8', |
| 'é': '\u00e9', |
| 'ê': '\u00ea', |
| 'ë': '\u00eb', |
| 'ì': '\u00ec', |
| 'í': '\u00ed', |
| 'î': '\u00ee', |
| 'ï': '\u00ef', |
| 'ð': '\u00f0', |
| 'ñ': '\u00f1', |
| 'ò': '\u00f2', |
| 'ó': '\u00f3', |
| 'ô': '\u00f4', |
| 'õ': '\u00f5', |
| 'ö': '\u00f6', |
| '÷': '\u00f7', |
| 'ø': '\u00f8', |
| 'ù': '\u00f9', |
| 'ú': '\u00fa', |
| 'û': '\u00fb', |
| 'ü': '\u00fc', |
| 'ý': '\u00fd', |
| 'þ': '\u00fe', |
| 'ÿ': '\u00ff', |
| '"': '\u0022', |
| '<': '\u003c', |
| '>': '\u003e', |
| ''': '\u0027', |
| '−': '\u2212', |
| 'ˆ': '\u02c6', |
| '˜': '\u02dc', |
| 'Š': '\u0160', |
| '‹': '\u2039', |
| 'Œ': '\u0152', |
| '‘': '\u2018', |
| '’': '\u2019', |
| '“': '\u201c', |
| '”': '\u201d', |
| '•': '\u2022', |
| '–': '\u2013', |
| '—': '\u2014', |
| '™': '\u2122', |
| 'š': '\u0161', |
| '›': '\u203a', |
| 'œ': '\u0153', |
| 'Ÿ': '\u0178', |
| 'ƒ': '\u0192', |
| 'Α': '\u0391', |
| 'Β': '\u0392', |
| 'Γ': '\u0393', |
| 'Δ': '\u0394', |
| 'Ε': '\u0395', |
| 'Ζ': '\u0396', |
| 'Η': '\u0397', |
| 'Θ': '\u0398', |
| 'Ι': '\u0399', |
| 'Κ': '\u039a', |
| 'Λ': '\u039b', |
| 'Μ': '\u039c', |
| 'Ν': '\u039d', |
| 'Ξ': '\u039e', |
| 'Ο': '\u039f', |
| 'Π': '\u03a0', |
| 'Ρ': '\u03a1', |
| 'Σ': '\u03a3', |
| 'Τ': '\u03a4', |
| 'Υ': '\u03a5', |
| 'Φ': '\u03a6', |
| 'Χ': '\u03a7', |
| 'Ψ': '\u03a8', |
| 'Ω': '\u03a9', |
| 'α': '\u03b1', |
| 'β': '\u03b2', |
| 'γ': '\u03b3', |
| 'δ': '\u03b4', |
| 'ε': '\u03b5', |
| 'ζ': '\u03b6', |
| 'η': '\u03b7', |
| 'θ': '\u03b8', |
| 'ι': '\u03b9', |
| 'κ': '\u03ba', |
| 'λ': '\u03bb', |
| 'μ': '\u03bc', |
| 'ν': '\u03bd', |
| 'ξ': '\u03be', |
| 'ο': '\u03bf', |
| 'π': '\u03c0', |
| 'ρ': '\u03c1', |
| 'ς': '\u03c2', |
| 'σ': '\u03c3', |
| 'τ': '\u03c4', |
| 'υ': '\u03c5', |
| 'φ': '\u03c6', |
| 'χ': '\u03c7', |
| 'ψ': '\u03c8', |
| 'ω': '\u03c9', |
| 'ϑ': '\u03d1', |
| 'ϒ': '\u03d2', |
| 'ϖ': '\u03d6', |
| ' ': '\u2002', |
| ' ': '\u2003', |
| ' ': '\u2009', |
| '‌': '\u200c', |
| '‍': '\u200d', |
| '‎': '\u200e', |
| '‏': '\u200f', |
| '‚': '\u201a', |
| '„': '\u201e', |
| '†': '\u2020', |
| '‡': '\u2021', |
| '…': '\u2026', |
| '‰': '\u2030', |
| '′': '\u2032', |
| '″': '\u2033', |
| '‾': '\u203e', |
| '⁄': '\u2044', |
| '€': '\u20ac', |
| 'ℑ': '\u2111', |
| '℘': '\u2118', |
| 'ℜ': '\u211c', |
| 'ℵ': '\u2135', |
| '←': '\u2190', |
| '↑': '\u2191', |
| '→': '\u2192', |
| '↓': '\u2193', |
| '↔': '\u2194', |
| '↵': '\u21b5', |
| '⇐': '\u21d0', |
| '⇑': '\u21d1', |
| '⇒': '\u21d2', |
| '⇓': '\u21d3', |
| '⇔': '\u21d4', |
| '∀': '\u2200', |
| '∂': '\u2202', |
| '∃': '\u2203', |
| '∅': '\u2205', |
| '∇': '\u2207', |
| '∈': '\u2208', |
| '∉': '\u2209', |
| '∋': '\u220b', |
| '∏': '\u220f', |
| '∑': '\u2211', |
| '∗': '\u2217', |
| '√': '\u221a', |
| '∝': '\u221d', |
| '∞': '\u221e', |
| '∠': '\u2220', |
| '∧': '\u2227', |
| '∨': '\u2228', |
| '∩': '\u2229', |
| '∪': '\u222a', |
| '∫': '\u222b', |
| '∴': '\u2234', |
| '∼': '\u223c', |
| '≅': '\u2245', |
| '≈': '\u2248', |
| '≠': '\u2260', |
| '≡': '\u2261', |
| '≤': '\u2264', |
| '≥': '\u2265', |
| '⊂': '\u2282', |
| '⊃': '\u2283', |
| '⊄': '\u2284', |
| '⊆': '\u2286', |
| '⊇': '\u2287', |
| '⊕': '\u2295', |
| '⊗': '\u2297', |
| '⊥': '\u22a5', |
| '⋅': '\u22c5', |
| '⌈': '\u2308', |
| '⌉': '\u2309', |
| '⌊': '\u230a', |
| '⌋': '\u230b', |
| '⟨': '\u2329', |
| '⟩': '\u232a', |
| '◊': '\u25ca', |
| '♠': '\u2660', |
| '♣': '\u2663', |
| '♥': '\u2665', |
| '♦': '\u2666' |
| }; |
| |
| var decode = function (str) { |
| if (!~str.indexOf('&')) return str; |
| |
| //Decode literal entities |
| for (var i in entities) { |
| str = str.replace(new RegExp(i, 'g'), entities[i]); |
| } |
| |
| //Decode hex entities |
| str = str.replace(/&#x(0*[0-9a-f]{2,5});?/gi, function (m, code) { |
| return String.fromCharCode(parseInt(+code, 16)); |
| }); |
| |
| //Decode numeric entities |
| str = str.replace(/&#([0-9]{2,4});?/gi, function (m, code) { |
| return String.fromCharCode(+code); |
| }); |
| |
| str = str.replace(/&/g, '&'); |
| |
| return str; |
| } |
| |
| var encode = function (str) { |
| str = str.replace(/&/g, '&'); |
| |
| //IE doesn't accept ' |
| str = str.replace(/'/g, '''); |
| |
| //Encode literal entities |
| for (var i in entities) { |
| str = str.replace(new RegExp(entities[i], 'g'), i); |
| } |
| |
| return str; |
| } |
| |
| exports.entities = { |
| encode: encode, |
| decode: decode |
| } |
| |
| //This module is adapted from the CodeIgniter framework |
| //The license is available at http://codeigniter.com/ |
| |
| var never_allowed_str = { |
| 'document.cookie': '[removed]', |
| 'document.write': '[removed]', |
| '.parentNode': '[removed]', |
| '.innerHTML': '[removed]', |
| 'window.location': '[removed]', |
| '-moz-binding': '[removed]', |
| '<!--': '<!--', |
| '-->': '-->', |
| '<![CDATA[': '<![CDATA[' |
| }; |
| |
| var never_allowed_regex = { |
| 'javascript\\s*:': '[removed]', |
| 'expression\\s*(\\(|&\\#40;)': '[removed]', |
| 'vbscript\\s*:': '[removed]', |
| 'Redirect\\s+302': '[removed]' |
| }; |
| |
| var non_displayables = [ |
| /%0[0-8bcef]/g, // url encoded 00-08, 11, 12, 14, 15 |
| /%1[0-9a-f]/g, // url encoded 16-31 |
| /[\x00-\x08]/g, // 00-08 |
| /\x0b/g, /\x0c/g, // 11,12 |
| /[\x0e-\x1f]/g // 14-31 |
| ]; |
| |
| var compact_words = [ |
| 'javascript', 'expression', 'vbscript', |
| 'script', 'applet', 'alert', 'document', |
| 'write', 'cookie', 'window' |
| ]; |
| |
| exports.xssClean = function(str, is_image) { |
| |
| //Recursively clean objects and arrays |
| if (typeof str === 'object') { |
| for (var i in str) { |
| str[i] = exports.xssClean(str[i]); |
| } |
| return str; |
| } |
| |
| //Remove invisible characters |
| str = remove_invisible_characters(str); |
| |
| //Protect query string variables in URLs => 901119URL5918AMP18930PROTECT8198 |
| str = str.replace(/\&([a-z\_0-9]+)\=([a-z\_0-9]+)/i, xss_hash() + '$1=$2'); |
| |
| //Validate standard character entities - add a semicolon if missing. We do this to enable |
| //the conversion of entities to ASCII later. |
| str = str.replace(/(&\#?[0-9a-z]{2,})([\x00-\x20])*;?/i, '$1;$2'); |
| |
| //Validate UTF16 two byte encoding (x00) - just as above, adds a semicolon if missing. |
| str = str.replace(/(&\#x?)([0-9A-F]+);?/i, '$1;$2'); |
| |
| //Un-protect query string variables |
| str = str.replace(xss_hash(), '&'); |
| |
| //Decode just in case stuff like this is submitted: |
| //<a href="http://%77%77%77%2E%67%6F%6F%67%6C%65%2E%63%6F%6D">Google</a> |
| try { |
| str = decodeURIComponent(str); |
| } catch (e) { |
| // str was not actually URI-encoded |
| } |
| |
| //Convert character entities to ASCII - this permits our tests below to work reliably. |
| //We only convert entities that are within tags since these are the ones that will pose security problems. |
| str = str.replace(/[a-z]+=([\'\"]).*?\1/gi, function(m, match) { |
| return m.replace(match, convert_attribute(match)); |
| }); |
| |
| //Remove invisible characters again |
| str = remove_invisible_characters(str); |
| |
| //Convert tabs to spaces |
| str = str.replace('\t', ' '); |
| |
| //Captured the converted string for later comparison |
| var converted_string = str; |
| |
| //Remove strings that are never allowed |
| for (var i in never_allowed_str) { |
| str = str.replace(i, never_allowed_str[i]); |
| } |
| |
| //Remove regex patterns that are never allowed |
| for (var i in never_allowed_regex) { |
| str = str.replace(new RegExp(i, 'i'), never_allowed_regex[i]); |
| } |
| |
| //Compact any exploded words like: j a v a s c r i p t |
| // We only want to do this when it is followed by a non-word character |
| for (var i in compact_words) { |
| var spacified = compact_words[i].split('').join('\\s*')+'\\s*'; |
| |
| str = str.replace(new RegExp('('+spacified+')(\\W)', 'ig'), function(m, compat, after) { |
| return compat.replace(/\s+/g, '') + after; |
| }); |
| } |
| |
| //Remove disallowed Javascript in links or img tags |
| do { |
| var original = str; |
| |
| if (str.match(/<a/i)) { |
| str = str.replace(/<a\s+([^>]*?)(>|$)/gi, function(m, attributes, end_tag) { |
| attributes = filter_attributes(attributes.replace('<','').replace('>','')); |
| return m.replace(attributes, attributes.replace(/href=.*?(alert\(|alert&\#40;|javascript\:|charset\=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)/gi, '')); |
| }); |
| } |
| |
| if (str.match(/<img/i)) { |
| str = str.replace(/<img\s+([^>]*?)(\s?\/?>|$)/gi, function(m, attributes, end_tag) { |
| attributes = filter_attributes(attributes.replace('<','').replace('>','')); |
| return m.replace(attributes, attributes.replace(/src=.*?(alert\(|alert&\#40;|javascript\:|charset\=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)/gi, '')); |
| }); |
| } |
| |
| if (str.match(/script/i) || str.match(/xss/i)) { |
| str = str.replace(/<(\/*)(script|xss)(.*?)\>/gi, '[removed]'); |
| } |
| |
| } while(original != str); |
| |
| //Remove JavaScript Event Handlers - Note: This code is a little blunt. It removes the event |
| //handler and anything up to the closing >, but it's unlikely to be a problem. |
| event_handlers = ['[^a-z_\-]on\\w*']; |
| |
| //Adobe Photoshop puts XML metadata into JFIF images, including namespacing, |
| //so we have to allow this for images |
| if (!is_image) { |
| event_handlers.push('xmlns'); |
| } |
| |
| str = str.replace(new RegExp("<([^><]+?)("+event_handlers.join('|')+")(\\s*=\\s*[^><]*)([><]*)", 'i'), '<$1$4'); |
| |
| //Sanitize naughty HTML elements |
| //If a tag containing any of the words in the list |
| //below is found, the tag gets converted to entities. |
| //So this: <blink> |
| //Becomes: <blink> |
| naughty = 'alert|applet|audio|basefont|base|behavior|bgsound|blink|body|embed|expression|form|frameset|frame|head|html|ilayer|iframe|input|isindex|layer|link|meta|object|plaintext|style|script|textarea|title|video|xml|xss'; |
| str = str.replace(new RegExp('<(/*\\s*)('+naughty+')([^><]*)([><]*)', 'gi'), function(m, a, b, c, d) { |
| return '<' + a + b + c + d.replace('>','>').replace('<','<'); |
| }); |
| |
| //Sanitize naughty scripting elements Similar to above, only instead of looking for |
| //tags it looks for PHP and JavaScript commands that are disallowed. Rather than removing the |
| //code, it simply converts the parenthesis to entities rendering the code un-executable. |
| //For example: eval('some code') |
| //Becomes: eval('some code') |
| str = str.replace(/(alert|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)/gi, '$1$2($3)'); |
| |
| //This adds a bit of extra precaution in case something got through the above filters |
| for (var i in never_allowed_str) { |
| str = str.replace(i, never_allowed_str[i]); |
| } |
| for (var i in never_allowed_regex) { |
| str = str.replace(new RegExp(i, 'i'), never_allowed_regex[i]); |
| } |
| |
| //Images are handled in a special way |
| if (is_image && str !== converted_string) { |
| throw new Error('Image may contain XSS'); |
| } |
| |
| return str; |
| } |
| |
| function remove_invisible_characters(str) { |
| for (var i in non_displayables) { |
| str = str.replace(non_displayables[i], ''); |
| } |
| return str; |
| } |
| |
| function xss_hash() { |
| //TODO: Create a random hash |
| return '!*$^#(@*#&'; |
| } |
| |
| function convert_attribute(str) { |
| return str.replace('>','>').replace('<','<').replace('\\','\\\\'); |
| } |
| |
| //Filter Attributes - filters tag attributes for consistency and safety |
| function filter_attributes(str) { |
| out = ''; |
| |
| str.replace(/\s*[a-z\-]+\s*=\s*(?:\042|\047)(?:[^\1]*?)\1/gi, function(m) { |
| out += m.replace(/\/\*.*?\*\//g, ''); |
| }); |
| |
| return out; |
| } |
| |
| var Validator = exports.Validator = function() {} |
| |
| Validator.prototype.check = function(str, fail_msg) { |
| this.str = str == null || (isNaN(str) && str.length == undefined) ? '' : str+''; |
| this.msg = fail_msg; |
| this._errors = []; |
| return this; |
| } |
| |
| //Create some aliases - may help code readability |
| Validator.prototype.validate = Validator.prototype.check; |
| Validator.prototype.assert = Validator.prototype.check; |
| |
| Validator.prototype.error = function(msg) { |
| throw new Error(msg); |
| } |
| |
| Validator.prototype.isEmail = function() { |
| if (!this.str.match(/^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!\.)){0,61}[a-zA-Z0-9]?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/)) { |
| return this.error(this.msg || 'Invalid email'); |
| } |
| return this; |
| } |
| |
| //Will work against Visa, MasterCard, American Express, Discover, Diners Club, and JCB card numbering formats |
| Validator.prototype.isCreditCard = function() { |
| this.str = this.str.replace(/[^0-9]+/g, ''); //remove all dashes, spaces, etc. |
| if (!this.str.match(/^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$/)) { |
| return this.error(this.msg || 'Invalid credit card'); |
| } |
| return this; |
| } |
| |
| Validator.prototype.isUrl = function() { |
| if (!this.str.match(/^(?:(?:ht|f)tp(?:s?)\:\/\/|~\/|\/)?(?:\w+:\w+@)?((?:(?:[-\w\d{1-3}]+\.)+(?:com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|edu|co\.uk|ac\.uk|it|fr|tv|museum|asia|local|travel|[a-z]{2}))|((\b25[0-5]\b|\b[2][0-4][0-9]\b|\b[0-1]?[0-9]?[0-9]\b)(\.(\b25[0-5]\b|\b[2][0-4][0-9]\b|\b[0-1]?[0-9]?[0-9]\b)){3}))(?::[\d]{1,5})?(?:(?:(?:\/(?:[-\w~!$+|.,=]|%[a-f\d]{2})+)+|\/)+|\?|#)?(?:(?:\?(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)(?:&(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)*)*(?:#(?:[-\w~!$ |\/.,*:;=]|%[a-f\d]{2})*)?$/i) || this.str.length > 2083) { |
| return this.error(this.msg || 'Invalid URL'); |
| } |
| return this; |
| } |
| |
| Validator.prototype.isIP = function() { |
| if (!this.str.match(/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/)) { |
| return this.error(this.msg || 'Invalid IP'); |
| } |
| return this; |
| } |
| |
| Validator.prototype.isAlpha = function() { |
| if (!this.str.match(/^[a-zA-Z]+$/)) { |
| return this.error(this.msg || 'Invalid characters'); |
| } |
| return this; |
| } |
| |
| Validator.prototype.isAlphanumeric = function() { |
| if (!this.str.match(/^[a-zA-Z0-9]+$/)) { |
| return this.error(this.msg || 'Invalid characters'); |
| } |
| return this; |
| } |
| |
| Validator.prototype.isNumeric = function() { |
| if (!this.str.match(/^-?[0-9]+$/)) { |
| return this.error(this.msg || 'Invalid number'); |
| } |
| return this; |
| } |
| |
| Validator.prototype.isLowercase = function() { |
| if (!this.str.match(/^[a-z0-9]+$/)) { |
| return this.error(this.msg || 'Invalid characters'); |
| } |
| return this; |
| } |
| |
| Validator.prototype.isUppercase = function() { |
| if (!this.str.match(/^[A-Z0-9]+$/)) { |
| return this.error(this.msg || 'Invalid characters'); |
| } |
| return this; |
| } |
| |
| Validator.prototype.isInt = function() { |
| if (!this.str.match(/^(?:-?(?:0|[1-9][0-9]*))$/)) { |
| return this.error(this.msg || 'Invalid integer'); |
| } |
| return this; |
| } |
| |
| Validator.prototype.isDecimal = function() { |
| if (!this.str.match(/^(?:-?(?:0|[1-9][0-9]*))?(?:\.[0-9]*)?$/)) { |
| return this.error(this.msg || 'Invalid decimal'); |
| } |
| return this; |
| } |
| |
| Validator.prototype.isFloat = function() { |
| return this.isDecimal(); |
| } |
| |
| Validator.prototype.notNull = function() { |
| if (this.str === '') { |
| return this.error(this.msg || 'Invalid characters'); |
| } |
| return this; |
| } |
| |
| Validator.prototype.isNull = function() { |
| if (this.str !== '') { |
| return this.error(this.msg || 'Invalid characters'); |
| } |
| return this; |
| } |
| |
| Validator.prototype.notEmpty = function() { |
| if (this.str.match(/^[\s\t\r\n]*$/)) { |
| return this.error(this.msg || 'String is whitespace'); |
| } |
| return this; |
| } |
| |
| Validator.prototype.equals = function(equals) { |
| if (this.str != equals) { |
| return this.error(this.msg || 'Not equal'); |
| } |
| return this; |
| } |
| |
| Validator.prototype.contains = function(str) { |
| if (this.str.indexOf(str) === -1) { |
| return this.error(this.msg || 'Invalid characters'); |
| } |
| return this; |
| } |
| |
| Validator.prototype.notContains = function(str) { |
| if (this.str.indexOf(str) >= 0) { |
| return this.error(this.msg || 'Invalid characters'); |
| } |
| return this; |
| } |
| |
| Validator.prototype.regex = Validator.prototype.is = function(pattern, modifiers) { |
| if (typeof pattern !== 'function') { |
| pattern = new RegExp(pattern, modifiers); |
| } |
| if (! this.str.match(pattern)) { |
| return this.error(this.msg || 'Invalid characters'); |
| } |
| return this; |
| } |
| |
| Validator.prototype.notRegex = Validator.prototype.not = function(pattern, modifiers) { |
| if (typeof pattern !== 'function') { |
| pattern = new RegExp(pattern, modifiers); |
| } |
| if (this.str.match(pattern)) { |
| this.error(this.msg || 'Invalid characters'); |
| } |
| return this; |
| } |
| |
| Validator.prototype.len = function(min, max) { |
| if (this.str.length < min) { |
| this.error(this.msg || 'String is too small'); |
| } |
| if (typeof max !== undefined && this.str.length > max) { |
| return this.error(this.msg || 'String is too large'); |
| } |
| return this; |
| } |
| |
| //Thanks to github.com/sreuter for the idea. |
| Validator.prototype.isUUID = function(version) { |
| if (version == 3 || version == 'v3') { |
| pattern = /[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}$/i; |
| } else if (version == 4 || version == 'v4') { |
| pattern = /[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i; |
| } else { |
| pattern = /[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i; |
| } |
| if (!this.str.match(pattern)) { |
| return this.error(this.msg || 'Not a UUID'); |
| } |
| return this; |
| } |
| |
| Validator.prototype.isDate = function() { |
| var intDate = Date.parse(this.str); |
| if (isNaN(intDate)) { |
| return this.error(this.msg || 'Not a date'); |
| } |
| return this; |
| } |
| |
| Validator.prototype.isIn = function(options) { |
| if (options && typeof options.indexOf === 'function') { |
| if (!~options.indexOf(this.str)) { |
| return this.error(this.msg || 'Unexpected value'); |
| } |
| return this; |
| } else { |
| return this.error(this.msg || 'Invalid in() argument'); |
| } |
| } |
| |
| Validator.prototype.notIn = function(options) { |
| if (options && typeof options.indexOf === 'function') { |
| if (options.indexOf(this.str) !== -1) { |
| return this.error(this.msg || 'Unexpected value'); |
| } |
| return this; |
| } else { |
| return this.error(this.msg || 'Invalid notIn() argument'); |
| } |
| } |
| |
| Validator.prototype.min = function(val) { |
| var number = parseFloat(this.str); |
| |
| if (!isNaN(number) && number < val) { |
| return this.error(this.msg || 'Invalid number'); |
| } |
| |
| return this; |
| } |
| |
| Validator.prototype.max = function(val) { |
| var number = parseFloat(this.str); |
| if (!isNaN(number) && number > val) { |
| return this.error(this.msg || 'Invalid number'); |
| } |
| return this; |
| } |
| |
| Validator.prototype.isArray = function() { |
| if (!Array.isArray(this.str)) { |
| return this.error(this.msg || 'Not an array'); |
| } |
| return this; |
| } |
| |
| var Filter = exports.Filter = function() {} |
| |
| var whitespace = '\\r\\n\\t\\s'; |
| |
| Filter.prototype.modify = function(str) { |
| this.str = str; |
| } |
| |
| //Create some aliases - may help code readability |
| Filter.prototype.convert = Filter.prototype.sanitize = function(str) { |
| this.str = str; |
| return this; |
| } |
| |
| Filter.prototype.xss = function(is_image) { |
| this.modify(exports.xssClean(this.str, is_image)); |
| return this.str; |
| } |
| |
| Filter.prototype.entityDecode = function() { |
| this.modify(decode(this.str)); |
| return this.str; |
| } |
| |
| Filter.prototype.entityEncode = function() { |
| this.modify(encode(this.str)); |
| return this.str; |
| } |
| |
| Filter.prototype.ltrim = function(chars) { |
| chars = chars || whitespace; |
| this.modify(this.str.replace(new RegExp('^['+chars+']+', 'g'), '')); |
| return this.str; |
| } |
| |
| Filter.prototype.rtrim = function(chars) { |
| chars = chars || whitespace; |
| this.modify(this.str.replace(new RegExp('['+chars+']+$', 'g'), '')); |
| return this.str; |
| } |
| |
| Filter.prototype.trim = function(chars) { |
| chars = chars || whitespace; |
| this.modify(this.str.replace(new RegExp('^['+chars+']+|['+chars+']+$', 'g'), '')); |
| return this.str; |
| } |
| |
| Filter.prototype.ifNull = function(replace) { |
| if (!this.str || this.str === '') { |
| this.modify(replace); |
| } |
| return this.str; |
| } |
| |
| Filter.prototype.toFloat = function() { |
| this.modify(parseFloat(this.str)); |
| return this.str; |
| } |
| |
| Filter.prototype.toInt = function(radix) { |
| radix = radix || 10; |
| this.modify(parseInt(this.str), radix); |
| return this.str; |
| } |
| |
| //Any strings with length > 0 (except for '0' and 'false') are considered true, |
| //all other strings are false |
| Filter.prototype.toBoolean = function() { |
| if (!this.str || this.str == '0' || this.str == 'false' || this.str == '') { |
| this.modify(false); |
| } else { |
| this.modify(true); |
| } |
| return this.str; |
| } |
| |
| //String must be equal to '1' or 'true' to be considered true, all other strings |
| //are false |
| Filter.prototype.toBooleanStrict = function() { |
| if (this.str == '1' || this.str == 'true') { |
| this.modify(true); |
| } else { |
| this.modify(false); |
| } |
| return this.str; |
| } |
| |
| //Quick access methods |
| exports.sanitize = exports.convert = function(str) { |
| var filter = new exports.Filter(); |
| return filter.sanitize(str); |
| } |
| |
| exports.check = exports.validate = exports.assert = function(str, fail_msg) { |
| var validator = new exports.Validator(); |
| return validator.check(str, fail_msg); |
| } |
| |
| })(typeof(exports) === 'undefined' ? window : exports); |
| |