diff --git a/README.md b/README.md index a64ca450f..d8ca90eb9 100644 --- a/README.md +++ b/README.md @@ -174,6 +174,7 @@ Validator | Description **isVAT(str, countryCode)** | check if the string is a [valid VAT number][VAT Number] if validation is available for the given country code matching [ISO 3166-1 alpha-2][ISO 3166-1 alpha-2]. <br/><br/>`countryCode` is one of `['AL', 'AR', 'AT', 'AU', 'BE', 'BG', 'BO', 'BR', 'BY', 'CA', 'CH', 'CL', 'CO', 'CR', 'CY', 'CZ', 'DE', 'DK', 'DO', 'EC', 'EE', 'EL', 'ES', 'FI', 'FR', 'GB', 'GT', 'HN', 'HR', 'HU', 'ID', 'IE', 'IL', 'IN', 'IS', 'IT', 'KZ', 'LT', 'LU', 'LV', 'MK', 'MT', 'MX', 'NG', 'NI', 'NL', 'NO', 'NZ', 'PA', 'PE', 'PH', 'PL', 'PT', 'PY', 'RO', 'RS', 'RU', 'SA', 'SE', 'SI', 'SK', 'SM', 'SV', 'TR', 'UA', 'UY', 'UZ', 'VE']`. **isWhitelisted(str, chars)** | check if the string consists only of characters that appear in the whitelist `chars`. **matches(str, pattern [, modifiers])** | check if the string matches the pattern.<br/><br/>Either `matches('foo', /foo/i)` or `matches('foo', 'foo', 'i')`. +**isCPF(str)** | check if the string follows the rules defined by the Brazilian Federal Revenue Service for CPF verification digit calculation. ## Sanitizers diff --git a/src/index.js b/src/index.js index c47674595..20baffb54 100644 --- a/src/index.js +++ b/src/index.js @@ -130,6 +130,8 @@ import isStrongPassword from './lib/isStrongPassword'; import isVAT from './lib/isVAT'; +import isCPF from './lib/isCPF'; + const version = '13.15.0'; const validator = { @@ -245,6 +247,7 @@ const validator = { isLicensePlate, isVAT, ibanLocales, + isCPF, }; export default validator; diff --git a/src/lib/isCPF.js b/src/lib/isCPF.js new file mode 100644 index 000000000..d9ddf789f --- /dev/null +++ b/src/lib/isCPF.js @@ -0,0 +1,52 @@ +import assertString from './util/assertString'; + +const isMask = /[.-]/g; +const repeatedDigitsRegex = /^(\d)\1{10}$/; + +export default function isCPF(cpf) { + assertString(cpf); + + let cleanedCPF = String(cpf); + + if (isMask.test(cleanedCPF)) { + cleanedCPF = cleanedCPF.replace(isMask, ''); + } + + if (cleanedCPF.length !== 11) return false; + if (repeatedDigitsRegex.test(cleanedCPF)) return false; + + const paramD1 = { + 10: 0, 9: 1, 8: 2, 7: 3, 6: 4, 5: 5, 4: 6, 3: 7, 2: 8, + }; + const paramD2 = { + 11: 0, 10: 1, 9: 2, 8: 3, 7: 4, 6: 5, 5: 6, 4: 7, 3: 8, 2: 9, + }; + let firstNineCharacters = cleanedCPF.slice(0, 9); + let d1 = 0; + let d2 = 0; + + for (let i = 10; i >= 2; i--) { + if (Number.isNaN(Number(firstNineCharacters[paramD1[i]]))) return false; + d1 += Number(firstNineCharacters[paramD1[i]]) * i; + } + + if (d1 % 11 < 2) { + d1 = 0; + } else { + d1 = 11 - (d1 % 11); + } + + firstNineCharacters += String(d1); + + for (let i = 11; i >= 2; i--) { + d2 += Number(firstNineCharacters[paramD2[i]]) * i; + } + + if (d2 % 11 < 2) { + d2 = 0; + } else { + d2 = 11 - (d2 % 11); + } + + return cleanedCPF.slice(-2) === `${d1}${d2}`; +} diff --git a/test/validators.test.js b/test/validators.test.js index d6e948a41..e8dcf2b00 100644 --- a/test/validators.test.js +++ b/test/validators.test.js @@ -15440,4 +15440,63 @@ describe('Validators', () => { ], }); }); + it('should validate a CPF', () => { + test({ + validator: 'isCPF', + valid: [ + '157.099.548-61', + '55734947598', + '601.425.558-35', + '54945762139', + '367.181.238-97', + '43849941922', + '979.893.929-84', + '98273014037', + '666.030.947-03', + '40668005130', + '506.999.755-65', + '98798876465', + '816.611.889-07', + '52991780002', + '037.039.807-61', + '62281573370', + '055.751.255-72', + '03085770860', + '286.860.021-29', + '24234579793', + ], + invalid: [ + '296.231.440-69', + '62424843039', + '477.887.094-96', + '41526890821', + '861.803.242-00', + '57751926579', + '034.928.141-63', + '66437339822', + '178.714.111-57', + '27507501358', + '769.259.949-19', + '43661169739', + '646.438.057-95', + '49403410474', + '113.512.907-25', + '81322401108', + '458.825.895-23', + '92472311425', + '585.251.576-48', + '00002283074', + '796.940.600-52', + '08228682581', + '906.765.489-12', + '31437427245', + '045.504.051-45', + '21383151886', + '519.213.751-90', + '06712261581', + '884.593.736-07', + '42188048376', + ], + }); + }); });