hs-password-validator
Version:
> A complete and comprehensive password validator
426 lines (353 loc) • 12.5 kB
JavaScript
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