UNPKG

hs-password-validator

Version:
426 lines (353 loc) 12.5 kB
import LibPasswordValidator from 'password-validator'; import { pipe, defaultTo, cond, equals, gt, lte, reverse } from 'ramda'; var SCORE_EMPTY = 0; var SCORE_WORST = 28; var SCORE_BAD = 35; var SCORE_WEAK = 59; var SCORE_GOOD = 85; var REQ_CHAR = 0; var MULT_MID_CHAR = 2; var MULT_CONSEC_ALPHA_UC = 2; var MULT_CONSEC_ALPHA_LC = 2; var MULT_CONSEC_NUMBER = 2; var MULT_SEQ_ALPHA = 3; var MULT_SEQ_NUMBER = 3; var MULT_SEQ_SYMBOL = 3; var MULT_LENGHT = 4; var MULT_NUMBER = 4; var MULT_SYMBOL = 6; var ALPHAS = 'abcdefghijklmnopqrstuvwxyz'; var NUMERICS = '01234567890'; var SYMBOLS = ')!@#$%^&*()'; var MIN_PWD_LEN = 8; // Used reference http://www.passwordmeter.com/ function checkPass(pwd) { var score = 0; var length = 0; var alphaUC = 0; var alphaLC = 0; var number = 0; var symbol = 0; var midChar = 0; var requirements = 0; var unqChar = 0; var repChar = 0; var repInc = 0; var consecAlphaUC = 0; var consecAlphaLC = 0; var consecNumber = 0; var seqAlpha = 0; var seqNumber = 0; var seqSymbol = 0; var minReqChars = 4; var tmpAlphaUC = ''; var tmpAlphaLC = ''; var tmpNumber = ''; if (pwd) { score = pwd.length * MULT_LENGHT; length = pwd.length; var arrPwd = pwd.replace(/\s+/g, '').split(/\s*/); var arrPwdLen = arrPwd.length; /* * Loop through password to check for Symbol, * Numeric, Lowercase and Uppercase pattern matches */ for (var a = 0; a < arrPwdLen; a++) { if (arrPwd[a].match(/[A-Z]/g)) { if (tmpAlphaUC !== '') { if (tmpAlphaUC === arrPwd[a]) { consecAlphaUC++; } } tmpAlphaUC = arrPwd[a]; alphaUC++; } else if (arrPwd[a].match(/[a-z]/g)) { if (tmpAlphaLC !== '') { if (tmpAlphaLC === arrPwd[a]) { consecAlphaLC++; } } tmpAlphaLC = arrPwd[a]; alphaLC++; } else if (arrPwd[a].match(/[0-9]/g)) { if (a > 0 && a < arrPwdLen - 1) { midChar++; } if (tmpNumber !== '') { if (tmpNumber === arrPwd[a]) { consecNumber++; } } tmpNumber = arrPwd[a]; number++; } else if (arrPwd[a].match(/[^a-zA-Z0-9_]/g)) { if (a > 0 && a < arrPwdLen - 1) { midChar++; } symbol++; } /* Internal loop through password to check for repeat characters */ var bCharExists = false; for (var b = 0; b < arrPwdLen; b++) { /* Repeat character exists */ if (arrPwd[a] === arrPwd[b] && a !== b) { bCharExists = true; /* * Calculate icrement deduction based on * proximity to identical characters * Deduction is incremented each time * a new match is discovered * Deduction amount is based on total * password length divided by the * difference of distance between currently selected match */ repInc += Math.abs(arrPwdLen / (b - a)); } } if (bCharExists) { repChar++; unqChar = arrPwdLen - repChar; repInc = unqChar ? Math.ceil(repInc / unqChar) : Math.ceil(repInc); } } /* Check for sequential alpha string patterns (forward and reverse) */ for (var s = 0; s < 23; s++) { var sFwd = ALPHAS.substring(s, s + 3); var sRev = reverse(sFwd); if (pwd.toLowerCase().includes(sFwd) || pwd.toLowerCase().includes(sRev)) { seqAlpha++; } } /* Check for sequential numeric string patterns (forward and reverse) */ for (var _s = 0; _s < 8; _s++) { var _sFwd = NUMERICS.substring(_s, _s + 3); var _sRev = reverse(_sFwd); if (pwd.toLowerCase().includes(_sFwd) || pwd.toLowerCase().includes(_sRev)) { seqNumber++; } } /* Check for sequential symbol string patterns (forward and reverse) */ for (var _s2 = 0; _s2 < 8; _s2++) { var _sFwd2 = SYMBOLS.substring(_s2, _s2 + 3); var _sRev2 = reverse(_sFwd2); if (pwd.toLowerCase().includes(_sFwd2) || pwd.toLowerCase().includes(_sRev2)) { seqSymbol++; } } /* Modify overall score value based on usage vs requirements */ /* General point assignment */ if (alphaUC > 0 && alphaUC < length) { score = score + (length - alphaUC) * 2; } if (alphaLC > 0 && alphaLC < length) { score = score + (length - alphaLC) * 2; } if (number > 0 && number < length) { score = score + number * MULT_NUMBER; } if (symbol > 0) { score = score + symbol * MULT_SYMBOL; } if (midChar > 0) { score = score + midChar * MULT_MID_CHAR; } /* Point deductions for poor practices */ // Only Letters if ((alphaLC > 0 || alphaUC > 0) && symbol === 0 && number === 0) { score = score - length; } // Only Numbers if (alphaLC === 0 && alphaUC === 0 && symbol === 0 && number > 0) { score = score - length; } if (repChar > 0) { // Same character exists more than once score = score - repInc; } if (consecAlphaUC > 0) { // Consecutive Uppercase Letters exist score = score - consecAlphaUC * MULT_CONSEC_ALPHA_UC; } if (consecAlphaLC > 0) { // Consecutive Lowercase Letters exist score = score - consecAlphaLC * MULT_CONSEC_ALPHA_LC; } if (consecNumber > 0) { // Consecutive Numbers exist score = score - consecNumber * MULT_CONSEC_NUMBER; } // Sequential alpha strings exist (3 characters or more) if (seqAlpha > 0) { score = score - seqAlpha * MULT_SEQ_ALPHA; } // Sequential numeric strings exist (3 characters or more) if (seqNumber > 0) { score = score - seqNumber * MULT_SEQ_NUMBER; } // Sequential symbol strings exist (3 characters or more) if (seqSymbol > 0) { score = score - seqSymbol * MULT_SEQ_SYMBOL; } requirements = REQ_CHAR; if (pwd.length >= MIN_PWD_LEN) { minReqChars = 3; } // One or more required characters exist if (requirements > minReqChars) { score = score + requirements * 2; } if (score < 0) { return 0; } return score; } return 0; } var getStrength = /*#__PURE__*/pipe( /*#__PURE__*/defaultTo(''), checkPass, /*#__PURE__*/cond([[/*#__PURE__*/equals(SCORE_EMPTY), function (score) { return [score, 'empty', '']; }], [/*#__PURE__*/gt(SCORE_WORST), function (score) { return [score, 'worst', 'Please create a stronger password!']; }], [/*#__PURE__*/gt(SCORE_BAD), function (score) { return [score, 'bad', 'Please create a stronger password!']; }], [/*#__PURE__*/gt(SCORE_WEAK), function (score) { return [score, 'weak', 'Good password, but can be better!']; }], [/*#__PURE__*/gt(SCORE_GOOD), function (score) { return [score, 'good', 'Very good!']; }], [/*#__PURE__*/lte(SCORE_GOOD), function (score) { return [score, 'strong', 'Excellent!']; }]])); var messages = { "en-US": { min: "Must contain as least {0} characters", max: "Must contain as most {0} characters", uppercase: "At least one uppercase letter", lowercase: "At least one lowercase letter", space: "Can not contain spaces", symbol: "At least one special character", number: "Must contain numbers", hasSequential: "Must not contain sequential characters", strength: "Password strength: {0}" }, "pt-BR": { min: "Deve conter no mínimo {0} caracteres", max: "Deve conter no máximo {0} caracteres", uppercase: "Pelo menos uma letra maiúscula", lowercase: "Pelo menos uma letra minúscula", space: "Não pode conter espaços", symbol: "Pelo menos um caractere especial", number: "Deve conter números", hasSequential: "Não pode conter sequências de caracteres", strength: "Força da senha: {0}" } }; var StringFormat = function StringFormat(str) { for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } return str.replace(/{(\d+)}/g, function (_, index) { return args[index]; }); }; var DEFAULT_MIN_LENGTH = 10; var DEFAULT_MAX_LENGTH = 128; var AVAILABLE_TAGS = ['worst', 'bad', 'weak', 'good', 'strong']; var min = function min(pw, argument) { return { validation: 'min', arguments: argument.minLength, message: StringFormat(messages[argument.lang].min, argument.minLength.toString()), satisfied: new LibPasswordValidator().min(argument.minLength).validate(pw) }; }; var max = function max(pw, argument) { return { validation: 'max', arguments: argument.maxLength, message: StringFormat(messages[argument.lang].max, argument.maxLength.toString()), satisfied: new LibPasswordValidator().max(argument.maxLength).validate(pw) }; }; var hasUppercase = function hasUppercase(pw, argument) { return { validation: 'uppercase', message: messages[argument.lang].uppercase, satisfied: new LibPasswordValidator().uppercase().validate(pw) }; }; var hasLowercase = function hasLowercase(pw, argument) { return { validation: 'lowercase', message: messages[argument.lang].lowercase, satisfied: new LibPasswordValidator().has().lowercase().validate(pw) }; }; var hasSpace = function hasSpace(pw, argument) { return { validation: 'space', message: messages[argument.lang].space, satisfied: new LibPasswordValidator().has().not().spaces().validate(pw) }; }; var hasSymbol = function hasSymbol(pw, argument) { return { validation: 'symbol', message: messages[argument.lang].symbol, satisfied: new LibPasswordValidator().has().symbols().validate(pw) }; }; var hasNumber = function hasNumber(pw, argument) { return { validation: 'number', message: messages[argument.lang].number, satisfied: new LibPasswordValidator().has().digits().validate(pw) }; }; var hasSequential = function hasSequential(pw, argument) { var regex = /(\w)\1+/; return { validation: 'sequential', message: messages[argument.lang].hasSequential, satisfied: !regex.test(pw) }; }; var passwdScore = function passwdScore(pw, argument) { var pwStrength = getStrength(pw); var isZeroScore = pwStrength[0] === 0; var tag = isZeroScore ? 'worst' : pwStrength[1]; var message = StringFormat(messages[argument.lang].strength, tag); return { validation: 'strength', tag: tag, message: message, satisfied: AVAILABLE_TAGS.indexOf(tag) >= AVAILABLE_TAGS.indexOf(argument.minAcceptable) }; }; var isValidInput = function isValidInput(errors) { return errors.some(function (error) { return error.satisfied === false; }); }; var casesMap = /*#__PURE__*/new Map([['min', min], ['max', max], ['uppercase', hasUppercase], ['lowercase', hasLowercase], ['symbol', hasSymbol], ['number', hasNumber], ['space', hasSpace], ['sequential', hasSequential], ['strength', passwdScore]]); var PasswordValidator = function PasswordValidator(_ref) { var _config$length$minLen, _config$length, _config$length$maxLen, _config$length2, _config$scoreConfig$m, _config$scoreConfig, _config$lang; var password = _ref.password, options = _ref.options, config = _ref.config; var validationConfig = { minLength: (_config$length$minLen = config == null ? void 0 : (_config$length = config.length) == null ? void 0 : _config$length.minLength) != null ? _config$length$minLen : DEFAULT_MIN_LENGTH, maxLength: (_config$length$maxLen = config == null ? void 0 : (_config$length2 = config.length) == null ? void 0 : _config$length2.maxLength) != null ? _config$length$maxLen : DEFAULT_MAX_LENGTH, minAcceptable: (_config$scoreConfig$m = config == null ? void 0 : (_config$scoreConfig = config.scoreConfig) == null ? void 0 : _config$scoreConfig.minAcceptable) != null ? _config$scoreConfig$m : 'strong', lang: (_config$lang = config == null ? void 0 : config.lang) != null ? _config$lang : 'en-US' }; var result = []; if (options) { options.forEach(function (option) { var validation = casesMap.get(option); validation && result.push(validation(password, validationConfig)); }); } else { casesMap.forEach(function (value) { result.push(value(password, validationConfig)); }); } return { hasInvalidFields: isValidInput(result), data: result }; }; export default PasswordValidator; //# sourceMappingURL=hs-password-validator.esm.js.map