UNPKG

zxcvbn-typescript

Version:

realistic password strength estimation, updated and ported to Typescript from Dan Wheeler's zxcvbn

242 lines 9.63 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __spreadArrays = (this && this.__spreadArrays) || function () { for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; for (var r = Array(s), k = 0, i = 0; i < il; i++) for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) r[k] = a[j]; return r; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.l33t_match = exports.enumerate_l33t_subs = exports.relevant_l33t_subtable = exports.set_user_input_dictionary = exports.reverse_dictionary_match = exports.dictionary_match = void 0; var frequency_lists_1 = __importDefault(require("../frequency_lists")); //------------------------------------------------------------------------------- // dictionary match (common passwords, english, last names, etc) ---------------- //------------------------------------------------------------------------------- var support_1 = require("./support"); var RANKED_DICTIONARIES = {}; function build_ranked_dictionary(ordered_list) { var result = {}; var i = 1; // rank starts at 1, not 0 for (var _i = 0, ordered_list_1 = ordered_list; _i < ordered_list_1.length; _i++) { var word = ordered_list_1[_i]; result[word] = i; i += 1; } return result; } for (var name_1 in frequency_lists_1.default) { var lst = frequency_lists_1.default[name_1]; RANKED_DICTIONARIES[name_1] = build_ranked_dictionary(lst); } /** * Attempts to match a string with a ranked dictionary of words. * * @param password - The string to examine * @param _ranked_dictionaries - For unit testing only: allows overriding the available dictionaries */ function dictionary_match(password, _ranked_dictionaries) { if (_ranked_dictionaries === void 0) { _ranked_dictionaries = RANKED_DICTIONARIES; } var matches = []; var password_lower = password.toLowerCase(); for (var dictionary_name in _ranked_dictionaries) { var ranked_dictionary = _ranked_dictionaries[dictionary_name]; for (var i = 0; i < password.length; i++) { for (var j = i; j < password.length; j++) { if (password_lower.slice(i, j + 1) in ranked_dictionary) { var word = password_lower.slice(i, j + 1); var rank = ranked_dictionary[word]; matches.push({ pattern: "dictionary", i: i, j: j, token: password.slice(i, j + 1), matched_word: word, rank: rank, dictionary_name: dictionary_name, reversed: false, l33t: false, }); } } } } return support_1.sorted(matches); } exports.dictionary_match = dictionary_match; /** * Attempts to match a string with a ranked dictionary of words after it is reversed. * * @param password - The string to examine * @param _ranked_dictionaries - For unit testing only: allows overriding the available dictionaries */ function reverse_dictionary_match(password, _ranked_dictionaries) { if (_ranked_dictionaries === void 0) { _ranked_dictionaries = RANKED_DICTIONARIES; } var reversed_password = password.split("").reverse().join(""); return dictionary_match(reversed_password, _ranked_dictionaries) .map(function (m) { var newM = __assign({}, m); newM.i = password.length - 1 - m.j; newM.j = password.length - 1 - m.i; newM.token = m.token.split("").reverse().join(""); // reverse back newM.reversed = true; return newM; }) .sort(function (m1, m2) { return m1.i - m2.i || m1.j - m2.j; }); } exports.reverse_dictionary_match = reverse_dictionary_match; /** * Adds a user provided dictionary as a user_inputs dictionary. * @param ordered_list The list to add as a dictionary. */ function set_user_input_dictionary(ordered_list) { RANKED_DICTIONARIES["user_inputs"] = build_ranked_dictionary(__spreadArrays(ordered_list)); } exports.set_user_input_dictionary = set_user_input_dictionary; /** * Prunes a copy of a l33t_table to only include the substitutions of interest. * @param password The password to consider * @param table The table to prune. */ function relevant_l33t_subtable(password, table) { var password_chars = new Set(password.split("")); var subtable = {}; for (var letter in table) { var relevant_subs = table[letter].filter(function (sub) { return password_chars.has(sub); }); if (relevant_subs.length > 0) { subtable[letter] = relevant_subs; } } return subtable; } exports.relevant_l33t_subtable = relevant_l33t_subtable; function dedup(subs) { var deduped = []; var members = new Set(); for (var _i = 0, subs_1 = subs; _i < subs_1.length; _i++) { var sub = subs_1[_i]; var label = sub .map(function (k, v) { return [k, v]; }) .sort() .map(function (k, v) { return k + "," + v; }) .join("-"); if (!members.has(label)) { members.add(label); deduped.push(sub); } } return deduped; } function helper(keys, table, subs) { if (subs === void 0) { subs = [[]]; } if (!keys.length) return subs; var first_key = keys[0], rest_keys = keys.slice(1); var next_subs = []; var _loop_1 = function (l33t_chr) { for (var _i = 0, subs_2 = subs; _i < subs_2.length; _i++) { var sub = subs_2[_i]; var dup_l33t_index = sub.findIndex(function (s) { return s[0] === l33t_chr; }); if (dup_l33t_index !== -1) next_subs.push(sub); next_subs.push(__spreadArrays(sub, [[l33t_chr, first_key]])); } }; for (var _i = 0, _a = table[first_key]; _i < _a.length; _i++) { var l33t_chr = _a[_i]; _loop_1(l33t_chr); } subs = dedup(next_subs); return helper(rest_keys, table, subs); } /** * Returns the list of possible l33t replacement dictionaries for a given password. * @param table The table to create l33t substitutions for. */ function enumerate_l33t_subs(table) { return helper(Object.keys(table), table).map(function (s) { var sub_dictionary = {}; for (var _i = 0, s_1 = s; _i < s_1.length; _i++) { var _a = s_1[_i], l33t_chr = _a[0], chr = _a[1]; sub_dictionary[l33t_chr] = chr; } return sub_dictionary; }); } exports.enumerate_l33t_subs = enumerate_l33t_subs; var L33T_TABLE = { a: ["4", "@"], b: ["8"], c: ["(", "{", "[", "<"], e: ["3"], g: ["6", "9"], i: ["1", "!", "|"], l: ["1", "|", "7"], o: ["0"], s: ["$", "5"], t: ["+", "7"], x: ["%"], z: ["2"], }; //------------------------------------------------------------------------------- // dictionary match with common l33t substitutions ------------------------------ //------------------------------------------------------------------------------- function l33t_match(password, _ranked_dictionaries, _l33t_table) { if (_ranked_dictionaries === void 0) { _ranked_dictionaries = RANKED_DICTIONARIES; } if (_l33t_table === void 0) { _l33t_table = L33T_TABLE; } var matches = []; for (var _i = 0, _a = enumerate_l33t_subs(relevant_l33t_subtable(password, _l33t_table)); _i < _a.length; _i++) { var sub = _a[_i]; if (support_1.empty(sub)) { break; // corner case: password has no relevant subs. } var subbed_password = support_1.translate(password, sub); var _loop_2 = function (match) { var token = password.slice(match.i, match.j + 1); if (token.toLowerCase() === match.matched_word) { return "continue"; } var match_sub = {}; // subset of mappings in sub that are in use for this match for (var subbed_chr in sub) { var chr = sub[subbed_chr]; if (token.indexOf(subbed_chr) !== -1) { match_sub[subbed_chr] = chr; } } match.l33t = true; match.token = token; match.sub = match_sub; match.sub_display = Object.keys(match_sub) .map(function (k) { return k + " -> " + match_sub[k]; }) .join(", "); matches.push(match); }; for (var _b = 0, _c = dictionary_match(subbed_password, _ranked_dictionaries); _b < _c.length; _b++) { var match = _c[_b]; _loop_2(match); } } return support_1.sorted(matches.filter(function (match // filter single-character l33t matches to reduce noise. ) { // otherwise '1' matches 'i', '4' matches 'a', both very common English words // with low dictionary rank. return match.token.length > 1; })); } exports.l33t_match = l33t_match; //# sourceMappingURL=dictionary_match.js.map