diff --git a/README.md b/README.md index ab5609ae0..cde507282 100644 --- a/README.md +++ b/README.md @@ -143,7 +143,7 @@ Validator | Description **isMultibyte(str)** | check if the string contains one or more multibyte chars. **isNumeric(str [, options])** | check if the string contains only numbers.

`options` is an object which defaults to `{no_symbols: false}` it also has locale as an option. If `no_symbols` is true, the validator will reject numeric strings that feature a symbol (e.g. `+`, `-`, or `.`).

`locale` determine the decimal separator and is one of `['ar', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-QA', 'ar-QM', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'bg-BG', 'cs-CZ', 'da-DK', 'de-DE', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'es-ES', 'fr-FR', 'fr-CA', 'hu-HU', 'it-IT', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'sl-SI', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'tr-TR', 'uk-UA']`. **isOctal(str)** | check if the string is a valid octal number. -**isPassportNumber(str, countryCode)** | check if the string is a valid passport number.

(countryCode is one of `[ 'AM', 'AR', 'AT', 'AU', 'BE', 'BG', 'BY', 'CA', 'CH', 'CN', 'CY', 'CZ', 'DE', 'DK', 'DZ', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HU', 'IE' 'IN', 'IS', 'IT', 'JP', 'KR', 'LT', 'LU', 'LV', 'LY', 'MT', 'NL', 'PO', 'PT', 'RO', 'RU', 'SE', 'SL', 'SK', 'TR', 'UA', 'US' ]`. +**isPassportNumber(str, countryCode)** | check if the string is a valid passport number.

(countryCode is one of `[ 'AM', 'AR', 'AT', 'AU', 'BE', 'BG', 'BY', 'BR', 'CA', 'CH', 'CN', 'CY', 'CZ', 'DE', 'DK', 'DZ', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HU', 'IE' 'IN', 'IS', 'IT', 'JP', 'KR', 'LT', 'LU', 'LV', 'LY', 'MT', 'NL', 'PO', 'PT', 'RO', 'RU', 'SE', 'SL', 'SK', 'TR', 'UA', 'US' ]`. **isPort(str)** | check if the string is a valid port number. **isPostalCode(str, locale)** | check if the string is a postal code,

(locale is one of `[ 'AD', 'AT', 'AU', 'AZ', 'BE', 'BG', 'BR', 'BY', 'CA', 'CH', 'CN', 'CZ', 'DE', 'DK', 'DO', 'DZ', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HT', 'HU', 'ID', 'IE' 'IL', 'IN', 'IR', 'IS', 'IT', 'JP', 'KE', 'LI', 'LT', 'LU', 'LV', 'MT', 'MX', 'MY', 'NL', 'NO', 'NP', 'NZ', 'PL', 'PR', 'PT', 'RO', 'RU', 'SA', 'SE', 'SG', 'SI', 'TH', 'TN', 'TW', 'UA', 'US', 'ZA', 'ZM' ]` OR 'any'. If 'any' is used, function will check if any of the locals match. Locale list is `validator.isPostalCodeLocales`.). **isRFC3339(str)** | check if the string is a valid [RFC 3339](https://tools.ietf.org/html/rfc3339) date. @@ -153,7 +153,7 @@ Validator | Description **isUppercase(str)** | check if the string is uppercase. **isSlug** | Check if the string is of type slug. `Options` allow a single hyphen between string. e.g. [`cn-cn`, `cn-c-c`] **isStrongPassword(str [, options])** | Check if a password is strong or not. Allows for custom requirements or scoring rules. If `returnScore` is true, then the function returns an integer score for the password rather than a boolean.
Default options:
`{ minLength: 8, minLowercase: 1, minUppercase: 1, minNumbers: 1, minSymbols: 1, returnScore: false, pointsPerUnique: 1, pointsPerRepeat: 0.5, pointsForContainingLower: 10, pointsForContainingUpper: 10, pointsForContainingNumber: 10, pointsForContainingSymbol: 10 }` -**isTaxID(str, locale)** | Check if the given value is a valid Tax Identification Number. Default locale is `en-US`.

More info about exact TIN support can be found in `src/lib/isTaxID.js`

Supported locales: `[ 'bg-BG', 'cs-CZ', 'de-AT', 'de-DE', 'dk-DK', 'el-CY', 'el-GR', 'en-GB', 'en-IE', 'en-US', 'es-ES', 'et-EE', 'fi-FI', 'fr-BE', 'fr-FR', 'fr-LU', 'hr-HR', 'hu-HU', 'it-IT', 'lb-LU', 'lt-LT', 'lv-LV' 'mt-MT', 'nl-BE', 'nl-NL', 'pl-PL', 'pt-PT', 'ro-RO', 'sk-SK', 'sl-SI', 'sv-SE' ]` +**isTaxID(str, locale)** | Check if the given value is a valid Tax Identification Number. Default locale is `en-US`.

More info about exact TIN support can be found in `src/lib/isTaxID.js`

Supported locales: `[ 'bg-BG', 'cs-CZ', 'de-AT', 'de-DE', 'dk-DK', 'el-CY', 'el-GR', 'en-GB', 'en-IE', 'en-US', 'es-ES', 'et-EE', 'fi-FI', 'fr-BE', 'fr-FR', 'fr-LU', 'hr-HR', 'hu-HU', 'it-IT', 'lb-LU', 'lt-LT', 'lv-LV' 'mt-MT', 'nl-BE', 'nl-NL', 'pl-PL', 'pt-BR', 'pt-PT', 'ro-RO', 'sk-SK', 'sl-SI', 'sv-SE' ]` **isURL(str [, options])** | check if the string is an URL.

`options` is an object which defaults to `{ protocols: ['http','https','ftp'], require_tld: true, require_protocol: false, require_host: true, require_valid_protocol: true, allow_underscores: false, host_whitelist: false, host_blacklist: false, allow_trailing_dot: false, allow_protocol_relative_urls: false, disallow_auth: false }`.

require_protocol - if set as true isURL will return false if protocol is not present in the URL.
require_valid_protocol - isURL will check if the URL's protocol is present in the protocols option.
protocols - valid protocols can be modified with this option.
require_host - if set as false isURL will not check if host is present in the URL.
require_port - if set as true isURL will check if port is present in the URL.
allow_protocol_relative_urls - if set as true protocol relative URLs will be allowed.
validate_length - if set as false isURL will skip string length validation (2083 characters is IE max URL length). **isUUID(str [, version])** | check if the string is a UUID (version 3, 4 or 5). **isVariableWidth(str)** | check if the string contains a mixture of full and half-width chars. diff --git a/src/lib/isPassportNumber.js b/src/lib/isPassportNumber.js index 76fd0827e..0c1582e57 100644 --- a/src/lib/isPassportNumber.js +++ b/src/lib/isPassportNumber.js @@ -13,6 +13,7 @@ const passportRegexByCountryCode = { AU: /^[A-Z]\d{7}$/, // AUSTRALIA BE: /^[A-Z]{2}\d{6}$/, // BELGIUM BG: /^\d{9}$/, // BULGARIA + BR: /^[A-Z]{2}\d{6}$/, // BRAZIL BY: /^[A-Z]{2}\d{7}$/, // BELARUS CA: /^[A-Z]{2}\d{6}$/, // CANADA CH: /^[A-Z]\d{7}$/, // SWITZERLAND diff --git a/src/lib/isTaxID.js b/src/lib/isTaxID.js index 90a0c2a96..f1ebae6d9 100644 --- a/src/lib/isTaxID.js +++ b/src/lib/isTaxID.js @@ -852,6 +852,93 @@ function plPlCheck(tin) { return checksum === parseInt(tin[10], 10); } +/* +* pt-BR validation function +* (Cadastro de Pessoas Físicas (CPF, persons) +* Cadastro Nacional de Pessoas Jurídicas (CNPJ, entities) +* Both inputs will be validated +*/ + +function ptBrCheck(tin) { + tin = tin.replace(/[^\d]+/g, ''); + if (tin === '') return false; + + if (tin.length === 11) { + let sum; + let ramainder; + sum = 0; + tin = tin.replace(/[^\d]+/g, ''); + + if ( // Reject known invalid CPFs + tin === '11111111111' || + tin === '22222222222' || + tin === '33333333333' || + tin === '44444444444' || + tin === '55555555555' || + tin === '66666666666' || + tin === '77777777777' || + tin === '88888888888' || + tin === '99999999999' || + tin === '00000000000' + ) return false; + + for (let i = 1; i <= 9; i++) sum += parseInt(tin.substring(i - 1, i), 10) * (11 - i); + ramainder = (sum * 10) % 11; + if ((ramainder === 10) || (ramainder === 11)) ramainder = 0; + if (ramainder !== parseInt(tin.substring(9, 10), 10)) return false; + sum = 0; + + for (let i = 1; i <= 10; i++) sum += parseInt(tin.substring(i - 1, i), 10) * (12 - i); + ramainder = (sum * 10) % 11; + if ((ramainder === 10) || (ramainder === 11)) ramainder = 0; + if (ramainder !== parseInt(tin.substring(10, 11), 10)) return false; + + return true; + } + + if (tin.length !== 14) { return false; } + + if ( // Reject know invalid CNPJs + tin === '00000000000000' || + tin === '11111111111111' || + tin === '22222222222222' || + tin === '33333333333333' || + tin === '44444444444444' || + tin === '55555555555555' || + tin === '66666666666666' || + tin === '77777777777777' || + tin === '88888888888888' || + tin === '99999999999999') { return false; } + + let length = tin.length - 2; + let identifiers = tin.substring(0, length); + let verificators = tin.substring(length); + let sum = 0; + let pos = length - 7; + + for (let i = length; i >= 1; i--) { + sum += identifiers.charAt(length - i) * pos; + pos -= 1; + if (pos < 2) { pos = 9; } + } + let result = sum % 11 < 2 ? 0 : 11 - (sum % 11); + if (result !== parseInt(verificators.charAt(0), 10)) { return false; } + + length += 1; + identifiers = tin.substring(0, length); + sum = 0; + pos = length - 7; + for (let i = length; i >= 1; i--) { + sum += identifiers.charAt(length - i) * pos; + pos -= 1; + if (pos < 2) { pos = 9; } + } + result = sum % 11 < 2 ? 0 : 11 - (sum % 11); + if (result !== parseInt(verificators.charAt(1), 10)) { return false; } + + return true; +} + /* * pt-PT validation function * (Número de identificação fiscal (NIF), persons/entities) @@ -1039,6 +1126,7 @@ const taxIdFormat = { 'mt-MT': /^\d{3,7}[APMGLHBZ]$|^([1-8])\1\d{7}$/i, 'nl-NL': /^\d{9}$/, 'pl-PL': /^\d{10,11}$/, + 'pt-BR': /^\d{11,14}$/, 'pt-PT': /^\d{9}$/, 'ro-RO': /^\d{13}$/, 'sk-SK': /^\d{6}\/{0,1}\d{3,4}$/, @@ -1076,6 +1164,7 @@ const taxIdCheck = { 'mt-MT': mtMtCheck, 'nl-NL': nlNlCheck, 'pl-PL': plPlCheck, + 'pt-BR': ptBrCheck, 'pt-PT': ptPtCheck, 'ro-RO': roRoCheck, 'sk-SK': skSkCheck, diff --git a/test/validators.js b/test/validators.js index 34b6474e8..b588a42d2 100644 --- a/test/validators.js +++ b/test/validators.js @@ -2289,6 +2289,18 @@ describe('Validators', () => { ], }); + test({ + validator: 'isPassportNumber', + args: ['BR'], + valid: [ + 'FZ973689', + 'GH231233', + ], + invalid: [ + 'ABX29332', + ], + }); + test({ validator: 'isPassportNumber', args: ['BY'], @@ -9965,6 +9977,22 @@ describe('Validators', () => { '2234567855', '02223013623'], }); + test({ + validator: 'isTaxID', + args: ['pt-BR'], + valid: [ + '35161990910', + '74407265027', + '05423994000172', + '11867044000130'], + invalid: [ + '170.691.440-72', + '01494282042', + '11111111111', + '94.592.973/0001-82', + '28592361000192', + '11111111111111'], + }); test({ validator: 'isTaxID', args: ['pt-PT'],