diff --git a/README.md b/README.md index e3fc8eaa7..97fa140f5 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ Validator | Description **isHexColor(str)** | check if the string is a hexadecimal color. **isHSL(str)** | check if the string is an HSL (hue, saturation, lightness, optional alpha) color based on [CSS Colors Level 4 specification](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value).

Comma-separated format supported. Space-separated format supported with the exception of a few edge cases (ex: `hsl(200grad+.1%62%/1)`). **isRgbColor(str, [, includePercentValues])** | check if the string is a rgb or rgba color.

`includePercentValues` defaults to `true`. If you don't want to allow to set `rgb` or `rgba` values with percents, like `rgb(5%,5%,5%)`, or `rgba(90%,90%,90%,.3)`, then set it to false. -**isIdentityCard(str [, locale])** | check if the string is a valid identity card code.

`locale` is one of `['ES', 'IN', 'zh-TW', 'he-IL', 'ar-TN', 'zh-CN']` OR `'any'`. If 'any' is used, function will check if any of the locals match.

Defaults to 'any'. +**isIdentityCard(str [, locale])** | check if the string is a valid identity card code.

`locale` is one of `['ES', 'IN', 'NO', 'zh-TW', 'he-IL', 'ar-TN', 'zh-CN']` OR `'any'`. If 'any' is used, function will check if any of the locals match.

Defaults to 'any'. **isIn(str, values)** | check if the string is in a array of allowed values. **isInt(str [, options])** | check if the string is an integer.

`options` is an object which can contain the keys `min` and/or `max` to check the integer is within boundaries (e.g. `{ min: 10, max: 99 }`). `options` can also contain the key `allow_leading_zeroes`, which when set to false will disallow integer values with leading zeroes (e.g. `{ allow_leading_zeroes: false }`). Finally, `options` can contain the keys `gt` and/or `lt` which will enforce integers being greater than or less than, respectively, the value provided (e.g. `{gt: 1, lt: 4}` for a number between 1 and 4). **isIP(str [, version])** | check if the string is an IP (version 4 or 6). diff --git a/es/lib/isIdentityCard.js b/es/lib/isIdentityCard.js index b845a397b..de8714aeb 100644 --- a/es/lib/isIdentityCard.js +++ b/es/lib/isIdentityCard.js @@ -29,8 +29,7 @@ var validators = { var p = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 5, 7, 6, 2, 8, 3, 0, 9, 4], [5, 8, 0, 3, 7, 9, 6, 1, 4, 2], [8, 9, 1, 6, 0, 4, 3, 5, 2, 7], [9, 4, 5, 3, 1, 2, 6, 8, 7, 0], [4, 2, 8, 6, 5, 7, 3, 9, 0, 1], [2, 7, 9, 3, 8, 0, 6, 4, 1, 5], [7, 0, 4, 6, 9, 1, 3, 2, 5, 8]]; // sanitize user input - var sanitized = str.trim(); - console.log(sanitized); // validate the data structure + var sanitized = str.trim(); // validate the data structure if (!DNI.test(sanitized)) { return false; @@ -43,6 +42,23 @@ var validators = { }); return c === 0; }, + NO: function NO(str) { + var sanitized = str.trim(); + if (isNaN(Number(sanitized))) return false; + if (sanitized.length !== 11) return false; + if (sanitized === '00000000000') return false; // https://no.wikipedia.org/wiki/F%C3%B8dselsnummer + + var f = sanitized.split('').map(Number); + var k1 = (11 - (3 * f[0] + 7 * f[1] + 6 * f[2] + 1 * f[3] + 8 * f[4] + 9 * f[5] + 4 * f[6] + 5 * f[7] + 2 * f[8]) % 11) % 11; + var k2 = (11 - (5 * f[0] + 4 * f[1] + 3 * f[2] + 2 * f[3] + 7 * f[4] + 6 * f[5] + 5 * f[6] + 4 * f[7] + 3 * f[8] + 2 * k1) % 11) % 11; + + if (k1 === 11) { + k1 = 0; + } + + if (k1 !== f[9] || k2 !== f[10]) return false; + return true; + }, 'he-IL': function heIL(str) { var DNI = /^\d{9}$/; // sanitize user input diff --git a/lib/isIdentityCard.js b/lib/isIdentityCard.js index 8c8575952..7f5c63d5d 100644 --- a/lib/isIdentityCard.js +++ b/lib/isIdentityCard.js @@ -39,8 +39,7 @@ var validators = { var p = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 5, 7, 6, 2, 8, 3, 0, 9, 4], [5, 8, 0, 3, 7, 9, 6, 1, 4, 2], [8, 9, 1, 6, 0, 4, 3, 5, 2, 7], [9, 4, 5, 3, 1, 2, 6, 8, 7, 0], [4, 2, 8, 6, 5, 7, 3, 9, 0, 1], [2, 7, 9, 3, 8, 0, 6, 4, 1, 5], [7, 0, 4, 6, 9, 1, 3, 2, 5, 8]]; // sanitize user input - var sanitized = str.trim(); - console.log(sanitized); // validate the data structure + var sanitized = str.trim(); // validate the data structure if (!DNI.test(sanitized)) { return false; @@ -53,6 +52,23 @@ var validators = { }); return c === 0; }, + NO: function NO(str) { + var sanitized = str.trim(); + if (isNaN(Number(sanitized))) return false; + if (sanitized.length !== 11) return false; + if (sanitized === '00000000000') return false; // https://no.wikipedia.org/wiki/F%C3%B8dselsnummer + + var f = sanitized.split('').map(Number); + var k1 = (11 - (3 * f[0] + 7 * f[1] + 6 * f[2] + 1 * f[3] + 8 * f[4] + 9 * f[5] + 4 * f[6] + 5 * f[7] + 2 * f[8]) % 11) % 11; + var k2 = (11 - (5 * f[0] + 4 * f[1] + 3 * f[2] + 2 * f[3] + 7 * f[4] + 6 * f[5] + 5 * f[6] + 4 * f[7] + 3 * f[8] + 2 * k1) % 11) % 11; + + if (k1 === 11) { + k1 = 0; + } + + if (k1 !== f[9] || k2 !== f[10]) return false; + return true; + }, 'he-IL': function heIL(str) { var DNI = /^\d{9}$/; // sanitize user input diff --git a/src/lib/isIdentityCard.js b/src/lib/isIdentityCard.js index 65416f59f..11c72cf77 100644 --- a/src/lib/isIdentityCard.js +++ b/src/lib/isIdentityCard.js @@ -61,7 +61,6 @@ const validators = { // sanitize user input const sanitized = str.trim(); - console.log(sanitized); // validate the data structure if (!DNI.test(sanitized)) { @@ -76,6 +75,26 @@ const validators = { return c === 0; }, + NO: (str) => { + const sanitized = str.trim(); + if (isNaN(Number(sanitized))) return false; + if (sanitized.length !== 11) return false; + if (sanitized === '00000000000') return false; + + // https://no.wikipedia.org/wiki/F%C3%B8dselsnummer + const f = sanitized.split('').map(Number); + let k1 = (11 - (((3 * f[0]) + (7 * f[1]) + (6 * f[2]) + + (1 * f[3]) + (8 * f[4]) + (9 * f[5]) + (4 * f[6]) + + (5 * f[7]) + (2 * f[8])) % 11)) % 11; + let k2 = (11 - (((5 * f[0]) + (4 * f[1]) + (3 * f[2]) + + (2 * f[3]) + (7 * f[4]) + (6 * f[5]) + (5 * f[6]) + + (4 * f[7]) + (3 * f[8]) + (2 * k1)) % 11)) % 11; + if (k1 === 11) { + k1 = 0; + } + if (k1 !== f[9] || k2 !== f[10]) return false; + return true; + }, 'he-IL': (str) => { const DNI = /^\d{9}$/; diff --git a/test/validators.js b/test/validators.js index c6031db10..60fcf1b8d 100755 --- a/test/validators.js +++ b/test/validators.js @@ -3978,6 +3978,24 @@ describe('Validators', () => { 'X1234567L', ], }, + { + locale: 'NO', + valid: [ + '09053426694', + '26028338723', + '08031470790', + '12051539514', + '02077448074', + '14035638319', + '13031379673', + '29126214926', + ], + invalid: [ + '09053426699', + '26028338724', + '92031470790', + ], + }, { locale: 'he-IL', valid: [ diff --git a/validator.js b/validator.js index fe8765116..2018aa700 100644 --- a/validator.js +++ b/validator.js @@ -82,7 +82,7 @@ function _unsupportedIterableToArray(o, minLen) { if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; - if (n === "Map" || n === "Set") return Array.from(n); + if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } @@ -1023,6 +1023,7 @@ function isNumeric(str, options) { * https://docs.microsoft.com/en-us/microsoft-365/compliance/eu-passport-number -- EU Passport Number * https://countrycode.org/ -- Country Codes */ + var passportRegexByCountryCode = { AM: /^[A-Z]{2}\d{7}$/, // ARMENIA @@ -1070,6 +1071,8 @@ var passportRegexByCountryCode = { // HUNGARY IE: /^[A-Z0-9]{2}\d{7}$/, // IRELAND + IN: /^[A-Z]{1}-?\d{7}$/, + // INDIA IS: /^(A)\d{7}$/, // ICELAND IT: /^[A-Z0-9]{2}\d{7}$/, @@ -1117,7 +1120,9 @@ var passportRegexByCountryCode = { */ function isPassportNumber(str, countryCode) { + assertString(str); /** Remove All Whitespaces, Convert to UPPERCASE */ + var normalizedStr = str.replace(/\s/g, '').toUpperCase(); return countryCode.toUpperCase() in passportRegexByCountryCode && passportRegexByCountryCode[countryCode].test(normalizedStr); } @@ -1345,6 +1350,7 @@ var ibanRegexThroughCountryCode = { IE: /^(IE[0-9]{2})[A-Z0-9]{4}\d{14}$/, IL: /^(IL[0-9]{2})\d{19}$/, IQ: /^(IQ[0-9]{2})[A-Z]{4}\d{15}$/, + IR: /^(IR[0-9]{2})0\d{2}0\d{18}$/, IS: /^(IS[0-9]{2})\d{22}$/, IT: /^(IT[0-9]{2})[A-Z]{1}\d{10}[A-Z0-9]{12}$/, JO: /^(JO[0-9]{2})[A-Z]{4}\d{22}$/, @@ -1356,7 +1362,6 @@ var ibanRegexThroughCountryCode = { LT: /^(LT[0-9]{2})\d{16}$/, LU: /^(LU[0-9]{2})\d{3}[A-Z0-9]{13}$/, LV: /^(LV[0-9]{2})[A-Z]{4}[A-Z0-9]{13}$/, - LY: /^((\+?218)|0)?(9[1-6]\d{7}|[1-8]\d{7,9})$/, MC: /^(MC[0-9]{2})\d{10}[A-Z0-9]{11}\d{2}$/, MD: /^(MD[0-9]{2})[A-Z0-9]{20}$/, ME: /^(ME[0-9]{2})\d{18}$/, @@ -1471,10 +1476,42 @@ function isHash(str, algorithm) { return hash.test(str); } -var jwt = /^([A-Za-z0-9\-_~+\/]+[=]{0,2})\.([A-Za-z0-9\-_~+\/]+[=]{0,2})(?:\.([A-Za-z0-9\-_~+\/]+[=]{0,2}))?$/; +var notBase64 = /[^A-Z0-9+\/=]/i; +var urlSafeBase64 = /^[A-Z0-9_\-]+$/i; +var defaultBase64Options = { + urlSafe: false +}; +function isBase64(str, options) { + assertString(str); + options = merge(options, defaultBase64Options); + var len = str.length; + + if (options.urlSafe) { + return urlSafeBase64.test(str); + } + + if (!len || len % 4 !== 0 || notBase64.test(str)) { + return false; + } + + var firstPaddingChar = str.indexOf('='); + return firstPaddingChar === -1 || firstPaddingChar === len - 1 || firstPaddingChar === len - 2 && str[len - 1] === '='; +} + function isJWT(str) { assertString(str); - return jwt.test(str); + var dotSplit = str.split('.'); + var len = dotSplit.length; + + if (len > 3 || len < 2) { + return false; + } + + return dotSplit.reduce(function (acc, currElem) { + return acc && isBase64(currElem, { + urlSafe: true + }); + }, true); } function isJSON(str) { @@ -1642,6 +1679,43 @@ var validators = { }); return sanitized.endsWith(controlDigits[number % 23]); }, + IN: function IN(str) { + var DNI = /^[1-9]\d{3}\s?\d{4}\s?\d{4}$/; // multiplication table + + var d = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 2, 3, 4, 0, 6, 7, 8, 9, 5], [2, 3, 4, 0, 1, 7, 8, 9, 5, 6], [3, 4, 0, 1, 2, 8, 9, 5, 6, 7], [4, 0, 1, 2, 3, 9, 5, 6, 7, 8], [5, 9, 8, 7, 6, 0, 4, 3, 2, 1], [6, 5, 9, 8, 7, 1, 0, 4, 3, 2], [7, 6, 5, 9, 8, 2, 1, 0, 4, 3], [8, 7, 6, 5, 9, 3, 2, 1, 0, 4], [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]]; // permutation table + + var p = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 5, 7, 6, 2, 8, 3, 0, 9, 4], [5, 8, 0, 3, 7, 9, 6, 1, 4, 2], [8, 9, 1, 6, 0, 4, 3, 5, 2, 7], [9, 4, 5, 3, 1, 2, 6, 8, 7, 0], [4, 2, 8, 6, 5, 7, 3, 9, 0, 1], [2, 7, 9, 3, 8, 0, 6, 4, 1, 5], [7, 0, 4, 6, 9, 1, 3, 2, 5, 8]]; // sanitize user input + + var sanitized = str.trim(); // validate the data structure + + if (!DNI.test(sanitized)) { + return false; + } + + var c = 0; + var invertedArray = sanitized.replace(/\s/g, '').split('').map(Number).reverse(); + invertedArray.forEach(function (val, i) { + c = d[c][p[i % 8][val]]; + }); + return c === 0; + }, + NO: function NO(str) { + var sanitized = str.trim(); + if (isNaN(Number(sanitized))) return false; + if (sanitized.length !== 11) return false; + if (sanitized === '00000000000') return false; // https://no.wikipedia.org/wiki/F%C3%B8dselsnummer + + var f = sanitized.split('').map(Number); + var k1 = (11 - (3 * f[0] + 7 * f[1] + 6 * f[2] + 1 * f[3] + 8 * f[4] + 9 * f[5] + 4 * f[6] + 5 * f[7] + 2 * f[8]) % 11) % 11; + var k2 = (11 - (5 * f[0] + 4 * f[1] + 3 * f[2] + 2 * f[3] + 7 * f[4] + 6 * f[5] + 5 * f[6] + 4 * f[7] + 3 * f[8] + 2 * k1) % 11) % 11; + + if (k1 === 11) { + k1 = 0; + } + + if (k1 !== f[9] || k2 !== f[10]) return false; + return true; + }, 'he-IL': function heIL(str) { var DNI = /^\d{9}$/; // sanitize user input @@ -2054,6 +2128,7 @@ var phones = { 'ar-IQ': /^(\+?964|0)?7[0-9]\d{8}$/, 'ar-JO': /^(\+?962|0)?7[789]\d{7}$/, 'ar-KW': /^(\+?965)[569]\d{7}$/, + 'ar-LY': /^((\+?218)|0)?(9[1-6]\d{7}|[1-8]\d{7,9})$/, 'ar-SA': /^(!?(\+?966)|0)?5\d{8}$/, 'ar-SY': /^(!?(\+?963)|0)?9\d{8}$/, 'ar-TN': /^(\+?216)?[2459]\d{7}$/, @@ -2064,6 +2139,7 @@ var phones = { 'da-DK': /^(\+?45)?\s?\d{2}\s?\d{2}\s?\d{2}\s?\d{2}$/, 'de-DE': /^(\+49)?0?1(5[0-25-9]\d|6([23]|0\d?)|7([0-57-9]|6\d))\d{7}$/, 'de-AT': /^(\+43|0)\d{1,4}\d{3,12}$/, + 'de-CH': /^(\+41|0)(7[5-9])\d{1,7}$/, 'el-GR': /^(\+?30|0)?(69\d{8})$/, 'en-AU': /^(\+?61|0)4\d{8}$/, 'en-GB': /^(\+?44|0)7\d{9}$/, @@ -2087,6 +2163,7 @@ var phones = { 'en-US': /^((\+1|1)?( |-)?)?(\([2-9][0-9]{2}\)|[2-9][0-9]{2})( |-)?([2-9][0-9]{2}( |-)?[0-9]{4})$/, 'en-ZA': /^(\+?27|0)\d{9}$/, 'en-ZM': /^(\+?26)?09[567]\d{7}$/, + 'en-ZW': /^(\+263)[0-9]{9}$/, 'es-CL': /^(\+?56|0)[2-9]\d{1}\d{7}$/, 'es-CR': /^(\+506)?[2-8]\d{7}$/, 'es-EC': /^(\+?593|0)([2-7]|9[2-9])\d{7}$/, @@ -2133,7 +2210,7 @@ var phones = { 'tr-TR': /^(\+?90|0)?5\d{9}$/, 'uk-UA': /^(\+?38|8)?0\d{9}$/, 'vi-VN': /^(\+?84|0)((3([2-9]))|(5([2689]))|(7([0|6-9]))|(8([1-6|89]))|(9([0-9])))([0-9]{7})$/, - 'zh-CN': /^((\+|00)86)?1([358][0-9]|4[579]|6[67]|7[01235678]|9[189])[0-9]{8}$/, + 'zh-CN': /^((\+|00)86)?1([3568][0-9]|4[579]|6[67]|7[01235678]|9[189])[0-9]{8}$/, 'zh-TW': /^(\+?886\-?|0)?9\d{8}$/ }; /* eslint-enable max-len */ @@ -2361,25 +2438,6 @@ function isBase32(str) { return false; } -var notBase64 = /[^A-Z0-9+\/=]/i; -function isBase64(str) { - var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - assertString(str); - - if (options.urlSafe) { - return /^[A-Za-z0-9_-]+$/.test(str); - } - - var len = str.length; - - if (!len || len % 4 !== 0 || notBase64.test(str)) { - return false; - } - - var firstPaddingChar = str.indexOf('='); - return firstPaddingChar === -1 || firstPaddingChar === len - 1 || firstPaddingChar === len - 2 && str[len - 1] === '='; -} - var validMediaType = /^[a-z]+\/[a-z0-9\-\+]+$/i; var validAttribute = /^[a-z\-]+=[a-z0-9\-]+$/i; var validData = /^[a-z0-9!\$&'\(\)\*\+,;=\-\._~:@\/\?%\s]*$/i; @@ -2467,13 +2525,13 @@ function isMimeType(str) { var lat = /^\(?[+-]?(90(\.0+)?|[1-8]?\d(\.\d+)?)$/; var _long = /^\s?[+-]?(180(\.0+)?|1[0-7]\d(\.\d+)?|\d{1,2}(\.\d+)?)\)?$/; -var isLatLong = function (str) { +function isLatLong(str) { assertString(str); if (!str.includes(',')) return false; var pair = str.split(','); if (pair[0].startsWith('(') && !pair[1].endsWith(')') || pair[1].endsWith(')') && !pair[0].startsWith('(')) return false; return lat.test(pair[0]) && _long.test(pair[1]); -}; +} var threeDigit = /^\d{3}$/; var fourDigit = /^\d{4}$/; @@ -2516,6 +2574,7 @@ var patterns = { MT: /^[A-Za-z]{3}\s{0,1}\d{4}$/, NL: /^\d{4}\s?[a-z]{2}$/i, NO: fourDigit, + NP: /^(10|21|22|32|33|34|44|45|56|57)\d{3}$|^(977)$/i, NZ: fourDigit, PL: /^\d{2}\-\d{3}$/, PR: /^00[679]\d{2}([ -]\d{4})?$/, @@ -2534,7 +2593,7 @@ var patterns = { ZM: fiveDigit }; var locales$4 = Object.keys(patterns); -var isPostalCode = function (str, locale) { +function isPostalCode(str, locale) { assertString(str); if (locale in patterns) { @@ -2556,7 +2615,7 @@ var isPostalCode = function (str, locale) { } throw new Error("Invalid locale '".concat(locale, "'")); -}; +} function ltrim(str, chars) { assertString(str); // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Escaping