Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@zxcvbn-ts/core

Package Overview
Dependencies
Maintainers
1
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@zxcvbn-ts/core - npm Package Compare versions

Comparing version 3.0.4 to 4.0.0-beta.0

dist/utils/debounce.d.ts

31

dist/data/dateSplits.esm.js
var dateSplits = {
4: [
// for length-4 strings, eg 1191 or 9111, two ways to split:
[1, 2], [2, 3] // 91 1 1
[1, 2],
// 1 1 91 (2nd split starts at index 1, 3rd at index 2)
[2, 3] // 91 1 1
],
5: [[1, 3], [2, 3],
5: [[1, 3],
// 1 11 91
[2, 3],
// 11 1 91
// [2, 3], // 91 1 11 <- duplicate previous one
[2, 4] // 91 11 1 <- New and must be added as bug fix
],
6: [[1, 2], [2, 4], [4, 5] // 1991 1 1
6: [[1, 2],
// 1 1 1991
[2, 4],
// 11 11 91
[4, 5] // 1991 1 1
],
// 1111991
7: [[1, 3], [2, 3], [4, 5], [4, 6] // 1991 11 1
7: [[1, 3],
// 1 11 1991
[2, 3],
// 11 1 1991
[4, 5],
// 1991 1 11
[4, 6] // 1991 11 1
],
8: [[2, 4], [4, 6] // 1991 11 11
8: [[2, 4],
// 11 11 1991
[4, 6] // 1991 11 11
]

@@ -21,0 +34,0 @@ };

@@ -6,18 +6,31 @@ 'use strict';

// for length-4 strings, eg 1191 or 9111, two ways to split:
[1, 2], [2, 3] // 91 1 1
[1, 2],
// 1 1 91 (2nd split starts at index 1, 3rd at index 2)
[2, 3] // 91 1 1
],
5: [[1, 3], [2, 3],
5: [[1, 3],
// 1 11 91
[2, 3],
// 11 1 91
// [2, 3], // 91 1 11 <- duplicate previous one
[2, 4] // 91 11 1 <- New and must be added as bug fix
],
6: [[1, 2], [2, 4], [4, 5] // 1991 1 1
6: [[1, 2],
// 1 1 1991
[2, 4],
// 11 11 91
[4, 5] // 1991 1 1
],
// 1111991
7: [[1, 3], [2, 3], [4, 5], [4, 6] // 1991 11 1
7: [[1, 3],
// 1 11 1991
[2, 3],
// 11 1 1991
[4, 5],
// 1991 1 11
[4, 6] // 1991 11 1
],
8: [[2, 4], [4, 6] // 1991 11 11
8: [[2, 4],
// 11 11 1991
[4, 6] // 1991 11 11
]

@@ -24,0 +37,0 @@ };

@@ -1,14 +0,13 @@

import { DefaultFeedbackFunction, FeedbackType, MatchEstimated } from './types';
type Matchers = {
[key: string]: DefaultFeedbackFunction;
};
import Options from './Options';
import { FeedbackType, MatchEstimated } from './types';
declare class Feedback {
readonly matchers: Matchers;
defaultFeedback: FeedbackType;
constructor();
setDefaultSuggestions(): void;
private options;
private readonly matchers;
private defaultFeedback;
constructor(options: Options);
private setDefaultSuggestions;
getFeedback(score: number, sequence: MatchEstimated[]): FeedbackType;
getLongestMatch(sequence: MatchEstimated[]): MatchEstimated;
getMatchFeedback(match: MatchEstimated, isSoleMatch: boolean): FeedbackType | null;
private getLongestMatch;
private getMatchFeedback;
}
export default Feedback;

@@ -1,2 +0,1 @@

import { zxcvbnOptions } from './Options.esm.js';
import bruteforceMatcher from './matcher/bruteforce/feedback.esm.js';

@@ -21,3 +20,4 @@ import dateMatcher from './matcher/date/feedback.esm.js';

class Feedback {
constructor() {
constructor(options) {
this.options = options;
this.matchers = {

@@ -40,3 +40,3 @@ bruteforce: bruteforceMatcher,

setDefaultSuggestions() {
this.defaultFeedback.suggestions.push(zxcvbnOptions.translations.suggestions.useWords, zxcvbnOptions.translations.suggestions.noNeed);
this.defaultFeedback.suggestions.push(this.options.translations.suggestions.useWords, this.options.translations.suggestions.noNeed);
}

@@ -50,3 +50,3 @@ getFeedback(score, sequence) {

}
const extraFeedback = zxcvbnOptions.translations.suggestions.anotherWord;
const extraFeedback = this.options.translations.suggestions.anotherWord;
const longestMatch = this.getLongestMatch(sequence);

@@ -76,6 +76,6 @@ let feedback = this.getMatchFeedback(longestMatch, sequence.length === 1);

if (this.matchers[match.pattern]) {
return this.matchers[match.pattern](match, isSoleMatch);
return this.matchers[match.pattern](this.options, match, isSoleMatch);
}
if (zxcvbnOptions.matchers[match.pattern] && 'feedback' in zxcvbnOptions.matchers[match.pattern]) {
return zxcvbnOptions.matchers[match.pattern].feedback(match, isSoleMatch);
if (this.options.matchers[match.pattern] && 'feedback' in this.options.matchers[match.pattern]) {
return this.options.matchers[match.pattern].feedback(this.options, match, isSoleMatch);
}

@@ -82,0 +82,0 @@ return defaultFeedback;

'use strict';
var Options = require('./Options.js');
var feedback = require('./matcher/bruteforce/feedback.js');

@@ -23,3 +22,4 @@ var feedback$1 = require('./matcher/date/feedback.js');

class Feedback {
constructor() {
constructor(options) {
this.options = options;
this.matchers = {

@@ -42,3 +42,3 @@ bruteforce: feedback,

setDefaultSuggestions() {
this.defaultFeedback.suggestions.push(Options.zxcvbnOptions.translations.suggestions.useWords, Options.zxcvbnOptions.translations.suggestions.noNeed);
this.defaultFeedback.suggestions.push(this.options.translations.suggestions.useWords, this.options.translations.suggestions.noNeed);
}

@@ -52,3 +52,3 @@ getFeedback(score, sequence) {

}
const extraFeedback = Options.zxcvbnOptions.translations.suggestions.anotherWord;
const extraFeedback = this.options.translations.suggestions.anotherWord;
const longestMatch = this.getLongestMatch(sequence);

@@ -78,6 +78,6 @@ let feedback = this.getMatchFeedback(longestMatch, sequence.length === 1);

if (this.matchers[match.pattern]) {
return this.matchers[match.pattern](match, isSoleMatch);
return this.matchers[match.pattern](this.options, match, isSoleMatch);
}
if (Options.zxcvbnOptions.matchers[match.pattern] && 'feedback' in Options.zxcvbnOptions.matchers[match.pattern]) {
return Options.zxcvbnOptions.matchers[match.pattern].feedback(match, isSoleMatch);
if (this.options.matchers[match.pattern] && 'feedback' in this.options.matchers[match.pattern]) {
return this.options.matchers[match.pattern].feedback(this.options, match, isSoleMatch);
}

@@ -84,0 +84,0 @@ return defaultFeedback;

@@ -1,7 +0,15 @@

import { zxcvbnOptions, Options } from './Options';
import debounce from './debounce';
import { ZxcvbnResult } from './types';
export declare const zxcvbn: (password: string, userInputs?: (string | number)[]) => ZxcvbnResult;
export declare const zxcvbnAsync: (password: string, userInputs?: (string | number)[]) => Promise<ZxcvbnResult>;
import debounce from './utils/debounce';
import { Matcher, OptionsType, ZxcvbnResult } from './types';
declare class ZxcvbnFactory {
private options;
private scoring;
constructor(options?: OptionsType, customMatchers?: Record<string, Matcher>);
private estimateAttackTimes;
private getFeedback;
private createReturnValue;
private main;
check(password: string, userInputs?: (string | number)[]): ZxcvbnResult;
checkAsync(password: string, userInputs?: (string | number)[]): Promise<ZxcvbnResult>;
}
export * from './types';
export { zxcvbnOptions, Options, debounce };
export { ZxcvbnFactory, debounce };
import Matching from './Matching.esm.js';
import scoring from './scoring/index.esm.js';
import TimeEstimates from './TimeEstimates.esm.js';
import { TimeEstimates } from './TimeEstimates.esm.js';
import Feedback from './Feedback.esm.js';
import { zxcvbnOptions } from './Options.esm.js';
export { Options } from './Options.esm.js';
export { default as debounce } from './debounce.esm.js';
import Options from './Options.esm.js';
export { default as debounce } from './utils/debounce.esm.js';
import Scoring from './scoring/index.esm.js';
const time = () => new Date().getTime();
const createReturnValue = (resolvedMatches, password, start) => {
const feedback = new Feedback();
const timeEstimates = new TimeEstimates();
const matchSequence = scoring.mostGuessableMatchSequence(password, resolvedMatches);
const calcTime = time() - start;
const attackTimes = timeEstimates.estimateAttackTimes(matchSequence.guesses);
return {
calcTime,
...matchSequence,
...attackTimes,
feedback: feedback.getFeedback(attackTimes.score, matchSequence.sequence)
};
};
const main = (password, userInputs) => {
if (userInputs) {
zxcvbnOptions.extendUserInputsDictionary(userInputs);
class ZxcvbnFactory {
constructor(options = {}, customMatchers = {}) {
this.options = new Options(options, customMatchers);
this.scoring = new Scoring(this.options);
}
const matching = new Matching();
return matching.match(password);
};
const zxcvbn = (password, userInputs) => {
const start = time();
const matches = main(password, userInputs);
if (matches instanceof Promise) {
throw new Error('You are using a Promised matcher, please use `zxcvbnAsync` for it.');
estimateAttackTimes(guesses) {
const timeEstimates = new TimeEstimates(this.options);
return timeEstimates.estimateAttackTimes(guesses);
}
return createReturnValue(matches, password, start);
};
const zxcvbnAsync = async (password, userInputs) => {
const usedPassword = password.substring(0, zxcvbnOptions.maxLength);
const start = time();
const matches = await main(usedPassword, userInputs);
return createReturnValue(matches, usedPassword, start);
};
getFeedback(score, sequence) {
const feedback = new Feedback(this.options);
return feedback.getFeedback(score, sequence);
}
createReturnValue(resolvedMatches, password, start) {
const matchSequence = this.scoring.mostGuessableMatchSequence(password, resolvedMatches);
const calcTime = time() - start;
const attackTimes = this.estimateAttackTimes(matchSequence.guesses);
return {
calcTime,
...matchSequence,
...attackTimes,
feedback: this.getFeedback(attackTimes.score, matchSequence.sequence)
};
}
main(password, userInputs) {
const userInputsOptions = this.options.getUserInputsOptions(userInputs);
const matching = new Matching(this.options);
return matching.match(password, userInputsOptions);
}
check(password, userInputs) {
const reducedPassword = password.substring(0, this.options.maxLength);
const start = time();
const matches = this.main(reducedPassword, userInputs);
if (matches instanceof Promise) {
throw new Error('You are using a Promised matcher, please use `zxcvbnAsync` for it.');
}
return this.createReturnValue(matches, reducedPassword, start);
}
async checkAsync(password, userInputs) {
const reducedPassword = password.substring(0, this.options.maxLength);
const start = time();
const matches = await this.main(reducedPassword, userInputs);
return this.createReturnValue(matches, reducedPassword, start);
}
}
export { zxcvbn, zxcvbnAsync, zxcvbnOptions };
export { ZxcvbnFactory };
//# sourceMappingURL=index.esm.js.map
'use strict';
var Matching = require('./Matching.js');
var index = require('./scoring/index.js');
var TimeEstimates = require('./TimeEstimates.js');
var Feedback = require('./Feedback.js');
var Options = require('./Options.js');
var debounce = require('./debounce.js');
var debounce = require('./utils/debounce.js');
var index = require('./scoring/index.js');
const time = () => new Date().getTime();
const createReturnValue = (resolvedMatches, password, start) => {
const feedback = new Feedback();
const timeEstimates = new TimeEstimates();
const matchSequence = index.mostGuessableMatchSequence(password, resolvedMatches);
const calcTime = time() - start;
const attackTimes = timeEstimates.estimateAttackTimes(matchSequence.guesses);
return {
calcTime,
...matchSequence,
...attackTimes,
feedback: feedback.getFeedback(attackTimes.score, matchSequence.sequence)
};
};
const main = (password, userInputs) => {
if (userInputs) {
Options.zxcvbnOptions.extendUserInputsDictionary(userInputs);
class ZxcvbnFactory {
constructor(options = {}, customMatchers = {}) {
this.options = new Options(options, customMatchers);
this.scoring = new index(this.options);
}
const matching = new Matching();
return matching.match(password);
};
const zxcvbn = (password, userInputs) => {
const start = time();
const matches = main(password, userInputs);
if (matches instanceof Promise) {
throw new Error('You are using a Promised matcher, please use `zxcvbnAsync` for it.');
estimateAttackTimes(guesses) {
const timeEstimates = new TimeEstimates.TimeEstimates(this.options);
return timeEstimates.estimateAttackTimes(guesses);
}
return createReturnValue(matches, password, start);
};
const zxcvbnAsync = async (password, userInputs) => {
const usedPassword = password.substring(0, Options.zxcvbnOptions.maxLength);
const start = time();
const matches = await main(usedPassword, userInputs);
return createReturnValue(matches, usedPassword, start);
};
getFeedback(score, sequence) {
const feedback = new Feedback(this.options);
return feedback.getFeedback(score, sequence);
}
createReturnValue(resolvedMatches, password, start) {
const matchSequence = this.scoring.mostGuessableMatchSequence(password, resolvedMatches);
const calcTime = time() - start;
const attackTimes = this.estimateAttackTimes(matchSequence.guesses);
return {
calcTime,
...matchSequence,
...attackTimes,
feedback: this.getFeedback(attackTimes.score, matchSequence.sequence)
};
}
main(password, userInputs) {
const userInputsOptions = this.options.getUserInputsOptions(userInputs);
const matching = new Matching(this.options);
return matching.match(password, userInputsOptions);
}
check(password, userInputs) {
const reducedPassword = password.substring(0, this.options.maxLength);
const start = time();
const matches = this.main(reducedPassword, userInputs);
if (matches instanceof Promise) {
throw new Error('You are using a Promised matcher, please use `zxcvbnAsync` for it.');
}
return this.createReturnValue(matches, reducedPassword, start);
}
async checkAsync(password, userInputs) {
const reducedPassword = password.substring(0, this.options.maxLength);
const start = time();
const matches = await this.main(reducedPassword, userInputs);
return this.createReturnValue(matches, reducedPassword, start);
}
}
exports.Options = Options.Options;
exports.zxcvbnOptions = Options.zxcvbnOptions;
exports.debounce = debounce;
exports.zxcvbn = zxcvbn;
exports.zxcvbnAsync = zxcvbnAsync;
exports.ZxcvbnFactory = ZxcvbnFactory;
//# sourceMappingURL=index.js.map

@@ -1,2 +0,3 @@

declare const _default: () => {
import Options from '../../Options';
declare const _default: (options: Options) => {
warning: string;

@@ -3,0 +4,0 @@ suggestions: string[];

@@ -1,7 +0,5 @@

import { zxcvbnOptions } from '../../Options.esm.js';
var dateMatcher = (() => {
var dateMatcher = (options => {
return {
warning: zxcvbnOptions.translations.warnings.dates,
suggestions: [zxcvbnOptions.translations.suggestions.dates]
warning: options.translations.warnings.dates,
suggestions: [options.translations.suggestions.dates]
};

@@ -8,0 +6,0 @@ });

'use strict';
var Options = require('../../Options.js');
var dateMatcher = (() => {
var dateMatcher = (options => {
return {
warning: Options.zxcvbnOptions.translations.warnings.dates,
suggestions: [Options.zxcvbnOptions.translations.suggestions.dates]
warning: options.translations.warnings.dates,
suggestions: [options.translations.suggestions.dates]
};

@@ -10,0 +8,0 @@ });

@@ -1,6 +0,7 @@

import { DateMatch } from '../../types';
interface DateMatchOptions {
password: string;
}
import { DateMatch, MatchOptions } from '../../types';
import Options from '../../Options';
type DateMatchOptions = Pick<MatchOptions, 'password'>;
declare class MatchDate {
private options;
constructor(options: Options);
match({ password }: DateMatchOptions): import("../../types").MatchExtended[];

@@ -7,0 +8,0 @@ getMatchesWithSeparator(password: string): DateMatch[];

import { DATE_MIN_YEAR, DATE_MAX_YEAR, REFERENCE_YEAR, DATE_SPLITS } from '../../data/const.esm.js';
import { sorted } from '../../helper.esm.js';
import { sorted } from '../../utils/helper.esm.js';

@@ -10,2 +10,5 @@ /*

class MatchDate {
constructor(options) {
this.options = options;
}
/*

@@ -191,5 +194,6 @@ * a "date" is recognized as:

// first look for a four digit year: yyyy + daymonth or daymonth + yyyy
const possibleYearSplits = [[integers[2], integers.slice(0, 2)], [integers[0], integers.slice(1, 3)] // year first
const possibleYearSplits = [[integers[2], integers.slice(0, 2)],
// year last
[integers[0], integers.slice(1, 3)] // year first
];
const possibleYearSplitsLength = possibleYearSplits.length;

@@ -196,0 +200,0 @@ for (let j = 0; j < possibleYearSplitsLength; j += 1) {

'use strict';
var _const = require('../../data/const.js');
var helper = require('../../helper.js');
var helper = require('../../utils/helper.js');

@@ -12,2 +12,5 @@ /*

class MatchDate {
constructor(options) {
this.options = options;
}
/*

@@ -193,5 +196,6 @@ * a "date" is recognized as:

// first look for a four digit year: yyyy + daymonth or daymonth + yyyy
const possibleYearSplits = [[integers[2], integers.slice(0, 2)], [integers[0], integers.slice(1, 3)] // year first
const possibleYearSplits = [[integers[2], integers.slice(0, 2)],
// year last
[integers[0], integers.slice(1, 3)] // year first
];
const possibleYearSplitsLength = possibleYearSplits.length;

@@ -198,0 +202,0 @@ for (let j = 0; j < possibleYearSplitsLength; j += 1) {

@@ -0,3 +1,4 @@

import Options from '../../Options';
import { MatchEstimated } from '../../types';
declare const _default: (match: MatchEstimated, isSoleMatch?: boolean) => {
declare const _default: (options: Options, match: MatchEstimated, isSoleMatch?: boolean) => {
warning: string | null;

@@ -4,0 +5,0 @@ suggestions: string[];

@@ -1,61 +0,62 @@

import { zxcvbnOptions } from '../../Options.esm.js';
import { START_UPPER, ALL_UPPER_INVERTED } from '../../data/const.esm.js';
const getDictionaryWarningPassword = (match, isSoleMatch) => {
const getDictionaryWarningPassword = (options, match, isSoleMatch) => {
let warning = null;
if (isSoleMatch && !match.l33t && !match.reversed) {
if (match.rank <= 10) {
warning = zxcvbnOptions.translations.warnings.topTen;
warning = options.translations.warnings.topTen;
} else if (match.rank <= 100) {
warning = zxcvbnOptions.translations.warnings.topHundred;
warning = options.translations.warnings.topHundred;
} else {
warning = zxcvbnOptions.translations.warnings.common;
warning = options.translations.warnings.common;
}
} else if (match.guessesLog10 <= 4) {
warning = zxcvbnOptions.translations.warnings.similarToCommon;
warning = options.translations.warnings.similarToCommon;
}
return warning;
};
const getDictionaryWarningWikipedia = (match, isSoleMatch) => {
const getDictionaryWarningWikipedia = (options, match, isSoleMatch) => {
let warning = null;
if (isSoleMatch) {
warning = zxcvbnOptions.translations.warnings.wordByItself;
warning = options.translations.warnings.wordByItself;
}
return warning;
};
const getDictionaryWarningNames = (match, isSoleMatch) => {
const getDictionaryWarningNames = (options, match, isSoleMatch) => {
if (isSoleMatch) {
return zxcvbnOptions.translations.warnings.namesByThemselves;
return options.translations.warnings.namesByThemselves;
}
return zxcvbnOptions.translations.warnings.commonNames;
return options.translations.warnings.commonNames;
};
const getDictionaryWarning = (match, isSoleMatch) => {
let warning = null;
const getDictionaryWarning = (options, match, isSoleMatch) => {
const dictName = match.dictionaryName;
const isAName = dictName === 'lastnames' || dictName.toLowerCase().includes('firstnames');
if (dictName === 'passwords') {
warning = getDictionaryWarningPassword(match, isSoleMatch);
} else if (dictName.includes('wikipedia')) {
warning = getDictionaryWarningWikipedia(match, isSoleMatch);
} else if (isAName) {
warning = getDictionaryWarningNames(match, isSoleMatch);
} else if (dictName === 'userInputs') {
warning = zxcvbnOptions.translations.warnings.userInputs;
const isAName = dictName.toLowerCase().includes('lastnames') || dictName.toLowerCase().includes('firstnames') || dictName.toLowerCase().includes('names');
if (dictName.includes('passwords')) {
return getDictionaryWarningPassword(options, match, isSoleMatch);
}
return warning;
if (dictName.includes('wikipedia')) {
return getDictionaryWarningWikipedia(options, match, isSoleMatch);
}
if (isAName) {
return getDictionaryWarningNames(options, match, isSoleMatch);
}
if (dictName.includes('userInputs')) {
return options.translations.warnings.userInputs;
}
return null;
};
var dictionaryMatcher = ((match, isSoleMatch) => {
const warning = getDictionaryWarning(match, isSoleMatch);
var dictionaryMatcher = ((options, match, isSoleMatch) => {
const warning = getDictionaryWarning(options, match, isSoleMatch);
const suggestions = [];
const word = match.token;
if (word.match(START_UPPER)) {
suggestions.push(zxcvbnOptions.translations.suggestions.capitalization);
suggestions.push(options.translations.suggestions.capitalization);
} else if (word.match(ALL_UPPER_INVERTED) && word.toLowerCase() !== word) {
suggestions.push(zxcvbnOptions.translations.suggestions.allUppercase);
suggestions.push(options.translations.suggestions.allUppercase);
}
if (match.reversed && match.token.length >= 4) {
suggestions.push(zxcvbnOptions.translations.suggestions.reverseWords);
suggestions.push(options.translations.suggestions.reverseWords);
}
if (match.l33t) {
suggestions.push(zxcvbnOptions.translations.suggestions.l33t);
suggestions.push(options.translations.suggestions.l33t);
}

@@ -62,0 +63,0 @@ return {

'use strict';
var Options = require('../../Options.js');
var _const = require('../../data/const.js');
const getDictionaryWarningPassword = (match, isSoleMatch) => {
const getDictionaryWarningPassword = (options, match, isSoleMatch) => {
let warning = null;
if (isSoleMatch && !match.l33t && !match.reversed) {
if (match.rank <= 10) {
warning = Options.zxcvbnOptions.translations.warnings.topTen;
warning = options.translations.warnings.topTen;
} else if (match.rank <= 100) {
warning = Options.zxcvbnOptions.translations.warnings.topHundred;
warning = options.translations.warnings.topHundred;
} else {
warning = Options.zxcvbnOptions.translations.warnings.common;
warning = options.translations.warnings.common;
}
} else if (match.guessesLog10 <= 4) {
warning = Options.zxcvbnOptions.translations.warnings.similarToCommon;
warning = options.translations.warnings.similarToCommon;
}
return warning;
};
const getDictionaryWarningWikipedia = (match, isSoleMatch) => {
const getDictionaryWarningWikipedia = (options, match, isSoleMatch) => {
let warning = null;
if (isSoleMatch) {
warning = Options.zxcvbnOptions.translations.warnings.wordByItself;
warning = options.translations.warnings.wordByItself;
}
return warning;
};
const getDictionaryWarningNames = (match, isSoleMatch) => {
const getDictionaryWarningNames = (options, match, isSoleMatch) => {
if (isSoleMatch) {
return Options.zxcvbnOptions.translations.warnings.namesByThemselves;
return options.translations.warnings.namesByThemselves;
}
return Options.zxcvbnOptions.translations.warnings.commonNames;
return options.translations.warnings.commonNames;
};
const getDictionaryWarning = (match, isSoleMatch) => {
let warning = null;
const getDictionaryWarning = (options, match, isSoleMatch) => {
const dictName = match.dictionaryName;
const isAName = dictName === 'lastnames' || dictName.toLowerCase().includes('firstnames');
if (dictName === 'passwords') {
warning = getDictionaryWarningPassword(match, isSoleMatch);
} else if (dictName.includes('wikipedia')) {
warning = getDictionaryWarningWikipedia(match, isSoleMatch);
} else if (isAName) {
warning = getDictionaryWarningNames(match, isSoleMatch);
} else if (dictName === 'userInputs') {
warning = Options.zxcvbnOptions.translations.warnings.userInputs;
const isAName = dictName.toLowerCase().includes('lastnames') || dictName.toLowerCase().includes('firstnames') || dictName.toLowerCase().includes('names');
if (dictName.includes('passwords')) {
return getDictionaryWarningPassword(options, match, isSoleMatch);
}
return warning;
if (dictName.includes('wikipedia')) {
return getDictionaryWarningWikipedia(options, match, isSoleMatch);
}
if (isAName) {
return getDictionaryWarningNames(options, match, isSoleMatch);
}
if (dictName.includes('userInputs')) {
return options.translations.warnings.userInputs;
}
return null;
};
var dictionaryMatcher = ((match, isSoleMatch) => {
const warning = getDictionaryWarning(match, isSoleMatch);
var dictionaryMatcher = ((options, match, isSoleMatch) => {
const warning = getDictionaryWarning(options, match, isSoleMatch);
const suggestions = [];
const word = match.token;
if (word.match(_const.START_UPPER)) {
suggestions.push(Options.zxcvbnOptions.translations.suggestions.capitalization);
suggestions.push(options.translations.suggestions.capitalization);
} else if (word.match(_const.ALL_UPPER_INVERTED) && word.toLowerCase() !== word) {
suggestions.push(Options.zxcvbnOptions.translations.suggestions.allUppercase);
suggestions.push(options.translations.suggestions.allUppercase);
}
if (match.reversed && match.token.length >= 4) {
suggestions.push(Options.zxcvbnOptions.translations.suggestions.reverseWords);
suggestions.push(options.translations.suggestions.reverseWords);
}
if (match.l33t) {
suggestions.push(Options.zxcvbnOptions.translations.suggestions.l33t);
suggestions.push(options.translations.suggestions.l33t);
}

@@ -64,0 +65,0 @@ return {

@@ -0,1 +1,2 @@

import Options from '../../Options';
import { DictionaryMatch } from '../../types';

@@ -6,8 +7,9 @@ import Reverse from './variants/matching/reverse';

declare class MatchDictionary {
private options;
l33t: L33t;
reverse: Reverse;
constructor();
match({ password }: DictionaryMatchOptions): import("../../types").MatchExtended[];
defaultMatch({ password, useLevenshtein }: DictionaryMatchOptions): DictionaryMatch[];
constructor(options: Options);
match(matchOptions: DictionaryMatchOptions): import("../../types").MatchExtended[];
defaultMatch({ password, userInputsOptions, useLevenshtein, }: DictionaryMatchOptions): DictionaryMatch[];
}
export default MatchDictionary;

@@ -1,22 +0,15 @@

import findLevenshteinDistance from '../../levenshtein.esm.js';
import { sorted } from '../../helper.esm.js';
import { zxcvbnOptions } from '../../Options.esm.js';
import findLevenshteinDistance from '../../utils/levenshtein.esm.js';
import { sorted } from '../../utils/helper.esm.js';
import MatchReverse from './variants/matching/reverse.esm.js';
import MatchL33t from './variants/matching/l33t.esm.js';
import mergeUserInputDictionary from '../../utils/mergeUserInputDictionary.esm.js';
class MatchDictionary {
constructor() {
this.l33t = new MatchL33t(this.defaultMatch);
this.reverse = new MatchReverse(this.defaultMatch);
constructor(options) {
this.options = options;
this.l33t = new MatchL33t(options, this.defaultMatch);
this.reverse = new MatchReverse(options, this.defaultMatch);
}
match({
password
}) {
const matches = [...this.defaultMatch({
password
}), ...this.reverse.match({
password
}), ...this.l33t.match({
password
})];
match(matchOptions) {
const matches = [...this.defaultMatch(matchOptions), ...this.reverse.match(matchOptions), ...this.l33t.match(matchOptions)];
return sorted(matches);

@@ -26,2 +19,3 @@ }

password,
userInputsOptions,
useLevenshtein = true

@@ -32,6 +26,10 @@ }) {

const passwordLower = password.toLowerCase();
const {
rankedDictionaries,
rankedDictionariesMaxWordSize
} = mergeUserInputDictionary(this.options.rankedDictionaries, this.options.rankedDictionariesMaxWordSize, userInputsOptions);
// eslint-disable-next-line complexity,max-statements
Object.keys(zxcvbnOptions.rankedDictionaries).forEach(dictionaryName => {
const rankedDict = zxcvbnOptions.rankedDictionaries[dictionaryName];
const longestDictionaryWordSize = zxcvbnOptions.rankedDictionariesMaxWordSize[dictionaryName];
Object.keys(rankedDictionaries).forEach(dictionaryName => {
const rankedDict = rankedDictionaries[dictionaryName];
const longestDictionaryWordSize = rankedDictionariesMaxWordSize[dictionaryName];
const searchWidth = Math.min(longestDictionaryWordSize, passwordLength);

@@ -47,4 +45,4 @@ for (let i = 0; i < passwordLength; i += 1) {

const isFullPassword = i === 0 && j === passwordLength - 1;
if (zxcvbnOptions.useLevenshteinDistance && isFullPassword && !isInDictionary && useLevenshtein) {
foundLevenshteinDistance = findLevenshteinDistance(usedPassword, rankedDict, zxcvbnOptions.levenshteinThreshold);
if (this.options.useLevenshteinDistance && isFullPassword && !isInDictionary && useLevenshtein) {
foundLevenshteinDistance = findLevenshteinDistance(usedPassword, rankedDict, this.options.levenshteinThreshold);
}

@@ -51,0 +49,0 @@ const isLevenshteinMatch = Object.keys(foundLevenshteinDistance).length !== 0;

'use strict';
var levenshtein = require('../../levenshtein.js');
var helper = require('../../helper.js');
var Options = require('../../Options.js');
var levenshtein = require('../../utils/levenshtein.js');
var helper = require('../../utils/helper.js');
var reverse = require('./variants/matching/reverse.js');
var l33t = require('./variants/matching/l33t.js');
var mergeUserInputDictionary = require('../../utils/mergeUserInputDictionary.js');
class MatchDictionary {
constructor() {
this.l33t = new l33t(this.defaultMatch);
this.reverse = new reverse(this.defaultMatch);
constructor(options) {
this.options = options;
this.l33t = new l33t(options, this.defaultMatch);
this.reverse = new reverse(options, this.defaultMatch);
}
match({
password
}) {
const matches = [...this.defaultMatch({
password
}), ...this.reverse.match({
password
}), ...this.l33t.match({
password
})];
match(matchOptions) {
const matches = [...this.defaultMatch(matchOptions), ...this.reverse.match(matchOptions), ...this.l33t.match(matchOptions)];
return helper.sorted(matches);

@@ -28,2 +21,3 @@ }

password,
userInputsOptions,
useLevenshtein = true

@@ -34,6 +28,10 @@ }) {

const passwordLower = password.toLowerCase();
const {
rankedDictionaries,
rankedDictionariesMaxWordSize
} = mergeUserInputDictionary(this.options.rankedDictionaries, this.options.rankedDictionariesMaxWordSize, userInputsOptions);
// eslint-disable-next-line complexity,max-statements
Object.keys(Options.zxcvbnOptions.rankedDictionaries).forEach(dictionaryName => {
const rankedDict = Options.zxcvbnOptions.rankedDictionaries[dictionaryName];
const longestDictionaryWordSize = Options.zxcvbnOptions.rankedDictionariesMaxWordSize[dictionaryName];
Object.keys(rankedDictionaries).forEach(dictionaryName => {
const rankedDict = rankedDictionaries[dictionaryName];
const longestDictionaryWordSize = rankedDictionariesMaxWordSize[dictionaryName];
const searchWidth = Math.min(longestDictionaryWordSize, passwordLength);

@@ -49,4 +47,4 @@ for (let i = 0; i < passwordLength; i += 1) {

const isFullPassword = i === 0 && j === passwordLength - 1;
if (Options.zxcvbnOptions.useLevenshteinDistance && isFullPassword && !isInDictionary && useLevenshtein) {
foundLevenshteinDistance = levenshtein(usedPassword, rankedDict, Options.zxcvbnOptions.levenshteinThreshold);
if (this.options.useLevenshteinDistance && isFullPassword && !isInDictionary && useLevenshtein) {
foundLevenshteinDistance = levenshtein(usedPassword, rankedDict, this.options.levenshteinThreshold);
}

@@ -53,0 +51,0 @@ const isLevenshteinMatch = Object.keys(foundLevenshteinDistance).length !== 0;

@@ -1,6 +0,6 @@

import { DictionaryMatch } from '../../types';
export interface DictionaryMatchOptions {
password: string;
import { DictionaryMatch, MatchOptions } from '../../types';
export interface DictionaryMatchOptionsLevenshtein extends MatchOptions {
useLevenshtein?: boolean;
}
export type DictionaryMatchOptions = Pick<DictionaryMatchOptionsLevenshtein, 'password' | 'userInputsOptions' | 'useLevenshtein'>;
export type DefaultMatch = (options: DictionaryMatchOptions) => DictionaryMatch[];

@@ -0,11 +1,11 @@

import Options from '../../../../Options';
import { L33tMatch } from '../../../../types';
import { DefaultMatch } from '../../types';
import { DefaultMatch, DictionaryMatchOptions } from '../../types';
declare class MatchL33t {
defaultMatch: DefaultMatch;
constructor(defaultMatch: DefaultMatch);
private options;
private defaultMatch;
constructor(options: Options, defaultMatch: DefaultMatch);
isAlreadyIncluded(matches: L33tMatch[], newMatch: L33tMatch): boolean;
match({ password }: {
password: string;
}): L33tMatch[];
match(matchOptions: DictionaryMatchOptions): L33tMatch[];
}
export default MatchL33t;

@@ -1,2 +0,1 @@

import { zxcvbnOptions } from '../../../../Options.esm.js';
import getCleanPasswords from './unmunger/getCleanPasswords.esm.js';

@@ -44,3 +43,4 @@

class MatchL33t {
constructor(defaultMatch) {
constructor(options, defaultMatch) {
this.options = options;
this.defaultMatch = defaultMatch;

@@ -55,9 +55,6 @@ }

}
match({
password
}) {
match(matchOptions) {
const matches = [];
const subbedPasswords = getCleanPasswords(password, zxcvbnOptions.l33tMaxSubstitutions, zxcvbnOptions.trieNodeRoot);
const subbedPasswords = getCleanPasswords(matchOptions.password, this.options.l33tMaxSubstitutions, this.options.trieNodeRoot);
let hasFullMatch = false;
let isFullSubstitution = true;
subbedPasswords.forEach(subbedPassword => {

@@ -68,13 +65,12 @@ if (hasFullMatch) {

const matchedDictionary = this.defaultMatch({
...matchOptions,
password: subbedPassword.password,
useLevenshtein: isFullSubstitution
useLevenshtein: subbedPassword.isFullSubstitution
});
// only the first entry has a full substitution
isFullSubstitution = false;
matchedDictionary.forEach(match => {
if (!hasFullMatch) {
hasFullMatch = match.i === 0 && match.j === password.length - 1;
hasFullMatch = match.i === 0 && match.j === matchOptions.password.length - 1;
}
const extras = getExtras(subbedPassword, match.i, match.j);
const token = password.slice(extras.i, +extras.j + 1 || 9e9);
const token = matchOptions.password.slice(extras.i, +extras.j + 1 || 9e9);
const newMatch = {

@@ -81,0 +77,0 @@ ...match,

'use strict';
var Options = require('../../../../Options.js');
var getCleanPasswords = require('./unmunger/getCleanPasswords.js');

@@ -46,3 +45,4 @@

class MatchL33t {
constructor(defaultMatch) {
constructor(options, defaultMatch) {
this.options = options;
this.defaultMatch = defaultMatch;

@@ -57,9 +57,6 @@ }

}
match({
password
}) {
match(matchOptions) {
const matches = [];
const subbedPasswords = getCleanPasswords(password, Options.zxcvbnOptions.l33tMaxSubstitutions, Options.zxcvbnOptions.trieNodeRoot);
const subbedPasswords = getCleanPasswords(matchOptions.password, this.options.l33tMaxSubstitutions, this.options.trieNodeRoot);
let hasFullMatch = false;
let isFullSubstitution = true;
subbedPasswords.forEach(subbedPassword => {

@@ -70,13 +67,12 @@ if (hasFullMatch) {

const matchedDictionary = this.defaultMatch({
...matchOptions,
password: subbedPassword.password,
useLevenshtein: isFullSubstitution
useLevenshtein: subbedPassword.isFullSubstitution
});
// only the first entry has a full substitution
isFullSubstitution = false;
matchedDictionary.forEach(match => {
if (!hasFullMatch) {
hasFullMatch = match.i === 0 && match.j === password.length - 1;
hasFullMatch = match.i === 0 && match.j === matchOptions.password.length - 1;
}
const extras = getExtras(subbedPassword, match.i, match.j);
const token = password.slice(extras.i, +extras.j + 1 || 9e9);
const token = matchOptions.password.slice(extras.i, +extras.j + 1 || 9e9);
const newMatch = {

@@ -83,0 +79,0 @@ ...match,

@@ -1,8 +0,8 @@

import { DefaultMatch } from '../../types';
import { DefaultMatch, DictionaryMatchOptions } from '../../types';
import Options from '../../../../Options';
declare class MatchReverse {
defaultMatch: DefaultMatch;
constructor(defaultMatch: DefaultMatch);
match({ password }: {
password: string;
}): {
private options;
private defaultMatch;
constructor(options: Options, defaultMatch: DefaultMatch);
match(matchOptions: DictionaryMatchOptions): {
token: string;

@@ -15,3 +15,3 @@ reversed: boolean;

rank: number;
dictionaryName: import("../../../../types").DictionaryNames;
dictionaryName: import("../../../../types").DictionaryNames | string;
l33t: boolean;

@@ -18,0 +18,0 @@ }[];

@@ -7,10 +7,10 @@ /*

class MatchReverse {
constructor(defaultMatch) {
constructor(options, defaultMatch) {
this.options = options;
this.defaultMatch = defaultMatch;
}
match({
password
}) {
const passwordReversed = password.split('').reverse().join('');
match(matchOptions) {
const passwordReversed = matchOptions.password.split('').reverse().join('');
return this.defaultMatch({
...matchOptions,
password: passwordReversed

@@ -20,6 +20,7 @@ }).map(match => ({

token: match.token.split('').reverse().join(''),
// reverse back
reversed: true,
// map coordinates back to original string
i: password.length - 1 - match.j,
j: password.length - 1 - match.i
i: matchOptions.password.length - 1 - match.j,
j: matchOptions.password.length - 1 - match.i
}));

@@ -26,0 +27,0 @@ }

@@ -9,10 +9,10 @@ 'use strict';

class MatchReverse {
constructor(defaultMatch) {
constructor(options, defaultMatch) {
this.options = options;
this.defaultMatch = defaultMatch;
}
match({
password
}) {
const passwordReversed = password.split('').reverse().join('');
match(matchOptions) {
const passwordReversed = matchOptions.password.split('').reverse().join('');
return this.defaultMatch({
...matchOptions,
password: passwordReversed

@@ -22,6 +22,7 @@ }).map(match => ({

token: match.token.split('').reverse().join(''),
// reverse back
reversed: true,
// map coordinates back to original string
i: password.length - 1 - match.j,
j: password.length - 1 - match.i
i: matchOptions.password.length - 1 - match.j,
j: matchOptions.password.length - 1 - match.i
}));

@@ -28,0 +29,0 @@ }

@@ -12,4 +12,5 @@ import TrieNode from './TrieNode';

changes: IndexedPasswordChanges[];
isFullSubstitution: boolean;
}
declare const getCleanPasswords: (password: string, limit: number, trieRoot: TrieNode) => PasswordWithSubs[];
export default getCleanPasswords;

@@ -43,3 +43,4 @@ class CleanPasswords {

password: this.buffer.join(''),
changes
changes,
isFullSubstitution: onlyFullSub
});

@@ -55,2 +56,3 @@ }

const cur = nodes[i - index];
const sub = cur.parents.join('');
if (cur.isTerminal()) {

@@ -60,3 +62,3 @@ // Skip if this would be a 4th or more consecutive substitution of the same letter

// So we can ignore the rest to save calculation time
if (lastSubLetter === cur.parents.join('') && consecutiveSubCount >= 3) {
if (lastSubLetter === sub && consecutiveSubCount >= 3) {
// eslint-disable-next-line no-continue

@@ -66,10 +68,10 @@ continue;

hasSubs = true;
const subs = cur.subs;
const letters = cur.subs;
// eslint-disable-next-line no-restricted-syntax
for (const sub of subs) {
this.buffer.push(sub);
for (const letter of letters) {
this.buffer.push(letter);
const newSubs = changes.concat({
i: subIndex,
letter: sub,
substitution: cur.parents.join('')
letter,
substitution: sub
});

@@ -80,7 +82,7 @@ // recursively build the rest of the string

isFullSub,
index: i + 1,
subIndex: subIndex + sub.length,
index: index + sub.length,
subIndex: subIndex + letter.length,
changes: newSubs,
lastSubLetter: cur.parents.join(''),
consecutiveSubCount: lastSubLetter === cur.parents.join('') ? consecutiveSubCount + 1 : 1
lastSubLetter: sub,
consecutiveSubCount: lastSubLetter === sub ? consecutiveSubCount + 1 : 1
});

@@ -87,0 +89,0 @@ // backtrack by ignoring the added postfix

@@ -45,3 +45,4 @@ 'use strict';

password: this.buffer.join(''),
changes
changes,
isFullSubstitution: onlyFullSub
});

@@ -57,2 +58,3 @@ }

const cur = nodes[i - index];
const sub = cur.parents.join('');
if (cur.isTerminal()) {

@@ -62,3 +64,3 @@ // Skip if this would be a 4th or more consecutive substitution of the same letter

// So we can ignore the rest to save calculation time
if (lastSubLetter === cur.parents.join('') && consecutiveSubCount >= 3) {
if (lastSubLetter === sub && consecutiveSubCount >= 3) {
// eslint-disable-next-line no-continue

@@ -68,10 +70,10 @@ continue;

hasSubs = true;
const subs = cur.subs;
const letters = cur.subs;
// eslint-disable-next-line no-restricted-syntax
for (const sub of subs) {
this.buffer.push(sub);
for (const letter of letters) {
this.buffer.push(letter);
const newSubs = changes.concat({
i: subIndex,
letter: sub,
substitution: cur.parents.join('')
letter,
substitution: sub
});

@@ -82,7 +84,7 @@ // recursively build the rest of the string

isFullSub,
index: i + 1,
subIndex: subIndex + sub.length,
index: index + sub.length,
subIndex: subIndex + letter.length,
changes: newSubs,
lastSubLetter: cur.parents.join(''),
consecutiveSubCount: lastSubLetter === cur.parents.join('') ? consecutiveSubCount + 1 : 1
lastSubLetter: sub,
consecutiveSubCount: lastSubLetter === sub ? consecutiveSubCount + 1 : 1
});

@@ -89,0 +91,0 @@ // backtrack by ignoring the added postfix

@@ -0,3 +1,4 @@

import Options from '../../Options';
import { MatchEstimated } from '../../types';
declare const _default: (match: MatchEstimated) => {
declare const _default: (options: Options, match: MatchEstimated) => {
warning: string;

@@ -4,0 +5,0 @@ suggestions: string[];

@@ -1,8 +0,6 @@

import { zxcvbnOptions } from '../../Options.esm.js';
var regexMatcher = (match => {
var regexMatcher = ((options, match) => {
if (match.regexName === 'recentYear') {
return {
warning: zxcvbnOptions.translations.warnings.recentYears,
suggestions: [zxcvbnOptions.translations.suggestions.recentYears, zxcvbnOptions.translations.suggestions.associatedYears]
warning: options.translations.warnings.recentYears,
suggestions: [options.translations.suggestions.recentYears, options.translations.suggestions.associatedYears]
};

@@ -9,0 +7,0 @@ }

'use strict';
var Options = require('../../Options.js');
var regexMatcher = (match => {
var regexMatcher = ((options, match) => {
if (match.regexName === 'recentYear') {
return {
warning: Options.zxcvbnOptions.translations.warnings.recentYears,
suggestions: [Options.zxcvbnOptions.translations.suggestions.recentYears, Options.zxcvbnOptions.translations.suggestions.associatedYears]
warning: options.translations.warnings.recentYears,
suggestions: [options.translations.suggestions.recentYears, options.translations.suggestions.associatedYears]
};

@@ -11,0 +9,0 @@ }

@@ -1,9 +0,9 @@

import { REGEXEN } from '../../data/const';
interface RegexMatchOptions {
password: string;
regexes?: typeof REGEXEN;
}
import { MatchOptions } from '../../types';
import Options from '../../Options';
type RegexMatchOptions = Pick<MatchOptions, 'password'>;
declare class MatchRegex {
match({ password, regexes }: RegexMatchOptions): import("../../types").MatchExtended[];
private options;
constructor(options: Options);
match({ password }: RegexMatchOptions): import("../../types").MatchExtended[];
}
export default MatchRegex;
import { REGEXEN } from '../../data/const.esm.js';
import { sorted } from '../../helper.esm.js';
import { sorted } from '../../utils/helper.esm.js';

@@ -10,9 +10,11 @@ /*

class MatchRegex {
constructor(options) {
this.options = options;
}
match({
password,
regexes = REGEXEN
password
}) {
const matches = [];
Object.keys(regexes).forEach(name => {
const regex = regexes[name];
Object.keys(REGEXEN).forEach(name => {
const regex = REGEXEN[name];
regex.lastIndex = 0; // keeps regexMatch stateless

@@ -19,0 +21,0 @@ let regexMatch;

'use strict';
var _const = require('../../data/const.js');
var helper = require('../../helper.js');
var helper = require('../../utils/helper.js');

@@ -12,9 +12,11 @@ /*

class MatchRegex {
constructor(options) {
this.options = options;
}
match({
password,
regexes = _const.REGEXEN
password
}) {
const matches = [];
Object.keys(regexes).forEach(name => {
const regex = regexes[name];
Object.keys(_const.REGEXEN).forEach(name => {
const regex = _const.REGEXEN[name];
regex.lastIndex = 0; // keeps regexMatch stateless

@@ -21,0 +23,0 @@ let regexMatch;

@@ -0,3 +1,4 @@

import Options from '../../Options';
import { MatchEstimated } from '../../types';
declare const _default: (match: MatchEstimated) => {
declare const _default: (options: Options, match: MatchEstimated) => {
warning: string;

@@ -4,0 +5,0 @@ suggestions: string[];

@@ -1,11 +0,9 @@

import { zxcvbnOptions } from '../../Options.esm.js';
var repeatMatcher = (match => {
let warning = zxcvbnOptions.translations.warnings.extendedRepeat;
var repeatMatcher = ((options, match) => {
let warning = options.translations.warnings.extendedRepeat;
if (match.baseToken.length === 1) {
warning = zxcvbnOptions.translations.warnings.simpleRepeat;
warning = options.translations.warnings.simpleRepeat;
}
return {
warning,
suggestions: [zxcvbnOptions.translations.suggestions.repeated]
suggestions: [options.translations.suggestions.repeated]
};

@@ -12,0 +10,0 @@ });

'use strict';
var Options = require('../../Options.js');
var repeatMatcher = (match => {
let warning = Options.zxcvbnOptions.translations.warnings.extendedRepeat;
var repeatMatcher = ((options, match) => {
let warning = options.translations.warnings.extendedRepeat;
if (match.baseToken.length === 1) {
warning = Options.zxcvbnOptions.translations.warnings.simpleRepeat;
warning = options.translations.warnings.simpleRepeat;
}
return {
warning,
suggestions: [Options.zxcvbnOptions.translations.suggestions.repeated]
suggestions: [options.translations.suggestions.repeated]
};

@@ -14,0 +12,0 @@ });

@@ -1,9 +0,9 @@

import { RepeatMatch } from '../../types';
import { MatchOptions, RepeatMatch } from '../../types';
import Matching from '../../Matching';
interface RepeatMatchOptions {
password: string;
omniMatch: Matching;
}
import Options from '../../Options';
declare class MatchRepeat {
match({ password, omniMatch }: RepeatMatchOptions): RepeatMatch[] | Promise<RepeatMatch[]>;
private options;
private scoring;
constructor(options: Options);
match({ password, omniMatch }: MatchOptions): RepeatMatch[] | Promise<RepeatMatch[]>;
normalizeMatch(baseToken: string, j: number, match: RegExpExecArray, baseGuesses: number | Promise<number>): RepeatMatch | Promise<RepeatMatch>;

@@ -10,0 +10,0 @@ getGreedyMatch(password: string, lastIndex: number): RegExpExecArray | null;

@@ -1,2 +0,2 @@

import scoring from '../../scoring/index.esm.js';
import Scoring from '../../scoring/index.esm.js';

@@ -9,2 +9,6 @@ /*

class MatchRepeat {
constructor(options) {
this.options = options;
this.scoring = new Scoring(options);
}
// eslint-disable-next-line max-statements

@@ -111,7 +115,7 @@ match({

return matches.then(resolvedMatches => {
const baseAnalysis = scoring.mostGuessableMatchSequence(baseToken, resolvedMatches);
const baseAnalysis = this.scoring.mostGuessableMatchSequence(baseToken, resolvedMatches);
return baseAnalysis.guesses;
});
}
const baseAnalysis = scoring.mostGuessableMatchSequence(baseToken, matches);
const baseAnalysis = this.scoring.mostGuessableMatchSequence(baseToken, matches);
return baseAnalysis.guesses;

@@ -118,0 +122,0 @@ }

@@ -11,2 +11,6 @@ 'use strict';

class MatchRepeat {
constructor(options) {
this.options = options;
this.scoring = new index(options);
}
// eslint-disable-next-line max-statements

@@ -113,7 +117,7 @@ match({

return matches.then(resolvedMatches => {
const baseAnalysis = index.mostGuessableMatchSequence(baseToken, resolvedMatches);
const baseAnalysis = this.scoring.mostGuessableMatchSequence(baseToken, resolvedMatches);
return baseAnalysis.guesses;
});
}
const baseAnalysis = index.mostGuessableMatchSequence(baseToken, matches);
const baseAnalysis = this.scoring.mostGuessableMatchSequence(baseToken, matches);
return baseAnalysis.guesses;

@@ -120,0 +124,0 @@ }

@@ -1,6 +0,7 @@

import { SeparatorMatch } from '../../types';
interface SeparatorMatchOptions {
password: string;
}
import { MatchOptions, SeparatorMatch } from '../../types';
import Options from '../../Options';
type SeparatorMatchOptions = Pick<MatchOptions, 'password'>;
declare class MatchSeparator {
private options;
constructor(options: Options);
static getMostUsedSeparatorChar(password: string): string | undefined;

@@ -7,0 +8,0 @@ static getSeparatorRegex(separator: string): RegExp;

@@ -10,2 +10,5 @@ import { SEPERATOR_CHARS } from '../../data/const.esm.js';

class MatchSeparator {
constructor(options) {
this.options = options;
}
static getMostUsedSeparatorChar(password) {

@@ -12,0 +15,0 @@ const mostUsedSeperators = [...password.split('').filter(c => separatorRegex.test(c)).reduce((memo, c) => {

@@ -12,2 +12,5 @@ 'use strict';

class MatchSeparator {
constructor(options) {
this.options = options;
}
static getMostUsedSeparatorChar(password) {

@@ -14,0 +17,0 @@ const mostUsedSeperators = [...password.split('').filter(c => separatorRegex.test(c)).reduce((memo, c) => {

@@ -1,2 +0,3 @@

declare const _default: () => {
import Options from '../../Options';
declare const _default: (options: Options) => {
warning: string;

@@ -3,0 +4,0 @@ suggestions: string[];

@@ -1,7 +0,5 @@

import { zxcvbnOptions } from '../../Options.esm.js';
var sequenceMatcher = (() => {
var sequenceMatcher = (options => {
return {
warning: zxcvbnOptions.translations.warnings.sequences,
suggestions: [zxcvbnOptions.translations.suggestions.sequences]
warning: options.translations.warnings.sequences,
suggestions: [options.translations.suggestions.sequences]
};

@@ -8,0 +6,0 @@ });

'use strict';
var Options = require('../../Options.js');
var sequenceMatcher = (() => {
var sequenceMatcher = (options => {
return {
warning: Options.zxcvbnOptions.translations.warnings.sequences,
suggestions: [Options.zxcvbnOptions.translations.suggestions.sequences]
warning: options.translations.warnings.sequences,
suggestions: [options.translations.suggestions.sequences]
};

@@ -10,0 +8,0 @@ });

@@ -1,2 +0,3 @@

import { SequenceMatch } from '../../types';
import { MatchOptions, SequenceMatch } from '../../types';
import Options from '../../Options';
type UpdateParams = {

@@ -9,7 +10,7 @@ i: number;

};
interface SequenceMatchOptions {
password: string;
}
type SequenceMatchOptions = Pick<MatchOptions, 'password'>;
declare class MatchSequence {
private options;
MAX_DELTA: number;
constructor(options: Options);
match({ password }: SequenceMatchOptions): SequenceMatch[];

@@ -16,0 +17,0 @@ update({ i, j, delta, password, result }: UpdateParams): number | null;

@@ -9,3 +9,4 @@ import { ALL_LOWER, ALL_UPPER, ALL_DIGIT } from '../../data/const.esm.js';

class MatchSequence {
constructor() {
constructor(options) {
this.options = options;
this.MAX_DELTA = 5;

@@ -12,0 +13,0 @@ }

@@ -11,3 +11,4 @@ 'use strict';

class MatchSequence {
constructor() {
constructor(options) {
this.options = options;
this.MAX_DELTA = 5;

@@ -14,0 +15,0 @@ }

@@ -0,3 +1,4 @@

import Options from '../../Options';
import { MatchEstimated } from '../../types';
declare const _default: (match: MatchEstimated) => {
declare const _default: (options: Options, match: MatchEstimated) => {
warning: string;

@@ -4,0 +5,0 @@ suggestions: string[];

@@ -1,11 +0,9 @@

import { zxcvbnOptions } from '../../Options.esm.js';
var spatialMatcher = (match => {
let warning = zxcvbnOptions.translations.warnings.keyPattern;
var spatialMatcher = ((options, match) => {
let warning = options.translations.warnings.keyPattern;
if (match.turns === 1) {
warning = zxcvbnOptions.translations.warnings.straightRow;
warning = options.translations.warnings.straightRow;
}
return {
warning,
suggestions: [zxcvbnOptions.translations.suggestions.longerKeyboardPattern]
suggestions: [options.translations.suggestions.longerKeyboardPattern]
};

@@ -12,0 +10,0 @@ });

'use strict';
var Options = require('../../Options.js');
var spatialMatcher = (match => {
let warning = Options.zxcvbnOptions.translations.warnings.keyPattern;
var spatialMatcher = ((options, match) => {
let warning = options.translations.warnings.keyPattern;
if (match.turns === 1) {
warning = Options.zxcvbnOptions.translations.warnings.straightRow;
warning = options.translations.warnings.straightRow;
}
return {
warning,
suggestions: [Options.zxcvbnOptions.translations.suggestions.longerKeyboardPattern]
suggestions: [options.translations.suggestions.longerKeyboardPattern]
};

@@ -14,0 +12,0 @@ });

@@ -1,7 +0,8 @@

import { LooseObject, SpatialMatch } from '../../types';
interface SpatialMatchOptions {
password: string;
}
import Options from '../../Options';
import { LooseObject, MatchOptions, SpatialMatch } from '../../types';
type SpatialMatchOptions = Pick<MatchOptions, 'password'>;
declare class MatchSpatial {
private options;
SHIFTED_RX: RegExp;
constructor(options: Options);
match({ password }: SpatialMatchOptions): import("../../types").MatchExtended[];

@@ -8,0 +9,0 @@ checkIfShifted(graphName: string, password: string, index: number): 1 | 0;

@@ -1,3 +0,2 @@

import { extend, sorted } from '../../helper.esm.js';
import { zxcvbnOptions } from '../../Options.esm.js';
import { extend, sorted } from '../../utils/helper.esm.js';

@@ -10,3 +9,4 @@ /*

class MatchSpatial {
constructor() {
constructor(options) {
this.options = options;
this.SHIFTED_RX = /[~!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL:"ZXCVBNM<>?]/;

@@ -18,4 +18,4 @@ }

const matches = [];
Object.keys(zxcvbnOptions.graphs).forEach(graphName => {
const graph = zxcvbnOptions.graphs[graphName];
Object.keys(this.options.graphs).forEach(graphName => {
const graph = this.options.graphs[graphName];
extend(matches, this.helper(password, graph, graphName));

@@ -22,0 +22,0 @@ });

'use strict';
var helper = require('../../helper.js');
var Options = require('../../Options.js');
var helper = require('../../utils/helper.js');

@@ -12,3 +11,4 @@ /*

class MatchSpatial {
constructor() {
constructor(options) {
this.options = options;
this.SHIFTED_RX = /[~!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL:"ZXCVBNM<>?]/;

@@ -20,4 +20,4 @@ }

const matches = [];
Object.keys(Options.zxcvbnOptions.graphs).forEach(graphName => {
const graph = Options.zxcvbnOptions.graphs[graphName];
Object.keys(this.options.graphs).forEach(graphName => {
const graph = this.options.graphs[graphName];
helper.extend(matches, this.helper(password, graph, graphName));

@@ -24,0 +24,0 @@ });

@@ -0,3 +1,4 @@

import Options from '../../Options';
import { MatchEstimated, MatchExtended } from '../../types';
declare const _default: ({ graph, token, shiftedCount, turns, }: MatchExtended | MatchEstimated) => number;
declare const _default: ({ graph, token, shiftedCount, turns }: MatchExtended | MatchEstimated, options: Options) => number;
export default _default;
import utils from '../../scoring/utils.esm.js';
import { zxcvbnOptions } from '../../Options.esm.js';

@@ -13,9 +12,8 @@ const calcAverageDegree = graph => {

};
const estimatePossiblePatterns = ({
const estimatePossiblePatterns = (graphEntry, {
token,
graph,
turns
}) => {
const startingPosition = Object.keys(zxcvbnOptions.graphs[graph]).length;
const averageDegree = calcAverageDegree(zxcvbnOptions.graphs[graph]);
const startingPosition = Object.keys(graphEntry).length;
const averageDegree = calcAverageDegree(graphEntry);
let guesses = 0;

@@ -37,6 +35,5 @@ const tokenLength = token.length;

turns
}) => {
let guesses = estimatePossiblePatterns({
}, options) => {
let guesses = estimatePossiblePatterns(options.graphs[graph], {
token,
graph,
turns

@@ -43,0 +40,0 @@ });

'use strict';
var utils = require('../../scoring/utils.js');
var Options = require('../../Options.js');

@@ -15,9 +14,8 @@ const calcAverageDegree = graph => {

};
const estimatePossiblePatterns = ({
const estimatePossiblePatterns = (graphEntry, {
token,
graph,
turns
}) => {
const startingPosition = Object.keys(Options.zxcvbnOptions.graphs[graph]).length;
const averageDegree = calcAverageDegree(Options.zxcvbnOptions.graphs[graph]);
const startingPosition = Object.keys(graphEntry).length;
const averageDegree = calcAverageDegree(graphEntry);
let guesses = 0;

@@ -39,6 +37,5 @@ const tokenLength = token.length;

turns
}) => {
let guesses = estimatePossiblePatterns({
}, options) => {
let guesses = estimatePossiblePatterns(options.graphs[graph], {
token,
graph,
turns

@@ -45,0 +42,0 @@ });

@@ -1,9 +0,12 @@

import { MatchExtended, MatchingType } from './types';
type Matchers = {
[key: string]: MatchingType;
};
import { MatchExtended, UserInputsOptions } from './types';
import Options from './Options';
declare class Matching {
readonly matchers: Matchers;
match(password: string): MatchExtended[] | Promise<MatchExtended[]>;
private options;
private readonly matchers;
constructor(options: Options);
private matcherFactory;
private processResult;
private handlePromises;
match(password: string, userInputsOptions?: UserInputsOptions): MatchExtended[] | Promise<MatchExtended[]>;
}
export default Matching;

@@ -1,2 +0,2 @@

import { extend, sorted } from './helper.esm.js';
import { extend, sorted } from './utils/helper.esm.js';
import MatchDate from './matcher/date/matching.esm.js';

@@ -9,6 +9,6 @@ import MatchDictionary from './matcher/dictionary/matching.esm.js';

import MatchSeparator from './matcher/separator/matching.esm.js';
import { zxcvbnOptions } from './Options.esm.js';
class Matching {
constructor() {
constructor(options) {
this.options = options;
this.matchers = {

@@ -25,25 +25,20 @@ date: MatchDate,

}
match(password) {
const matches = [];
const promises = [];
const matchers = [...Object.keys(this.matchers), ...Object.keys(zxcvbnOptions.matchers)];
matchers.forEach(key => {
if (!this.matchers[key] && !zxcvbnOptions.matchers[key]) {
return;
}
const Matcher = this.matchers[key] ? this.matchers[key] : zxcvbnOptions.matchers[key].Matching;
const usedMatcher = new Matcher();
const result = usedMatcher.match({
password,
omniMatch: this
matcherFactory(name) {
if (!this.matchers[name] && !this.options.matchers[name]) {
return null;
}
const Matcher = this.matchers[name] ? this.matchers[name] : this.options.matchers[name].Matching;
return new Matcher(this.options);
}
processResult(matches, promises, result) {
if (result instanceof Promise) {
result.then(response => {
extend(matches, response);
});
if (result instanceof Promise) {
result.then(response => {
extend(matches, response);
});
promises.push(result);
} else {
extend(matches, result);
}
});
promises.push(result);
} else {
extend(matches, result);
}
}
handlePromises(matches, promises) {
if (promises.length > 0) {

@@ -60,2 +55,21 @@ return new Promise((resolve, reject) => {

}
match(password, userInputsOptions) {
const matches = [];
const promises = [];
const matchers = [...Object.keys(this.matchers), ...Object.keys(this.options.matchers)];
matchers.forEach(key => {
const matcher = this.matcherFactory(key);
if (!matcher) {
return;
}
const result = matcher.match({
password,
omniMatch: this,
userInputsOptions
});
// extends matches and promises by references
this.processResult(matches, promises, result);
});
return this.handlePromises(matches, promises);
}
}

@@ -62,0 +76,0 @@

'use strict';
var helper = require('./helper.js');
var helper = require('./utils/helper.js');
var matching = require('./matcher/date/matching.js');

@@ -11,6 +11,6 @@ var matching$1 = require('./matcher/dictionary/matching.js');

var matching$6 = require('./matcher/separator/matching.js');
var Options = require('./Options.js');
class Matching {
constructor() {
constructor(options) {
this.options = options;
this.matchers = {

@@ -27,25 +27,20 @@ date: matching,

}
match(password) {
const matches = [];
const promises = [];
const matchers = [...Object.keys(this.matchers), ...Object.keys(Options.zxcvbnOptions.matchers)];
matchers.forEach(key => {
if (!this.matchers[key] && !Options.zxcvbnOptions.matchers[key]) {
return;
}
const Matcher = this.matchers[key] ? this.matchers[key] : Options.zxcvbnOptions.matchers[key].Matching;
const usedMatcher = new Matcher();
const result = usedMatcher.match({
password,
omniMatch: this
matcherFactory(name) {
if (!this.matchers[name] && !this.options.matchers[name]) {
return null;
}
const Matcher = this.matchers[name] ? this.matchers[name] : this.options.matchers[name].Matching;
return new Matcher(this.options);
}
processResult(matches, promises, result) {
if (result instanceof Promise) {
result.then(response => {
helper.extend(matches, response);
});
if (result instanceof Promise) {
result.then(response => {
helper.extend(matches, response);
});
promises.push(result);
} else {
helper.extend(matches, result);
}
});
promises.push(result);
} else {
helper.extend(matches, result);
}
}
handlePromises(matches, promises) {
if (promises.length > 0) {

@@ -62,2 +57,21 @@ return new Promise((resolve, reject) => {

}
match(password, userInputsOptions) {
const matches = [];
const promises = [];
const matchers = [...Object.keys(this.matchers), ...Object.keys(this.options.matchers)];
matchers.forEach(key => {
const matcher = this.matcherFactory(key);
if (!matcher) {
return;
}
const result = matcher.match({
password,
omniMatch: this,
userInputsOptions
});
// extends matches and promises by references
this.processResult(matches, promises, result);
});
return this.handlePromises(matches, promises);
}
}

@@ -64,0 +78,0 @@

@@ -1,4 +0,4 @@

import { TranslationKeys, OptionsType, OptionsDictionary, OptionsL33tTable, OptionsGraph, RankedDictionaries, Matchers, Matcher } from './types';
import { TranslationKeys, OptionsType, OptionsDictionary, OptionsL33tTable, OptionsGraph, RankedDictionaries, Matchers, Matcher, UserInputsOptions, TimeEstimationValues } from './types';
import TrieNode from './matcher/dictionary/variants/matching/unmunger/TrieNode';
export declare class Options {
export default class Options {
matchers: Matchers;

@@ -16,12 +16,12 @@ l33tTable: OptionsL33tTable;

maxLength: number;
constructor();
setOptions(options?: OptionsType): void;
setTranslations(translations: TranslationKeys): void;
checkCustomTranslations(translations: TranslationKeys): boolean;
setRankedDictionaries(): void;
getRankedDictionariesMaxWordSize(list: (string | number)[]): number;
buildSanitizedRankedDictionary(list: (string | number)[]): import("./types").LooseObject;
extendUserInputsDictionary(dictionary: (string | number)[]): void;
addMatcher(name: string, matcher: Matcher): void;
timeEstimationValues: TimeEstimationValues;
constructor(options?: OptionsType, customMatchers?: Record<string, Matcher>);
private setOptions;
private setTranslations;
private checkCustomTranslations;
private setRankedDictionaries;
private getRankedDictionariesMaxWordSize;
private buildSanitizedRankedDictionary;
getUserInputsOptions(dictionary?: (string | number)[]): UserInputsOptions;
private addMatcher;
}
export declare const zxcvbnOptions: Options;

@@ -1,2 +0,2 @@

import { buildRankedDictionary } from './helper.esm.js';
import { buildRankedDictionary } from './utils/helper.esm.js';
import l33tTable from './data/l33tTable.esm.js';

@@ -6,5 +6,6 @@ import translationKeys from './data/translationKeys.esm.js';

import l33tTableToTrieNode from './matcher/dictionary/variants/matching/unmunger/l33tTableToTrieNode.esm.js';
import { timeEstimationValuesDefaults, checkTimeEstimationValues } from './TimeEstimates.esm.js';
class Options {
constructor() {
constructor(options = {}, customMatchers = {}) {
this.matchers = {};

@@ -24,3 +25,14 @@ this.l33tTable = l33tTable;

this.maxLength = 256;
this.setRankedDictionaries();
this.timeEstimationValues = {
scoring: {
...timeEstimationValuesDefaults.scoring
},
attackTime: {
...timeEstimationValuesDefaults.attackTime
}
};
this.setOptions(options);
Object.entries(customMatchers).forEach(([name, matcher]) => {
this.addMatcher(name, matcher);
});
}

@@ -55,2 +67,13 @@ // eslint-disable-next-line max-statements,complexity

}
if (options.timeEstimationValues !== undefined) {
checkTimeEstimationValues(options.timeEstimationValues);
this.timeEstimationValues = {
scoring: {
...options.timeEstimationValues.scoring
},
attackTime: {
...options.timeEstimationValues.attackTime
}
};
}
}

@@ -113,9 +136,13 @@ setTranslations(translations) {

}
extendUserInputsDictionary(dictionary) {
if (!this.dictionary.userInputs) {
this.dictionary.userInputs = [];
getUserInputsOptions(dictionary) {
let rankedDictionary = {};
let rankedDictionaryMaxWordSize = 0;
if (dictionary) {
rankedDictionary = this.buildSanitizedRankedDictionary(dictionary);
rankedDictionaryMaxWordSize = this.getRankedDictionariesMaxWordSize(dictionary);
}
const newList = [...this.dictionary.userInputs, ...dictionary];
this.rankedDictionaries.userInputs = this.buildSanitizedRankedDictionary(newList);
this.rankedDictionariesMaxWordSize.userInputs = this.getRankedDictionariesMaxWordSize(newList);
return {
rankedDictionary,
rankedDictionaryMaxWordSize
};
}

@@ -130,5 +157,4 @@ addMatcher(name, matcher) {

}
const zxcvbnOptions = new Options();
export { Options, zxcvbnOptions };
export { Options as default };
//# sourceMappingURL=Options.esm.js.map
'use strict';
var helper = require('./helper.js');
var helper = require('./utils/helper.js');
var l33tTable = require('./data/l33tTable.js');

@@ -8,5 +8,6 @@ var translationKeys = require('./data/translationKeys.js');

var l33tTableToTrieNode = require('./matcher/dictionary/variants/matching/unmunger/l33tTableToTrieNode.js');
var TimeEstimates = require('./TimeEstimates.js');
class Options {
constructor() {
constructor(options = {}, customMatchers = {}) {
this.matchers = {};

@@ -26,3 +27,14 @@ this.l33tTable = l33tTable;

this.maxLength = 256;
this.setRankedDictionaries();
this.timeEstimationValues = {
scoring: {
...TimeEstimates.timeEstimationValuesDefaults.scoring
},
attackTime: {
...TimeEstimates.timeEstimationValuesDefaults.attackTime
}
};
this.setOptions(options);
Object.entries(customMatchers).forEach(([name, matcher]) => {
this.addMatcher(name, matcher);
});
}

@@ -57,2 +69,13 @@ // eslint-disable-next-line max-statements,complexity

}
if (options.timeEstimationValues !== undefined) {
TimeEstimates.checkTimeEstimationValues(options.timeEstimationValues);
this.timeEstimationValues = {
scoring: {
...options.timeEstimationValues.scoring
},
attackTime: {
...options.timeEstimationValues.attackTime
}
};
}
}

@@ -115,9 +138,13 @@ setTranslations(translations) {

}
extendUserInputsDictionary(dictionary) {
if (!this.dictionary.userInputs) {
this.dictionary.userInputs = [];
getUserInputsOptions(dictionary) {
let rankedDictionary = {};
let rankedDictionaryMaxWordSize = 0;
if (dictionary) {
rankedDictionary = this.buildSanitizedRankedDictionary(dictionary);
rankedDictionaryMaxWordSize = this.getRankedDictionariesMaxWordSize(dictionary);
}
const newList = [...this.dictionary.userInputs, ...dictionary];
this.rankedDictionaries.userInputs = this.buildSanitizedRankedDictionary(newList);
this.rankedDictionariesMaxWordSize.userInputs = this.getRankedDictionariesMaxWordSize(newList);
return {
rankedDictionary,
rankedDictionaryMaxWordSize
};
}

@@ -132,6 +159,4 @@ addMatcher(name, matcher) {

}
const zxcvbnOptions = new Options();
exports.Options = Options;
exports.zxcvbnOptions = zxcvbnOptions;
module.exports = Options;
//# sourceMappingURL=Options.js.map

@@ -0,3 +1,4 @@

import Options from '../Options';
import { MatchEstimated, MatchExtended } from '../types';
declare const _default: (match: MatchExtended | MatchEstimated, password: string) => import("../types").Match;
declare const _default: (options: Options, match: MatchExtended | MatchEstimated, password: string) => import("../types").Match;
export default _default;
import { MIN_SUBMATCH_GUESSES_SINGLE_CHAR, MIN_SUBMATCH_GUESSES_MULTI_CHAR } from '../data/const.esm.js';
import utils from './utils.esm.js';
import { zxcvbnOptions } from '../Options.esm.js';
import bruteforceMatcher from '../matcher/bruteforce/scoring.esm.js';

@@ -34,8 +33,8 @@ import dateMatcher from '../matcher/date/scoring.esm.js';

};
const getScoring = (name, match) => {
const getScoring = (options, name, match) => {
if (matchers[name]) {
return matchers[name](match);
return matchers[name](match, options);
}
if (zxcvbnOptions.matchers[name] && 'scoring' in zxcvbnOptions.matchers[name]) {
return zxcvbnOptions.matchers[name].scoring(match);
if (options.matchers[name] && 'scoring' in options.matchers[name]) {
return options.matchers[name].scoring(match, options);
}

@@ -48,3 +47,3 @@ return 0;

// eslint-disable-next-line complexity, max-statements
var estimateGuesses = ((match, password) => {
var estimateGuesses = ((options, match, password) => {
const extraData = {};

@@ -56,3 +55,3 @@ // a match's guess estimate doesn't change. cache it.

const minGuesses = getMinGuesses(match, password);
const estimationResult = getScoring(match.pattern, match);
const estimationResult = getScoring(options, match.pattern, match);
let guesses = 0;

@@ -59,0 +58,0 @@ if (typeof estimationResult === 'number') {

@@ -5,3 +5,2 @@ 'use strict';

var utils = require('./utils.js');
var Options = require('../Options.js');
var scoring = require('../matcher/bruteforce/scoring.js');

@@ -37,8 +36,8 @@ var scoring$1 = require('../matcher/date/scoring.js');

};
const getScoring = (name, match) => {
const getScoring = (options, name, match) => {
if (matchers[name]) {
return matchers[name](match);
return matchers[name](match, options);
}
if (Options.zxcvbnOptions.matchers[name] && 'scoring' in Options.zxcvbnOptions.matchers[name]) {
return Options.zxcvbnOptions.matchers[name].scoring(match);
if (options.matchers[name] && 'scoring' in options.matchers[name]) {
return options.matchers[name].scoring(match, options);
}

@@ -51,3 +50,3 @@ return 0;

// eslint-disable-next-line complexity, max-statements
var estimateGuesses = ((match, password) => {
var estimateGuesses = ((options, match, password) => {
const extraData = {};

@@ -59,3 +58,3 @@ // a match's guess estimate doesn't change. cache it.

const minGuesses = getMinGuesses(match, password);
const estimationResult = getScoring(match.pattern, match);
const estimationResult = getScoring(options, match.pattern, match);
let guesses = 0;

@@ -62,0 +61,0 @@ if (typeof estimationResult === 'number') {

import { MatchExtended, MatchEstimated } from '../types';
declare const _default: {
import Options from '../Options';
export default class Scoring {
private options;
constructor(options: Options);
mostGuessableMatchSequence(password: string, matches: MatchExtended[], excludeAdditive?: boolean): {

@@ -10,3 +13,2 @@ password: string;

getGuesses(password: string, optimalSequenceLength: number): number;
};
export default _default;
}

@@ -33,5 +33,5 @@ import utils from './utils.esm.js';

// than previously encountered sequences, updating state if so.
update(match, sequenceLength) {
update(options, match, sequenceLength) {
const k = match.j;
const estimatedMatch = estimateGuesses(match, this.password);
const estimatedMatch = estimateGuesses(options, match, this.password);
let pi = estimatedMatch.guesses;

@@ -71,6 +71,6 @@ if (sequenceLength > 1) {

// helper: evaluate bruteforce matches ending at passwordCharIndex.
bruteforceUpdate(passwordCharIndex) {
bruteforceUpdate(options, passwordCharIndex) {
// see if a single bruteforce match spanning the passwordCharIndex-prefix is optimal.
let match = this.makeBruteforceMatch(0, passwordCharIndex);
this.update(match, 1);
this.update(options, match, 1);
for (let i = 1; i <= passwordCharIndex; i += 1) {

@@ -91,3 +91,3 @@ // generate passwordCharIndex bruteforce matches, spanning from (i=1, j=passwordCharIndex) up to (i=passwordCharIndex, j=passwordCharIndex).

// try adding m to this length-sequenceLength sequence.
this.update(match, parseInt(sequenceLength, 10) + 1);
this.update(options, match, parseInt(sequenceLength, 10) + 1);
}

@@ -126,3 +126,6 @@ });

};
var scoring = {
class Scoring {
constructor(options) {
this.options = options;
}
// ------------------------------------------------------------------------------

@@ -178,2 +181,3 @@ // search --- most guessable match sequence -------------------------------------

// optimal.m[k][sequenceLength] is undefined.
// @ts-ignore
m: scoringHelper.fillArray(passwordLength, 'object'),

@@ -190,9 +194,9 @@ // same structure as optimal.m -- holds the product term Prod(m.guesses for m in sequence).

Object.keys(scoringHelper.optimal.m[match.i - 1]).forEach(sequenceLength => {
scoringHelper.update(match, parseInt(sequenceLength, 10) + 1);
scoringHelper.update(this.options, match, parseInt(sequenceLength, 10) + 1);
});
} else {
scoringHelper.update(match, 1);
scoringHelper.update(this.options, match, 1);
}
});
scoringHelper.bruteforceUpdate(k);
scoringHelper.bruteforceUpdate(this.options, k);
}

@@ -208,3 +212,3 @@ const optimalMatchSequence = scoringHelper.unwind(passwordLength);

};
},
}
getGuesses(password, optimalSequenceLength) {

@@ -220,5 +224,5 @@ const passwordLength = password.length;

}
};
}
export { scoring as default };
export { Scoring as default };
//# sourceMappingURL=index.esm.js.map

@@ -35,5 +35,5 @@ 'use strict';

// than previously encountered sequences, updating state if so.
update(match, sequenceLength) {
update(options, match, sequenceLength) {
const k = match.j;
const estimatedMatch = estimate(match, this.password);
const estimatedMatch = estimate(options, match, this.password);
let pi = estimatedMatch.guesses;

@@ -73,6 +73,6 @@ if (sequenceLength > 1) {

// helper: evaluate bruteforce matches ending at passwordCharIndex.
bruteforceUpdate(passwordCharIndex) {
bruteforceUpdate(options, passwordCharIndex) {
// see if a single bruteforce match spanning the passwordCharIndex-prefix is optimal.
let match = this.makeBruteforceMatch(0, passwordCharIndex);
this.update(match, 1);
this.update(options, match, 1);
for (let i = 1; i <= passwordCharIndex; i += 1) {

@@ -93,3 +93,3 @@ // generate passwordCharIndex bruteforce matches, spanning from (i=1, j=passwordCharIndex) up to (i=passwordCharIndex, j=passwordCharIndex).

// try adding m to this length-sequenceLength sequence.
this.update(match, parseInt(sequenceLength, 10) + 1);
this.update(options, match, parseInt(sequenceLength, 10) + 1);
}

@@ -128,3 +128,6 @@ });

};
var scoring = {
class Scoring {
constructor(options) {
this.options = options;
}
// ------------------------------------------------------------------------------

@@ -180,2 +183,3 @@ // search --- most guessable match sequence -------------------------------------

// optimal.m[k][sequenceLength] is undefined.
// @ts-ignore
m: scoringHelper.fillArray(passwordLength, 'object'),

@@ -192,9 +196,9 @@ // same structure as optimal.m -- holds the product term Prod(m.guesses for m in sequence).

Object.keys(scoringHelper.optimal.m[match.i - 1]).forEach(sequenceLength => {
scoringHelper.update(match, parseInt(sequenceLength, 10) + 1);
scoringHelper.update(this.options, match, parseInt(sequenceLength, 10) + 1);
});
} else {
scoringHelper.update(match, 1);
scoringHelper.update(this.options, match, 1);
}
});
scoringHelper.bruteforceUpdate(k);
scoringHelper.bruteforceUpdate(this.options, k);
}

@@ -210,3 +214,3 @@ const optimalMatchSequence = scoringHelper.unwind(passwordLength);

};
},
}
getGuesses(password, optimalSequenceLength) {

@@ -222,5 +226,5 @@ const passwordLength = password.length;

}
};
}
module.exports = scoring;
module.exports = Scoring;
//# sourceMappingURL=index.js.map

@@ -24,3 +24,2 @@ var utils = {

},
log2(n) {

@@ -27,0 +26,0 @@ return Math.log(n) / Math.log(2);

@@ -26,3 +26,2 @@ 'use strict';

},
log2(n) {

@@ -29,0 +28,0 @@ return Math.log(n) / Math.log(2);

@@ -1,12 +0,16 @@

import { CrackTimesDisplay, CrackTimesSeconds, Score } from './types';
declare class TimeEstimates {
translate(displayStr: string, value: number | undefined): string;
import Options from './Options';
import { CrackTimes, Score, TimeEstimationValues } from './types';
export declare const timeEstimationValuesDefaults: TimeEstimationValues;
export declare const checkTimeEstimationValues: (timeEstimationValues: TimeEstimationValues) => void;
export declare class TimeEstimates {
private options;
constructor(options: Options);
estimateAttackTimes(guesses: number): {
crackTimesSeconds: CrackTimesSeconds;
crackTimesDisplay: CrackTimesDisplay;
crackTimes: CrackTimes;
score: Score;
};
guessesToScore(guesses: number): Score;
displayTime(seconds: number): string;
private calculateCrackTimesSeconds;
private guessesToScore;
private displayTime;
private translate;
}
export default TimeEstimates;

@@ -1,3 +0,1 @@

import { zxcvbnOptions } from './Options.esm.js';
const SECOND = 1;

@@ -19,2 +17,26 @@ const MINUTE = SECOND * 60;

};
const timeEstimationValuesDefaults = {
scoring: {
0: 1e3,
1: 1e6,
2: 1e8,
3: 1e10
},
attackTime: {
onlineThrottlingXPerHour: 100,
onlineNoThrottlingXPerSecond: 10,
offlineSlowHashingXPerSecond: 1e4,
offlineFastHashingXPerSecond: 1e10
}
};
const checkTimeEstimationValues = timeEstimationValues => {
Object.entries(timeEstimationValues).forEach(([key, data]) => {
Object.entries(data).forEach(([subKey, value]) => {
// @ts-ignore
if (value < timeEstimationValuesDefaults[key][subKey]) {
throw new Error('Time estimation values are not to be allowed to be less than default');
}
});
});
};
/*

@@ -26,50 +48,52 @@ * -------------------------------------------------------------------------------

class TimeEstimates {
translate(displayStr, value) {
let key = displayStr;
if (value !== undefined && value !== 1) {
key += 's';
}
const {
timeEstimation
} = zxcvbnOptions.translations;
return timeEstimation[key].replace('{base}', `${value}`);
constructor(options) {
this.options = options;
}
estimateAttackTimes(guesses) {
const crackTimesSeconds = {
onlineThrottling100PerHour: guesses / (100 / 3600),
onlineNoThrottling10PerSecond: guesses / 10,
offlineSlowHashing1e4PerSecond: guesses / 1e4,
offlineFastHashing1e10PerSecond: guesses / 1e10
};
const crackTimesDisplay = {
onlineThrottling100PerHour: '',
onlineNoThrottling10PerSecond: '',
offlineSlowHashing1e4PerSecond: '',
offlineFastHashing1e10PerSecond: ''
};
Object.keys(crackTimesSeconds).forEach(scenario => {
const seconds = crackTimesSeconds[scenario];
crackTimesDisplay[scenario] = this.displayTime(seconds);
});
const crackTimesSeconds = this.calculateCrackTimesSeconds(guesses);
const crackTimes = Object.keys(crackTimesSeconds).reduce((previousValue, crackTime) => {
const usedScenario = crackTime;
const seconds = crackTimesSeconds[usedScenario];
const {
base,
displayStr
} = this.displayTime(seconds);
// eslint-disable-next-line no-param-reassign
previousValue[usedScenario] = {
base,
seconds,
display: this.translate(displayStr, base)
};
return previousValue;
}, {});
return {
crackTimesSeconds,
crackTimesDisplay,
crackTimes,
score: this.guessesToScore(guesses)
};
}
calculateCrackTimesSeconds(guesses) {
const attackTimesOptions = this.options.timeEstimationValues.attackTime;
return {
onlineThrottlingXPerHour: guesses / (attackTimesOptions.onlineThrottlingXPerHour / 3600),
onlineNoThrottlingXPerSecond: guesses / attackTimesOptions.onlineNoThrottlingXPerSecond,
offlineSlowHashingXPerSecond: guesses / attackTimesOptions.offlineSlowHashingXPerSecond,
offlineFastHashingXPerSecond: guesses / attackTimesOptions.offlineFastHashingXPerSecond
};
}
guessesToScore(guesses) {
const scoringOptions = this.options.timeEstimationValues.scoring;
const DELTA = 5;
if (guesses < 1e3 + DELTA) {
if (guesses < scoringOptions[0] + DELTA) {
// risky password: "too guessable"
return 0;
}
if (guesses < 1e6 + DELTA) {
if (guesses < scoringOptions[1] + DELTA) {
// modest protection from throttled online attacks: "very guessable"
return 1;
}
if (guesses < 1e8 + DELTA) {
if (guesses < scoringOptions[2] + DELTA) {
// modest protection from unthrottled online attacks: "somewhat guessable"
return 2;
}
if (guesses < 1e10 + DELTA) {
if (guesses < scoringOptions[3] + DELTA) {
// modest protection from offline attacks: "safely unguessable"

@@ -84,3 +108,3 @@ // assuming a salted, slow hash function like bcrypt, scrypt, PBKDF2, argon, etc

let displayStr = 'centuries';
let base;
let base = null;
const timeKeys = Object.keys(times);

@@ -96,7 +120,20 @@ const foundIndex = timeKeys.findIndex(time => seconds < times[time]);

}
return this.translate(displayStr, base);
return {
base,
displayStr
};
}
translate(displayStr, value) {
let key = displayStr;
if (value !== null && value !== 1) {
key += 's';
}
const {
timeEstimation
} = this.options.translations;
return timeEstimation[key].replace('{base}', `${value}`);
}
}
export { TimeEstimates as default };
export { TimeEstimates, checkTimeEstimationValues, timeEstimationValuesDefaults };
//# sourceMappingURL=TimeEstimates.esm.js.map
'use strict';
var Options = require('./Options.js');
const SECOND = 1;

@@ -21,2 +19,26 @@ const MINUTE = SECOND * 60;

};
const timeEstimationValuesDefaults = {
scoring: {
0: 1e3,
1: 1e6,
2: 1e8,
3: 1e10
},
attackTime: {
onlineThrottlingXPerHour: 100,
onlineNoThrottlingXPerSecond: 10,
offlineSlowHashingXPerSecond: 1e4,
offlineFastHashingXPerSecond: 1e10
}
};
const checkTimeEstimationValues = timeEstimationValues => {
Object.entries(timeEstimationValues).forEach(([key, data]) => {
Object.entries(data).forEach(([subKey, value]) => {
// @ts-ignore
if (value < timeEstimationValuesDefaults[key][subKey]) {
throw new Error('Time estimation values are not to be allowed to be less than default');
}
});
});
};
/*

@@ -28,50 +50,52 @@ * -------------------------------------------------------------------------------

class TimeEstimates {
translate(displayStr, value) {
let key = displayStr;
if (value !== undefined && value !== 1) {
key += 's';
}
const {
timeEstimation
} = Options.zxcvbnOptions.translations;
return timeEstimation[key].replace('{base}', `${value}`);
constructor(options) {
this.options = options;
}
estimateAttackTimes(guesses) {
const crackTimesSeconds = {
onlineThrottling100PerHour: guesses / (100 / 3600),
onlineNoThrottling10PerSecond: guesses / 10,
offlineSlowHashing1e4PerSecond: guesses / 1e4,
offlineFastHashing1e10PerSecond: guesses / 1e10
};
const crackTimesDisplay = {
onlineThrottling100PerHour: '',
onlineNoThrottling10PerSecond: '',
offlineSlowHashing1e4PerSecond: '',
offlineFastHashing1e10PerSecond: ''
};
Object.keys(crackTimesSeconds).forEach(scenario => {
const seconds = crackTimesSeconds[scenario];
crackTimesDisplay[scenario] = this.displayTime(seconds);
});
const crackTimesSeconds = this.calculateCrackTimesSeconds(guesses);
const crackTimes = Object.keys(crackTimesSeconds).reduce((previousValue, crackTime) => {
const usedScenario = crackTime;
const seconds = crackTimesSeconds[usedScenario];
const {
base,
displayStr
} = this.displayTime(seconds);
// eslint-disable-next-line no-param-reassign
previousValue[usedScenario] = {
base,
seconds,
display: this.translate(displayStr, base)
};
return previousValue;
}, {});
return {
crackTimesSeconds,
crackTimesDisplay,
crackTimes,
score: this.guessesToScore(guesses)
};
}
calculateCrackTimesSeconds(guesses) {
const attackTimesOptions = this.options.timeEstimationValues.attackTime;
return {
onlineThrottlingXPerHour: guesses / (attackTimesOptions.onlineThrottlingXPerHour / 3600),
onlineNoThrottlingXPerSecond: guesses / attackTimesOptions.onlineNoThrottlingXPerSecond,
offlineSlowHashingXPerSecond: guesses / attackTimesOptions.offlineSlowHashingXPerSecond,
offlineFastHashingXPerSecond: guesses / attackTimesOptions.offlineFastHashingXPerSecond
};
}
guessesToScore(guesses) {
const scoringOptions = this.options.timeEstimationValues.scoring;
const DELTA = 5;
if (guesses < 1e3 + DELTA) {
if (guesses < scoringOptions[0] + DELTA) {
// risky password: "too guessable"
return 0;
}
if (guesses < 1e6 + DELTA) {
if (guesses < scoringOptions[1] + DELTA) {
// modest protection from throttled online attacks: "very guessable"
return 1;
}
if (guesses < 1e8 + DELTA) {
if (guesses < scoringOptions[2] + DELTA) {
// modest protection from unthrottled online attacks: "somewhat guessable"
return 2;
}
if (guesses < 1e10 + DELTA) {
if (guesses < scoringOptions[3] + DELTA) {
// modest protection from offline attacks: "safely unguessable"

@@ -86,3 +110,3 @@ // assuming a salted, slow hash function like bcrypt, scrypt, PBKDF2, argon, etc

let displayStr = 'centuries';
let base;
let base = null;
const timeKeys = Object.keys(times);

@@ -98,7 +122,22 @@ const foundIndex = timeKeys.findIndex(time => seconds < times[time]);

}
return this.translate(displayStr, base);
return {
base,
displayStr
};
}
translate(displayStr, value) {
let key = displayStr;
if (value !== null && value !== 1) {
key += 's';
}
const {
timeEstimation
} = this.options.translations;
return timeEstimation[key].replace('{base}', `${value}`);
}
}
module.exports = TimeEstimates;
exports.TimeEstimates = TimeEstimates;
exports.checkTimeEstimationValues = checkTimeEstimationValues;
exports.timeEstimationValuesDefaults = timeEstimationValuesDefaults;
//# sourceMappingURL=TimeEstimates.js.map

@@ -7,2 +7,3 @@ import translationKeys from './data/translationKeys';

import { PasswordChanges } from './matcher/dictionary/variants/matching/unmunger/getCleanPasswords';
import Options from './Options';
export type TranslationKeys = typeof translationKeys;

@@ -38,3 +39,3 @@ export type L33tTableDefault = typeof l33tTableDefault;

rank: number;
dictionaryName: DictionaryNames;
dictionaryName: DictionaryNames | string;
reversed: boolean;

@@ -93,18 +94,23 @@ l33t: boolean;

export interface Optimal {
m: Match;
pi: Match;
g: Match;
m: Match[];
pi: Record<string, number>[];
g: Record<string, number>[];
}
export interface CrackTime {
base: number | null;
seconds: number;
display: string;
}
export interface CrackTimes {
onlineThrottlingXPerHour: CrackTime;
onlineNoThrottlingXPerSecond: CrackTime;
offlineSlowHashingXPerSecond: CrackTime;
offlineFastHashingXPerSecond: CrackTime;
}
export interface CrackTimesSeconds {
onlineThrottling100PerHour: number;
onlineNoThrottling10PerSecond: number;
offlineSlowHashing1e4PerSecond: number;
offlineFastHashing1e10PerSecond: number;
onlineThrottlingXPerHour: number;
onlineNoThrottlingXPerSecond: number;
offlineSlowHashingXPerSecond: number;
offlineFastHashingXPerSecond: number;
}
export interface CrackTimesDisplay {
onlineThrottling100PerHour: string;
onlineNoThrottling10PerSecond: string;
offlineSlowHashing1e4PerSecond: string;
offlineFastHashing1e10PerSecond: string;
}
export interface FeedbackType {

@@ -126,2 +132,16 @@ warning: string | null;

}
export interface TimeEstimationValues {
scoring: {
0: number;
1: number;
2: number;
3: number;
};
attackTime: {
onlineThrottlingXPerHour: number;
onlineNoThrottlingXPerSecond: number;
offlineSlowHashingXPerSecond: number;
offlineFastHashingXPerSecond: number;
};
}
export interface OptionsType {

@@ -164,2 +184,6 @@ /**

maxLength?: number;
/**
* @description Define the values to calculate the scoring and attack times. DO NOT CHANGE unless you know what you are doing. The default values are just fine as long as you are using a strong, slow hash function. Can be adjusted to account for increasingly powerful attacker hardware.
*/
timeEstimationValues?: TimeEstimationValues;
}

@@ -172,4 +196,8 @@ export interface RankedDictionary {

}
export type DefaultFeedbackFunction = (match: MatchEstimated, isSoleMatch?: boolean) => FeedbackType | null;
export type DefaultScoringFunction = (match: MatchExtended | MatchEstimated) => number | DictionaryReturn;
export type DefaultFeedbackFunction = (options: Options, match: MatchEstimated, isSoleMatch?: boolean) => FeedbackType | null;
export type DefaultScoringFunction = (match: MatchExtended | MatchEstimated, options: Options) => number | DictionaryReturn;
export interface UserInputsOptions {
rankedDictionary: RankedDictionary;
rankedDictionaryMaxWordSize: number;
}
export interface MatchOptions {

@@ -181,5 +209,6 @@ password: string;

omniMatch: Matching;
userInputsOptions?: UserInputsOptions;
}
export type MatchingType = new () => {
match({ password, omniMatch, }: MatchOptions): MatchExtended[] | Promise<MatchExtended[]>;
export type MatchingType = new (options: Options) => {
match({ password, omniMatch, userInputsOptions, }: MatchOptions): MatchExtended[] | Promise<MatchExtended[]>;
};

@@ -197,4 +226,3 @@ export interface Matcher {

feedback: FeedbackType;
crackTimesSeconds: CrackTimesSeconds;
crackTimesDisplay: CrackTimesDisplay;
crackTimes: CrackTimes;
score: Score;

@@ -201,0 +229,0 @@ password: string;

@@ -1,2 +0,2 @@

this.zxcvbnts=this.zxcvbnts||{},this.zxcvbnts.core=function(t){"use strict";const e=(t,e)=>t.push.apply(t,e),s=t=>t.sort(((t,e)=>t.i-e.i||t.j-e.j)),n=t=>{const e={};let s=1;return t.forEach((t=>{e[t]=s,s+=1})),e};const r={4:[[1,2],[2,3]],5:[[1,3],[2,3],[2,4]],6:[[1,2],[2,4],[4,5]],7:[[1,3],[2,3],[4,5],[4,6]],8:[[2,4],[4,6]]},a=/^[A-Z\xbf-\xdf][^A-Z\xbf-\xdf]+$/,i=/^[^A-Z\xbf-\xdf]+[A-Z\xbf-\xdf]$/,o=/^[A-Z\xbf-\xdf]+$/,c=/^[^a-z\xdf-\xff]+$/,l=/^[a-z\xdf-\xff]+$/,h=/^[^A-Z\xbf-\xdf]+$/,u=/[a-z\xdf-\xff]/,d=/[A-Z\xbf-\xdf]/,g=/[^A-Za-z\xbf-\xdf]/gi,p=/^\d+$/,f=(new Date).getFullYear(),m={recentYear:/19\d\d|200\d|201\d|202\d/g},b=[" ",",",";",":","|","/","\\","_",".","-"],y=b.length;class k{match({password:t}){const e=[...this.getMatchesWithoutSeparator(t),...this.getMatchesWithSeparator(t)],n=this.filterNoise(e);return s(n)}getMatchesWithSeparator(t){const e=[],s=/^(\d{1,4})([\s/\\_.-])(\d{1,2})\2(\d{1,4})$/;for(let n=0;n<=Math.abs(t.length-6);n+=1)for(let r=n+5;r<=n+9&&!(r>=t.length);r+=1){const a=t.slice(n,+r+1||9e9),i=s.exec(a);if(null!=i){const t=this.mapIntegersToDayMonthYear([parseInt(i[1],10),parseInt(i[3],10),parseInt(i[4],10)]);null!=t&&e.push({pattern:"date",token:a,i:n,j:r,separator:i[2],year:t.year,month:t.month,day:t.day})}}return e}getMatchesWithoutSeparator(t){const e=[],s=/^\d{4,8}$/,n=t=>Math.abs(t.year-f);for(let a=0;a<=Math.abs(t.length-4);a+=1)for(let i=a+3;i<=a+7&&!(i>=t.length);i+=1){const o=t.slice(a,+i+1||9e9);if(s.exec(o)){const t=[],s=o.length;if(r[s].forEach((([e,s])=>{const n=this.mapIntegersToDayMonthYear([parseInt(o.slice(0,e),10),parseInt(o.slice(e,s),10),parseInt(o.slice(s),10)]);null!=n&&t.push(n)})),t.length>0){let s=t[0],r=n(t[0]);t.slice(1).forEach((t=>{const e=n(t);e<r&&(s=t,r=e)})),e.push({pattern:"date",token:o,i:a,j:i,separator:"",year:s.year,month:s.month,day:s.day})}}}return e}filterNoise(t){return t.filter((e=>{let s=!1;const n=t.length;for(let r=0;r<n;r+=1){const n=t[r];if(e!==n&&n.i<=e.i&&n.j>=e.j){s=!0;break}}return!s}))}mapIntegersToDayMonthYear(t){if(t[1]>31||t[1]<=0)return null;let e=0,s=0,n=0;for(let r=0,a=t.length;r<a;r+=1){const a=t[r];if(a>99&&a<1e3||a>2050)return null;a>31&&(s+=1),a>12&&(e+=1),a<=0&&(n+=1)}return s>=2||3===e||n>=2?null:this.getDayMonth(t)}getDayMonth(t){const e=[[t[2],t.slice(0,2)],[t[0],t.slice(1,3)]],s=e.length;for(let t=0;t<s;t+=1){const[s,n]=e[t];if(1e3<=s&&s<=2050){const t=this.mapIntegersToDayMonth(n);return null!=t?{year:s,month:t.month,day:t.day}:null}}for(let t=0;t<s;t+=1){const[s,n]=e[t],r=this.mapIntegersToDayMonth(n);if(null!=r)return{year:this.twoToFourDigitYear(s),month:r.month,day:r.day}}return null}mapIntegersToDayMonth(t){const e=[t,t.slice().reverse()];for(let t=0;t<e.length;t+=1){const s=e[t],n=s[0],r=s[1];if(n>=1&&n<=31&&r>=1&&r<=12)return{day:n,month:r}}return null}twoToFourDigitYear(t){return t>99?t:t>50?t+1900:t+2e3}}const w=new Uint32Array(65536),x=(t,e)=>{if(t.length<e.length){const s=e;e=t,t=s}return 0===e.length?t.length:t.length<=32?((t,e)=>{const s=t.length,n=e.length,r=1<<s-1;let a=-1,i=0,o=s,c=s;for(;c--;)w[t.charCodeAt(c)]|=1<<c;for(c=0;c<n;c++){let t=w[e.charCodeAt(c)];const s=t|i;t|=(t&a)+a^a,i|=~(t|a),a&=t,i&r&&o++,a&r&&o--,i=i<<1|1,a=a<<1|~(s|i),i&=s}for(c=s;c--;)w[t.charCodeAt(c)]=0;return o})(t,e):((t,e)=>{const s=e.length,n=t.length,r=[],a=[],i=Math.ceil(s/32),o=Math.ceil(n/32);for(let t=0;t<i;t++)a[t]=-1,r[t]=0;let c=0;for(;c<o-1;c++){let i=0,o=-1;const l=32*c,h=Math.min(32,n)+l;for(let e=l;e<h;e++)w[t.charCodeAt(e)]|=1<<e;for(let t=0;t<s;t++){const s=w[e.charCodeAt(t)],n=a[t/32|0]>>>t&1,c=r[t/32|0]>>>t&1,l=s|i,h=((s|c)&o)+o^o|s|c;let u=i|~(h|o),d=o&h;u>>>31^n&&(a[t/32|0]^=1<<t),d>>>31^c&&(r[t/32|0]^=1<<t),u=u<<1|n,d=d<<1|c,o=d|~(l|u),i=u&l}for(let e=l;e<h;e++)w[t.charCodeAt(e)]=0}let l=0,h=-1;const u=32*c,d=Math.min(32,n-u)+u;for(let e=u;e<d;e++)w[t.charCodeAt(e)]|=1<<e;let g=n;for(let t=0;t<s;t++){const s=w[e.charCodeAt(t)],i=a[t/32|0]>>>t&1,o=r[t/32|0]>>>t&1,c=s|l,u=((s|o)&h)+h^h|s|o;let d=l|~(u|h),p=h&u;g+=d>>>n-1&1,g-=p>>>n-1&1,d>>>31^i&&(a[t/32|0]^=1<<t),p>>>31^o&&(r[t/32|0]^=1<<t),d=d<<1|i,p=p<<1|o,h=p|~(c|d),l=d&c}for(let e=u;e<d;e++)w[t.charCodeAt(e)]=0;return g})(t,e)},M=(t,e,s)=>{let n=0;const r=Object.keys(e).find((e=>{const r=((t,e,s)=>{const n=t.length<=e.length,r=t.length<=s;return n||r?Math.ceil(t.length/4):s})(t,e,s);if(Math.abs(t.length-e.length)>r)return!1;const a=x(t,e),i=a<=r;return i&&(n=a),i}));return r?{levenshteinDistance:n,levenshteinDistanceEntry:r}:{}};var S={a:["4","@"],b:["8"],c:["(","{","[","<"],d:["6","|)"],e:["3"],f:["#"],g:["6","9","&"],h:["#","|-|"],i:["1","!","|"],k:["<","|<"],l:["!","1","|","7"],m:["^^","nn","2n","/\\\\/\\\\"],n:["//"],o:["0","()"],q:["9"],u:["|_|"],s:["$","5"],t:["+","7"],v:["<",">","/"],w:["^/","uu","vv","2u","2v","\\\\/\\\\/"],x:["%","><"],z:["2"]},v={warnings:{straightRow:"straightRow",keyPattern:"keyPattern",simpleRepeat:"simpleRepeat",extendedRepeat:"extendedRepeat",sequences:"sequences",recentYears:"recentYears",dates:"dates",topTen:"topTen",topHundred:"topHundred",common:"common",similarToCommon:"similarToCommon",wordByItself:"wordByItself",namesByThemselves:"namesByThemselves",commonNames:"commonNames",userInputs:"userInputs",pwned:"pwned"},suggestions:{l33t:"l33t",reverseWords:"reverseWords",allUppercase:"allUppercase",capitalization:"capitalization",dates:"dates",recentYears:"recentYears",associatedYears:"associatedYears",sequences:"sequences",repeated:"repeated",longerKeyboardPattern:"longerKeyboardPattern",anotherWord:"anotherWord",useWords:"useWords",noNeed:"noNeed",pwned:"pwned"},timeEstimation:{ltSecond:"ltSecond",second:"second",seconds:"seconds",minute:"minute",minutes:"minutes",hour:"hour",hours:"hours",day:"day",days:"days",month:"month",months:"months",year:"year",years:"years",centuries:"centuries"}};class T{constructor(t=[]){this.parents=t,this.children=new Map}addSub(t,...e){const s=t.charAt(0);this.children.has(s)||this.children.set(s,new T([...this.parents,s]));let n=this.children.get(s);for(let e=1;e<t.length;e+=1){const s=t.charAt(e);n.hasChild(s)||n.addChild(s),n=n.getChild(s)}return n.subs=(n.subs||[]).concat(e),this}getChild(t){return this.children.get(t)}isTerminal(){return!!this.subs}addChild(t){this.hasChild(t)||this.children.set(t,new T([...this.parents,t]))}hasChild(t){return this.children.has(t)}}var j=(t,e)=>(Object.entries(t).forEach((([t,s])=>{s.forEach((s=>{e.addSub(s,t)}))})),e);class I{constructor(){this.matchers={},this.l33tTable=S,this.trieNodeRoot=j(S,new T),this.dictionary={userInputs:[]},this.rankedDictionaries={},this.rankedDictionariesMaxWordSize={},this.translations=v,this.graphs={},this.useLevenshteinDistance=!1,this.levenshteinThreshold=2,this.l33tMaxSubstitutions=100,this.maxLength=256,this.setRankedDictionaries()}setOptions(t={}){t.l33tTable&&(this.l33tTable=t.l33tTable,this.trieNodeRoot=j(t.l33tTable,new T)),t.dictionary&&(this.dictionary=t.dictionary,this.setRankedDictionaries()),t.translations&&this.setTranslations(t.translations),t.graphs&&(this.graphs=t.graphs),void 0!==t.useLevenshteinDistance&&(this.useLevenshteinDistance=t.useLevenshteinDistance),void 0!==t.levenshteinThreshold&&(this.levenshteinThreshold=t.levenshteinThreshold),void 0!==t.l33tMaxSubstitutions&&(this.l33tMaxSubstitutions=t.l33tMaxSubstitutions),void 0!==t.maxLength&&(this.maxLength=t.maxLength)}setTranslations(t){if(!this.checkCustomTranslations(t))throw new Error("Invalid translations object fallback to keys");this.translations=t}checkCustomTranslations(t){let e=!0;return Object.keys(v).forEach((s=>{if(s in t){const n=s;Object.keys(v[n]).forEach((s=>{s in t[n]||(e=!1)}))}else e=!1})),e}setRankedDictionaries(){const t={},e={};Object.keys(this.dictionary).forEach((s=>{t[s]=n(this.dictionary[s]),e[s]=this.getRankedDictionariesMaxWordSize(this.dictionary[s])})),this.rankedDictionaries=t,this.rankedDictionariesMaxWordSize=e}getRankedDictionariesMaxWordSize(t){const e=t.map((t=>"string"!=typeof t?t.toString().length:t.length));return 0===e.length?0:e.reduce(((t,e)=>Math.max(t,e)),-1/0)}buildSanitizedRankedDictionary(t){const e=[];return t.forEach((t=>{const s=typeof t;"string"!==s&&"number"!==s&&"boolean"!==s||e.push(t.toString().toLowerCase())})),n(e)}extendUserInputsDictionary(t){this.dictionary.userInputs||(this.dictionary.userInputs=[]);const e=[...this.dictionary.userInputs,...t];this.rankedDictionaries.userInputs=this.buildSanitizedRankedDictionary(e),this.rankedDictionariesMaxWordSize.userInputs=this.getRankedDictionariesMaxWordSize(e)}addMatcher(t,e){this.matchers[t]?console.info(`Matcher ${t} already exists`):this.matchers[t]=e}}const A=new I;class C{constructor(t){this.defaultMatch=t}match({password:t}){const e=t.split("").reverse().join("");return this.defaultMatch({password:e}).map((e=>({...e,token:e.token.split("").reverse().join(""),reversed:!0,i:t.length-1-e.j,j:t.length-1-e.i})))}}class D{constructor({substr:t,limit:e,trieRoot:s}){this.buffer=[],this.finalPasswords=[],this.substr=t,this.limit=e,this.trieRoot=s}getAllPossibleSubsAtIndex(t){const e=[];let s=this.trieRoot;for(let n=t;n<this.substr.length;n+=1){const t=this.substr.charAt(n);if(s=s.getChild(t),!s)break;e.push(s)}return e}helper({onlyFullSub:t,isFullSub:e,index:s,subIndex:n,changes:r,lastSubLetter:a,consecutiveSubCount:i}){if(this.finalPasswords.length>=this.limit)return;if(s===this.substr.length)return void(t===e&&this.finalPasswords.push({password:this.buffer.join(""),changes:r}));const o=[...this.getAllPossibleSubsAtIndex(s)];let c=!1;for(let l=s+o.length-1;l>=s;l-=1){const h=o[l-s];if(h.isTerminal()){if(a===h.parents.join("")&&i>=3)continue;c=!0;const s=h.subs;for(const o of s){this.buffer.push(o);const s=r.concat({i:n,letter:o,substitution:h.parents.join("")});if(this.helper({onlyFullSub:t,isFullSub:e,index:l+1,subIndex:n+o.length,changes:s,lastSubLetter:h.parents.join(""),consecutiveSubCount:a===h.parents.join("")?i+1:1}),this.buffer.pop(),this.finalPasswords.length>=this.limit)return}}}if(!t||!c){const o=this.substr.charAt(s);this.buffer.push(o),this.helper({onlyFullSub:t,isFullSub:e&&!c,index:s+1,subIndex:n+1,changes:r,lastSubLetter:a,consecutiveSubCount:i}),this.buffer.pop()}}getAll(){return this.helper({onlyFullSub:!0,isFullSub:!0,index:0,subIndex:0,changes:[],lastSubLetter:void 0,consecutiveSubCount:0}),this.helper({onlyFullSub:!1,isFullSub:!0,index:0,subIndex:0,changes:[],lastSubLetter:void 0,consecutiveSubCount:0}),this.finalPasswords}}class E{constructor(t){this.defaultMatch=t}isAlreadyIncluded(t,e){return t.some((t=>Object.entries(t).every((([t,s])=>"subs"===t||s===e[t]))))}match({password:t}){const e=[],s=((t,e,s)=>new D({substr:t,limit:e,trieRoot:s}).getAll())(t,A.l33tMaxSubstitutions,A.trieNodeRoot);let n=!1,r=!0;return s.forEach((s=>{if(n)return;const a=this.defaultMatch({password:s.password,useLevenshtein:r});r=!1,a.forEach((r=>{n||(n=0===r.i&&r.j===t.length-1);const a=((t,e,s)=>{const n=t.changes.filter((t=>t.i<e)).reduce(((t,e)=>t-e.letter.length+e.substitution.length),e),r=t.changes.filter((t=>t.i>=e&&t.i<=s)),a=r.reduce(((t,e)=>t-e.letter.length+e.substitution.length),s-e+n),i=[],o=[];return r.forEach((t=>{i.findIndex((e=>e.letter===t.letter&&e.substitution===t.substitution))<0&&(i.push({letter:t.letter,substitution:t.substitution}),o.push(`${t.substitution} -> ${t.letter}`))})),{i:n,j:a,subs:i,subDisplay:o.join(", ")}})(s,r.i,r.j),i=t.slice(a.i,+a.j+1||9e9),o={...r,l33t:!0,token:i,...a},c=this.isAlreadyIncluded(e,o);i.toLowerCase()===r.matchedWord||c||e.push(o)}))})),e.filter((t=>t.token.length>1))}}class L{constructor(){this.l33t=new E(this.defaultMatch),this.reverse=new C(this.defaultMatch)}match({password:t}){const e=[...this.defaultMatch({password:t}),...this.reverse.match({password:t}),...this.l33t.match({password:t})];return s(e)}defaultMatch({password:t,useLevenshtein:e=!0}){const s=[],n=t.length,r=t.toLowerCase();return Object.keys(A.rankedDictionaries).forEach((a=>{const i=A.rankedDictionaries[a],o=A.rankedDictionariesMaxWordSize[a],c=Math.min(o,n);for(let o=0;o<n;o+=1){const l=Math.min(o+c,n);for(let c=o;c<l;c+=1){const l=r.slice(o,+c+1||9e9),h=l in i;let u={};const d=0===o&&c===n-1;A.useLevenshteinDistance&&d&&!h&&e&&(u=M(l,i,A.levenshteinThreshold));const g=0!==Object.keys(u).length;if(h||g){const e=i[g?u.levenshteinDistanceEntry:l];s.push({pattern:"dictionary",i:o,j:c,token:t.slice(o,+c+1||9e9),matchedWord:l,rank:e,dictionaryName:a,reversed:!1,l33t:!1,...u})}}}})),s}}class P{match({password:t,regexes:e=m}){const n=[];return Object.keys(e).forEach((s=>{const r=e[s];let a;for(r.lastIndex=0;a=r.exec(t);)if(a){const t=a[0];n.push({pattern:"regex",token:t,i:a.index,j:a.index+a[0].length-1,regexName:s,regexMatch:a})}})),s(n)}}var R={nCk(t,e){let s=t;if(e>s)return 0;if(0===e)return 1;let n=1;for(let t=1;t<=e;t+=1)n*=s,n/=t,s-=1;return n},log10:t=>0===t?0:Math.log(t)/Math.log(10),log2:t=>Math.log(t)/Math.log(2),factorial(t){let e=1;for(let s=2;s<=t;s+=1)e*=s;return e}};var z=t=>{const e=t.replace(g,"");if(e.match(h)||e.toLowerCase()===e)return 1;const s=[a,i,c],n=s.length;for(let t=0;t<n;t+=1){const n=s[t];if(e.match(n))return 2}return(t=>{const e=t.split(""),s=e.filter((t=>t.match(d))).length,n=e.filter((t=>t.match(u))).length;let r=0;const a=Math.min(s,n);for(let t=1;t<=a;t+=1)r+=R.nCk(s+n,t);return r})(e)};const O=(t,e)=>{let s=0,n=t.indexOf(e);for(;n>=0;)s+=1,n=t.indexOf(e,n+e.length);return s};var N=({l33t:t,subs:e,token:s})=>{if(!t)return 1;let n=1;return e.forEach((t=>{const{subbedCount:e,unsubbedCount:r}=(({sub:t,token:e})=>{const s=e.toLowerCase();return{subbedCount:O(s,t.substitution),unsubbedCount:O(s,t.letter)}})({sub:t,token:s});if(0===e||0===r)n*=2;else{const t=Math.min(r,e);let s=0;for(let n=1;n<=t;n+=1)s+=R.nCk(r+e,n);n*=s}})),n};const F=({token:t,graph:e,turns:s})=>{const n=Object.keys(A.graphs[e]).length,r=(t=>{let e=0;return Object.keys(t).forEach((s=>{const n=t[s];e+=n.filter((t=>!!t)).length})),e/=Object.entries(t).length,e})(A.graphs[e]);let a=0;const i=t.length;for(let t=2;t<=i;t+=1){const e=Math.min(s,t-1);for(let s=1;s<=e;s+=1)a+=R.nCk(t-1,s-1)*n*r**s}return a};const q={bruteforce:({token:t})=>{let e,s=10**t.length;return s===Number.POSITIVE_INFINITY&&(s=Number.MAX_VALUE),e=1===t.length?11:51,Math.max(s,e)},date:({year:t,separator:e})=>{let s=365*Math.max(Math.abs(t-f),20);return e&&(s*=4),s},dictionary:({rank:t,reversed:e,l33t:s,subs:n,token:r,dictionaryName:a})=>{const i=t,o=z(r),c=N({l33t:s,subs:n,token:r});let l;return l="diceware"===a?3888:i*o*c*(e?2:1),{baseGuesses:i,uppercaseVariations:o,l33tVariations:c,calculation:l}},regex:({regexName:t,regexMatch:e,token:s})=>{const n={alphaLower:26,alphaUpper:26,alpha:52,alphanumeric:62,digits:10,symbols:33};return t in n?n[t]**s.length:"recentYear"===t?Math.max(Math.abs(parseInt(e[0],10)-f),20):0},repeat:({baseGuesses:t,repeatCount:e})=>t*e,sequence:({token:t,ascending:e})=>{const s=t.charAt(0);let n=0;return n=["a","A","z","Z","0","1","9"].includes(s)?4:s.match(/\d/)?10:26,e||(n*=2),n*t.length},spatial:({graph:t,token:e,shiftedCount:s,turns:n})=>{let r=F({token:e,graph:t,turns:n});if(s){const t=e.length-s;if(0===s||0===t)r*=2;else{let e=0;for(let n=1;n<=Math.min(s,t);n+=1)e+=R.nCk(s+t,n);r*=e}}return Math.round(r)},separator:()=>y};var W=(t,e)=>{const s={};if("guesses"in t&&null!=t.guesses)return t;const n=((t,e)=>{let s=1;return t.token.length<e.length&&(s=1===t.token.length?10:50),s})(t,e),r=((t,e)=>q[t]?q[t](e):A.matchers[t]&&"scoring"in A.matchers[t]?A.matchers[t].scoring(e):0)(t.pattern,t);let a=0;"number"==typeof r?a=r:"dictionary"===t.pattern&&(a=r.calculation,s.baseGuesses=r.baseGuesses,s.uppercaseVariations=r.uppercaseVariations,s.l33tVariations=r.l33tVariations);const i=Math.max(a,n);return{...t,...s,guesses:i,guessesLog10:R.log10(i)}};const Y={password:"",optimal:{},excludeAdditive:!1,separatorRegex:void 0,fillArray(t,e){const s=[];for(let n=0;n<t;n+=1){let t=[];"object"===e&&(t={}),s.push(t)}return s},makeBruteforceMatch(t,e){return{pattern:"bruteforce",token:this.password.slice(t,+e+1||9e9),i:t,j:e}},update(t,e){const s=t.j,n=W(t,this.password);let r=n.guesses;e>1&&(r*=this.optimal.pi[n.i-1][e-1]);let a=R.factorial(e)*r;this.excludeAdditive||(a+=1e4**(e-1));let i=!1;Object.keys(this.optimal.g[s]).forEach((t=>{const n=this.optimal.g[s][t];parseInt(t,10)<=e&&n<=a&&(i=!0)})),i||(this.optimal.g[s][e]=a,this.optimal.m[s][e]=n,this.optimal.pi[s][e]=r)},bruteforceUpdate(t){let e=this.makeBruteforceMatch(0,t);this.update(e,1);for(let s=1;s<=t;s+=1){e=this.makeBruteforceMatch(s,t);const n=this.optimal.m[s-1];Object.keys(n).forEach((t=>{"bruteforce"!==n[t].pattern&&this.update(e,parseInt(t,10)+1)}))}},unwind(t){const e=[];let s=t-1,n=0,r=Infinity;const a=this.optimal.g[s];for(a&&Object.keys(a).forEach((t=>{const e=a[t];e<r&&(n=parseInt(t,10),r=e)}));s>=0;){const t=this.optimal.m[s][n];e.unshift(t),s=t.i-1,n-=1}return e}};var $={mostGuessableMatchSequence(t,e,s=!1){Y.password=t,Y.excludeAdditive=s;const n=t.length;let r=Y.fillArray(n,"array");e.forEach((t=>{r[t.j].push(t)})),r=r.map((t=>t.sort(((t,e)=>t.i-e.i)))),Y.optimal={m:Y.fillArray(n,"object"),pi:Y.fillArray(n,"object"),g:Y.fillArray(n,"object")};for(let t=0;t<n;t+=1)r[t].forEach((t=>{t.i>0?Object.keys(Y.optimal.m[t.i-1]).forEach((e=>{Y.update(t,parseInt(e,10)+1)})):Y.update(t,1)})),Y.bruteforceUpdate(t);const a=Y.unwind(n),i=a.length,o=this.getGuesses(t,i);return{password:t,guesses:o,guessesLog10:R.log10(o),sequence:a}},getGuesses(t,e){const s=t.length;let n=0;return n=0===t.length?1:Y.optimal.g[s-1][e],n}};class G{match({password:t,omniMatch:e}){const s=[];let n=0;for(;n<t.length;){const r=this.getGreedyMatch(t,n),a=this.getLazyMatch(t,n);if(null==r)break;const{match:i,baseToken:o}=this.setMatchToken(r,a);if(i){const t=i.index+i[0].length-1,r=this.getBaseGuesses(o,e);s.push(this.normalizeMatch(o,t,i,r)),n=t+1}}return s.some((t=>t instanceof Promise))?Promise.all(s):s}normalizeMatch(t,e,s,n){const r={pattern:"repeat",i:s.index,j:e,token:s[0],baseToken:t,baseGuesses:0,repeatCount:s[0].length/t.length};return n instanceof Promise?n.then((t=>({...r,baseGuesses:t}))):{...r,baseGuesses:n}}getGreedyMatch(t,e){const s=/(.+)\1+/g;return s.lastIndex=e,s.exec(t)}getLazyMatch(t,e){const s=/(.+?)\1+/g;return s.lastIndex=e,s.exec(t)}setMatchToken(t,e){const s=/^(.+?)\1+$/;let n,r="";if(e&&t[0].length>e[0].length){n=t;const e=s.exec(n[0]);e&&(r=e[1])}else n=e,n&&(r=n[1]);return{match:n,baseToken:r}}getBaseGuesses(t,e){const s=e.match(t);if(s instanceof Promise)return s.then((e=>$.mostGuessableMatchSequence(t,e).guesses));return $.mostGuessableMatchSequence(t,s).guesses}}class U{constructor(){this.MAX_DELTA=5}match({password:t}){const e=[];if(1===t.length)return[];let s=0,n=null;const r=t.length;for(let a=1;a<r;a+=1){const r=t.charCodeAt(a)-t.charCodeAt(a-1);if(null==n&&(n=r),r!==n){const i=a-1;this.update({i:s,j:i,delta:n,password:t,result:e}),s=i,n=r}}return this.update({i:s,j:r-1,delta:n,password:t,result:e}),e}update({i:t,j:e,delta:s,password:n,result:r}){if(e-t>1||1===Math.abs(s)){const a=Math.abs(s);if(a>0&&a<=this.MAX_DELTA){const a=n.slice(t,+e+1||9e9),{sequenceName:i,sequenceSpace:o}=this.getSequence(a);return r.push({pattern:"sequence",i:t,j:e,token:n.slice(t,+e+1||9e9),sequenceName:i,sequenceSpace:o,ascending:s>0})}}return null}getSequence(t){let e="unicode",s=26;return l.test(t)?(e="lower",s=26):o.test(t)?(e="upper",s=26):p.test(t)&&(e="digits",s=10),{sequenceName:e,sequenceSpace:s}}}class B{constructor(){this.SHIFTED_RX=/[~!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL:"ZXCVBNM<>?]/}match({password:t}){const n=[];return Object.keys(A.graphs).forEach((s=>{const r=A.graphs[s];e(n,this.helper(t,r,s))})),s(n)}checkIfShifted(t,e,s){return!t.includes("keypad")&&this.SHIFTED_RX.test(e.charAt(s))?1:0}helper(t,e,s){let n;const r=[];let a=0;const i=t.length;for(;a<i-1;){let o=a+1,c=null,l=0;for(n=this.checkIfShifted(s,t,a);;){const h=e[t.charAt(o-1)]||[];let u=!1,d=-1,g=-1;if(o<i){const e=t.charAt(o),s=h.length;for(let t=0;t<s;t+=1){const s=h[t];if(g+=1,s){const t=s.indexOf(e);if(-1!==t){u=!0,d=g,1===t&&(n+=1),c!==d&&(l+=1,c=d);break}}}}if(!u){o-a>2&&r.push({pattern:"spatial",i:a,j:o-1,token:t.slice(a,o),graph:s,turns:l,shiftedCount:n}),a=o;break}o+=1}}return r}}const H=new RegExp(`[${b.join("")}]`);class Z{static getMostUsedSeparatorChar(t){const e=[...t.split("").filter((t=>H.test(t))).reduce(((t,e)=>{const s=t.get(e);return s?t.set(e,s+1):t.set(e,1),t}),new Map).entries()].sort((([t,e],[s,n])=>n-e));if(!e.length)return;const s=e[0];return s[1]<2?void 0:s[0]}static getSeparatorRegex(t){return new RegExp(`([^${t}\n])(${t})(?!${t})`,"g")}match({password:t}){const e=[];if(0===t.length)return e;const s=Z.getMostUsedSeparatorChar(t);if(void 0===s)return e;const n=Z.getSeparatorRegex(s);for(const r of t.matchAll(n)){if(void 0===r.index)continue;const t=r.index+1;e.push({pattern:"separator",token:s,i:t,j:t})}return e}}class _{constructor(){this.matchers={date:k,dictionary:L,regex:P,repeat:G,sequence:U,spatial:B,separator:Z}}match(t){const n=[],r=[];return[...Object.keys(this.matchers),...Object.keys(A.matchers)].forEach((s=>{if(!this.matchers[s]&&!A.matchers[s])return;const a=(new(this.matchers[s]?this.matchers[s]:A.matchers[s].Matching)).match({password:t,omniMatch:this});a instanceof Promise?(a.then((t=>{e(n,t)})),r.push(a)):e(n,a)})),r.length>0?new Promise(((t,e)=>{Promise.all(r).then((()=>{t(s(n))})).catch((t=>{e(t)}))})):s(n)}}const V=2678400,X=32140800,K={second:1,minute:60,hour:3600,day:86400,month:V,year:X,century:321408e4};class J{translate(t,e){let s=t;void 0!==e&&1!==e&&(s+="s");const{timeEstimation:n}=A.translations;return n[s].replace("{base}",`${e}`)}estimateAttackTimes(t){const e={onlineThrottling100PerHour:t/(100/3600),onlineNoThrottling10PerSecond:t/10,offlineSlowHashing1e4PerSecond:t/1e4,offlineFastHashing1e10PerSecond:t/1e10},s={onlineThrottling100PerHour:"",onlineNoThrottling10PerSecond:"",offlineSlowHashing1e4PerSecond:"",offlineFastHashing1e10PerSecond:""};return Object.keys(e).forEach((t=>{const n=e[t];s[t]=this.displayTime(n)})),{crackTimesSeconds:e,crackTimesDisplay:s,score:this.guessesToScore(t)}}guessesToScore(t){return t<1005?0:t<1000005?1:t<100000005?2:t<10000000005?3:4}displayTime(t){let e,s="centuries";const n=Object.keys(K),r=n.findIndex((e=>t<K[e]));return r>-1&&(s=n[r-1],0!==r?e=Math.round(t/K[s]):s="ltSecond"),this.translate(s,e)}}var Q=()=>null,tt=()=>({warning:A.translations.warnings.dates,suggestions:[A.translations.suggestions.dates]});const et=(t,e)=>{let s=null;const n=t.dictionaryName,r="lastnames"===n||n.toLowerCase().includes("firstnames");return"passwords"===n?s=((t,e)=>{let s=null;return!e||t.l33t||t.reversed?t.guessesLog10<=4&&(s=A.translations.warnings.similarToCommon):s=t.rank<=10?A.translations.warnings.topTen:t.rank<=100?A.translations.warnings.topHundred:A.translations.warnings.common,s})(t,e):n.includes("wikipedia")?s=((t,e)=>{let s=null;return e&&(s=A.translations.warnings.wordByItself),s})(0,e):r?s=((t,e)=>e?A.translations.warnings.namesByThemselves:A.translations.warnings.commonNames)(0,e):"userInputs"===n&&(s=A.translations.warnings.userInputs),s};var st=(t,e)=>{const s=et(t,e),n=[],r=t.token;return r.match(a)?n.push(A.translations.suggestions.capitalization):r.match(c)&&r.toLowerCase()!==r&&n.push(A.translations.suggestions.allUppercase),t.reversed&&t.token.length>=4&&n.push(A.translations.suggestions.reverseWords),t.l33t&&n.push(A.translations.suggestions.l33t),{warning:s,suggestions:n}},nt=t=>"recentYear"===t.regexName?{warning:A.translations.warnings.recentYears,suggestions:[A.translations.suggestions.recentYears,A.translations.suggestions.associatedYears]}:{warning:null,suggestions:[]},rt=t=>{let e=A.translations.warnings.extendedRepeat;return 1===t.baseToken.length&&(e=A.translations.warnings.simpleRepeat),{warning:e,suggestions:[A.translations.suggestions.repeated]}},at=()=>({warning:A.translations.warnings.sequences,suggestions:[A.translations.suggestions.sequences]}),it=t=>{let e=A.translations.warnings.keyPattern;return 1===t.turns&&(e=A.translations.warnings.straightRow),{warning:e,suggestions:[A.translations.suggestions.longerKeyboardPattern]}},ot=()=>null;const ct={warning:null,suggestions:[]};class lt{constructor(){this.matchers={bruteforce:Q,date:tt,dictionary:st,regex:nt,repeat:rt,sequence:at,spatial:it,separator:ot},this.defaultFeedback={warning:null,suggestions:[]},this.setDefaultSuggestions()}setDefaultSuggestions(){this.defaultFeedback.suggestions.push(A.translations.suggestions.useWords,A.translations.suggestions.noNeed)}getFeedback(t,e){if(0===e.length)return this.defaultFeedback;if(t>2)return ct;const s=A.translations.suggestions.anotherWord,n=this.getLongestMatch(e);let r=this.getMatchFeedback(n,1===e.length);return null!=r?r.suggestions.unshift(s):r={warning:null,suggestions:[s]},r}getLongestMatch(t){let e=t[0];return t.slice(1).forEach((t=>{t.token.length>e.token.length&&(e=t)})),e}getMatchFeedback(t,e){return this.matchers[t.pattern]?this.matchers[t.pattern](t,e):A.matchers[t.pattern]&&"feedback"in A.matchers[t.pattern]?A.matchers[t.pattern].feedback(t,e):ct}}const ht=()=>(new Date).getTime(),ut=(t,e,s)=>{const n=new lt,r=new J,a=$.mostGuessableMatchSequence(e,t),i=ht()-s,o=r.estimateAttackTimes(a.guesses);return{calcTime:i,...a,...o,feedback:n.getFeedback(o.score,a.sequence)}},dt=(t,e)=>{e&&A.extendUserInputsDictionary(e);return(new _).match(t)};return t.Options=I,t.debounce=(t,e,s)=>{let n;return function(...r){const a=this,i=s&&!n;if(void 0!==n&&clearTimeout(n),n=setTimeout((()=>{n=void 0,s||t.apply(a,r)}),e),i)return t.apply(a,r)}},t.zxcvbn=(t,e)=>{const s=ht(),n=dt(t,e);if(n instanceof Promise)throw new Error("You are using a Promised matcher, please use `zxcvbnAsync` for it.");return ut(n,t,s)},t.zxcvbnAsync=async(t,e)=>{const s=t.substring(0,A.maxLength),n=ht(),r=await dt(s,e);return ut(r,s,n)},t.zxcvbnOptions=A,t}({});
this.zxcvbnts=this.zxcvbnts||{},this.zxcvbnts.core=function(t){"use strict";const e=(t,e)=>t.push.apply(t,e),s=t=>t.sort(((t,e)=>t.i-e.i||t.j-e.j)),n=t=>{const e={};let s=1;return t.forEach((t=>{e[t]=s,s+=1})),e};const r={4:[[1,2],[2,3]],5:[[1,3],[2,3],[2,4]],6:[[1,2],[2,4],[4,5]],7:[[1,3],[2,3],[4,5],[4,6]],8:[[2,4],[4,6]]},i=/^[A-Z\xbf-\xdf][^A-Z\xbf-\xdf]+$/,o=/^[^A-Z\xbf-\xdf]+[A-Z\xbf-\xdf]$/,a=/^[A-Z\xbf-\xdf]+$/,c=/^[^a-z\xdf-\xff]+$/,l=/^[a-z\xdf-\xff]+$/,h=/^[^A-Z\xbf-\xdf]+$/,u=/[a-z\xdf-\xff]/,d=/[A-Z\xbf-\xdf]/,g=/[^A-Za-z\xbf-\xdf]/gi,p=/^\d+$/,f=(new Date).getFullYear(),m={recentYear:/19\d\d|200\d|201\d|202\d/g},b=[" ",",",";",":","|","/","\\","_",".","-"],k=b.length;class y{constructor(t){this.options=t}match({password:t}){const e=[...this.getMatchesWithoutSeparator(t),...this.getMatchesWithSeparator(t)],n=this.filterNoise(e);return s(n)}getMatchesWithSeparator(t){const e=[],s=/^(\d{1,4})([\s/\\_.-])(\d{1,2})\2(\d{1,4})$/;for(let n=0;n<=Math.abs(t.length-6);n+=1)for(let r=n+5;r<=n+9&&!(r>=t.length);r+=1){const i=t.slice(n,+r+1||9e9),o=s.exec(i);if(null!=o){const t=this.mapIntegersToDayMonthYear([parseInt(o[1],10),parseInt(o[3],10),parseInt(o[4],10)]);null!=t&&e.push({pattern:"date",token:i,i:n,j:r,separator:o[2],year:t.year,month:t.month,day:t.day})}}return e}getMatchesWithoutSeparator(t){const e=[],s=/^\d{4,8}$/,n=t=>Math.abs(t.year-f);for(let i=0;i<=Math.abs(t.length-4);i+=1)for(let o=i+3;o<=i+7&&!(o>=t.length);o+=1){const a=t.slice(i,+o+1||9e9);if(s.exec(a)){const t=[],s=a.length;if(r[s].forEach((([e,s])=>{const n=this.mapIntegersToDayMonthYear([parseInt(a.slice(0,e),10),parseInt(a.slice(e,s),10),parseInt(a.slice(s),10)]);null!=n&&t.push(n)})),t.length>0){let s=t[0],r=n(t[0]);t.slice(1).forEach((t=>{const e=n(t);e<r&&(s=t,r=e)})),e.push({pattern:"date",token:a,i:i,j:o,separator:"",year:s.year,month:s.month,day:s.day})}}}return e}filterNoise(t){return t.filter((e=>{let s=!1;const n=t.length;for(let r=0;r<n;r+=1){const n=t[r];if(e!==n&&n.i<=e.i&&n.j>=e.j){s=!0;break}}return!s}))}mapIntegersToDayMonthYear(t){if(t[1]>31||t[1]<=0)return null;let e=0,s=0,n=0;for(let r=0,i=t.length;r<i;r+=1){const i=t[r];if(i>99&&i<1e3||i>2050)return null;i>31&&(s+=1),i>12&&(e+=1),i<=0&&(n+=1)}return s>=2||3===e||n>=2?null:this.getDayMonth(t)}getDayMonth(t){const e=[[t[2],t.slice(0,2)],[t[0],t.slice(1,3)]],s=e.length;for(let t=0;t<s;t+=1){const[s,n]=e[t];if(1e3<=s&&s<=2050){const t=this.mapIntegersToDayMonth(n);return null!=t?{year:s,month:t.month,day:t.day}:null}}for(let t=0;t<s;t+=1){const[s,n]=e[t],r=this.mapIntegersToDayMonth(n);if(null!=r)return{year:this.twoToFourDigitYear(s),month:r.month,day:r.day}}return null}mapIntegersToDayMonth(t){const e=[t,t.slice().reverse()];for(let t=0;t<e.length;t+=1){const s=e[t],n=s[0],r=s[1];if(n>=1&&n<=31&&r>=1&&r<=12)return{day:n,month:r}}return null}twoToFourDigitYear(t){return t>99?t:t>50?t+1900:t+2e3}}const w=new Uint32Array(65536),x=(t,e)=>{if(t.length<e.length){const s=e;e=t,t=s}return 0===e.length?t.length:t.length<=32?((t,e)=>{const s=t.length,n=e.length,r=1<<s-1;let i=-1,o=0,a=s,c=s;for(;c--;)w[t.charCodeAt(c)]|=1<<c;for(c=0;c<n;c++){let t=w[e.charCodeAt(c)];const s=t|o;t|=(t&i)+i^i,o|=~(t|i),i&=t,o&r&&a++,i&r&&a--,o=o<<1|1,i=i<<1|~(s|o),o&=s}for(c=s;c--;)w[t.charCodeAt(c)]=0;return a})(t,e):((t,e)=>{const s=e.length,n=t.length,r=[],i=[],o=Math.ceil(s/32),a=Math.ceil(n/32);for(let t=0;t<o;t++)i[t]=-1,r[t]=0;let c=0;for(;c<a-1;c++){let o=0,a=-1;const l=32*c,h=Math.min(32,n)+l;for(let e=l;e<h;e++)w[t.charCodeAt(e)]|=1<<e;for(let t=0;t<s;t++){const s=w[e.charCodeAt(t)],n=i[t/32|0]>>>t&1,c=r[t/32|0]>>>t&1,l=s|o,h=((s|c)&a)+a^a|s|c;let u=o|~(h|a),d=a&h;u>>>31^n&&(i[t/32|0]^=1<<t),d>>>31^c&&(r[t/32|0]^=1<<t),u=u<<1|n,d=d<<1|c,a=d|~(l|u),o=u&l}for(let e=l;e<h;e++)w[t.charCodeAt(e)]=0}let l=0,h=-1;const u=32*c,d=Math.min(32,n-u)+u;for(let e=u;e<d;e++)w[t.charCodeAt(e)]|=1<<e;let g=n;for(let t=0;t<s;t++){const s=w[e.charCodeAt(t)],o=i[t/32|0]>>>t&1,a=r[t/32|0]>>>t&1,c=s|l,u=((s|a)&h)+h^h|s|a;let d=l|~(u|h),p=h&u;g+=d>>>n-1&1,g-=p>>>n-1&1,d>>>31^o&&(i[t/32|0]^=1<<t),p>>>31^a&&(r[t/32|0]^=1<<t),d=d<<1|o,p=p<<1|a,h=p|~(c|d),l=d&c}for(let e=u;e<d;e++)w[t.charCodeAt(e)]=0;return g})(t,e)},M=(t,e,s)=>{let n=0;const r=Object.keys(e).find((e=>{const r=((t,e,s)=>{const n=t.length<=e.length,r=t.length<=s;return n||r?Math.ceil(t.length/4):s})(t,e,s);if(Math.abs(t.length-e.length)>r)return!1;const i=x(t,e),o=i<=r;return o&&(n=i),o}));return r?{levenshteinDistance:n,levenshteinDistanceEntry:r}:{}};class S{constructor(t,e){this.options=t,this.defaultMatch=e}match(t){const e=t.password.split("").reverse().join("");return this.defaultMatch({...t,password:e}).map((e=>({...e,token:e.token.split("").reverse().join(""),reversed:!0,i:t.password.length-1-e.j,j:t.password.length-1-e.i})))}}class v{constructor({substr:t,limit:e,trieRoot:s}){this.buffer=[],this.finalPasswords=[],this.substr=t,this.limit=e,this.trieRoot=s}getAllPossibleSubsAtIndex(t){const e=[];let s=this.trieRoot;for(let n=t;n<this.substr.length;n+=1){const t=this.substr.charAt(n);if(s=s.getChild(t),!s)break;e.push(s)}return e}helper({onlyFullSub:t,isFullSub:e,index:s,subIndex:n,changes:r,lastSubLetter:i,consecutiveSubCount:o}){if(this.finalPasswords.length>=this.limit)return;if(s===this.substr.length)return void(t===e&&this.finalPasswords.push({password:this.buffer.join(""),changes:r,isFullSubstitution:t}));const a=[...this.getAllPossibleSubsAtIndex(s)];let c=!1;for(let l=s+a.length-1;l>=s;l-=1){const h=a[l-s],u=h.parents.join("");if(h.isTerminal()){if(i===u&&o>=3)continue;c=!0;const a=h.subs;for(const c of a){this.buffer.push(c);const a=r.concat({i:n,letter:c,substitution:u});if(this.helper({onlyFullSub:t,isFullSub:e,index:s+u.length,subIndex:n+c.length,changes:a,lastSubLetter:u,consecutiveSubCount:i===u?o+1:1}),this.buffer.pop(),this.finalPasswords.length>=this.limit)return}}}if(!t||!c){const a=this.substr.charAt(s);this.buffer.push(a),this.helper({onlyFullSub:t,isFullSub:e&&!c,index:s+1,subIndex:n+1,changes:r,lastSubLetter:i,consecutiveSubCount:o}),this.buffer.pop()}}getAll(){return this.helper({onlyFullSub:!0,isFullSub:!0,index:0,subIndex:0,changes:[],lastSubLetter:void 0,consecutiveSubCount:0}),this.helper({onlyFullSub:!1,isFullSub:!0,index:0,subIndex:0,changes:[],lastSubLetter:void 0,consecutiveSubCount:0}),this.finalPasswords}}class T{constructor(t,e){this.options=t,this.defaultMatch=e}isAlreadyIncluded(t,e){return t.some((t=>Object.entries(t).every((([t,s])=>"subs"===t||s===e[t]))))}match(t){const e=[],s=(n=t.password,r=this.options.l33tMaxSubstitutions,i=this.options.trieNodeRoot,new v({substr:n,limit:r,trieRoot:i}).getAll());var n,r,i;let o=!1;return s.forEach((s=>{if(o)return;this.defaultMatch({...t,password:s.password,useLevenshtein:s.isFullSubstitution}).forEach((n=>{o||(o=0===n.i&&n.j===t.password.length-1);const r=((t,e,s)=>{const n=t.changes.filter((t=>t.i<e)).reduce(((t,e)=>t-e.letter.length+e.substitution.length),e),r=t.changes.filter((t=>t.i>=e&&t.i<=s)),i=r.reduce(((t,e)=>t-e.letter.length+e.substitution.length),s-e+n),o=[],a=[];return r.forEach((t=>{o.findIndex((e=>e.letter===t.letter&&e.substitution===t.substitution))<0&&(o.push({letter:t.letter,substitution:t.substitution}),a.push(`${t.substitution} -> ${t.letter}`))})),{i:n,j:i,subs:o,subDisplay:a.join(", ")}})(s,n.i,n.j),i=t.password.slice(r.i,+r.j+1||9e9),a={...n,l33t:!0,token:i,...r},c=this.isAlreadyIncluded(e,a);i.toLowerCase()===n.matchedWord||c||e.push(a)}))})),e.filter((t=>t.token.length>1))}}class j{constructor(t){this.options=t,this.l33t=new T(t,this.defaultMatch),this.reverse=new S(t,this.defaultMatch)}match(t){const e=[...this.defaultMatch(t),...this.reverse.match(t),...this.l33t.match(t)];return s(e)}defaultMatch({password:t,userInputsOptions:e,useLevenshtein:s=!0}){const n=[],r=t.length,i=t.toLowerCase(),{rankedDictionaries:o,rankedDictionariesMaxWordSize:a}=((t,e,s)=>{const n={...t},r={...e};return s?(n.userInputs={...n.userInputs||{},...s.rankedDictionary},r.userInputs=Math.max(s.rankedDictionaryMaxWordSize,r.userInputs||0),{rankedDictionaries:n,rankedDictionariesMaxWordSize:r}):{rankedDictionaries:n,rankedDictionariesMaxWordSize:r}})(this.options.rankedDictionaries,this.options.rankedDictionariesMaxWordSize,e);return Object.keys(o).forEach((e=>{const c=o[e],l=a[e],h=Math.min(l,r);for(let o=0;o<r;o+=1){const a=Math.min(o+h,r);for(let l=o;l<a;l+=1){const a=i.slice(o,+l+1||9e9),h=a in c;let u={};const d=0===o&&l===r-1;this.options.useLevenshteinDistance&&d&&!h&&s&&(u=M(a,c,this.options.levenshteinThreshold));const g=0!==Object.keys(u).length;if(h||g){const s=c[g?u.levenshteinDistanceEntry:a];n.push({pattern:"dictionary",i:o,j:l,token:t.slice(o,+l+1||9e9),matchedWord:a,rank:s,dictionaryName:e,reversed:!1,l33t:!1,...u})}}}})),n}}class A{constructor(t){this.options=t}match({password:t}){const e=[];return Object.keys(m).forEach((s=>{const n=m[s];let r;for(n.lastIndex=0;r=n.exec(t);)if(r){const t=r[0];e.push({pattern:"regex",token:t,i:r.index,j:r.index+r[0].length-1,regexName:s,regexMatch:r})}})),s(e)}}var I={nCk(t,e){let s=t;if(e>s)return 0;if(0===e)return 1;let n=1;for(let t=1;t<=e;t+=1)n*=s,n/=t,s-=1;return n},log10:t=>0===t?0:Math.log(t)/Math.log(10),log2:t=>Math.log(t)/Math.log(2),factorial(t){let e=1;for(let s=2;s<=t;s+=1)e*=s;return e}};var C=t=>{const e=t.replace(g,"");if(e.match(h)||e.toLowerCase()===e)return 1;const s=[i,o,c],n=s.length;for(let t=0;t<n;t+=1){const n=s[t];if(e.match(n))return 2}return(t=>{const e=t.split(""),s=e.filter((t=>t.match(d))).length,n=e.filter((t=>t.match(u))).length;let r=0;const i=Math.min(s,n);for(let t=1;t<=i;t+=1)r+=I.nCk(s+n,t);return r})(e)};const E=(t,e)=>{let s=0,n=t.indexOf(e);for(;n>=0;)s+=1,n=t.indexOf(e,n+e.length);return s};var D=({l33t:t,subs:e,token:s})=>{if(!t)return 1;let n=1;return e.forEach((t=>{const{subbedCount:e,unsubbedCount:r}=(({sub:t,token:e})=>{const s=e.toLowerCase();return{subbedCount:E(s,t.substitution),unsubbedCount:E(s,t.letter)}})({sub:t,token:s});if(0===e||0===r)n*=2;else{const t=Math.min(r,e);let s=0;for(let n=1;n<=t;n+=1)s+=I.nCk(r+e,n);n*=s}})),n};const L=(t,{token:e,turns:s})=>{const n=Object.keys(t).length,r=(t=>{let e=0;return Object.keys(t).forEach((s=>{const n=t[s];e+=n.filter((t=>!!t)).length})),e/=Object.entries(t).length,e})(t);let i=0;const o=e.length;for(let t=2;t<=o;t+=1){const e=Math.min(s,t-1);for(let s=1;s<=e;s+=1)i+=I.nCk(t-1,s-1)*n*r**s}return i};const P={bruteforce:({token:t})=>{let e,s=10**t.length;return s===Number.POSITIVE_INFINITY&&(s=Number.MAX_VALUE),e=1===t.length?11:51,Math.max(s,e)},date:({year:t,separator:e})=>{let s=365*Math.max(Math.abs(t-f),20);return e&&(s*=4),s},dictionary:({rank:t,reversed:e,l33t:s,subs:n,token:r,dictionaryName:i})=>{const o=t,a=C(r),c=D({l33t:s,subs:n,token:r});let l;return l="diceware"===i?3888:o*a*c*(e?2:1),{baseGuesses:o,uppercaseVariations:a,l33tVariations:c,calculation:l}},regex:({regexName:t,regexMatch:e,token:s})=>{const n={alphaLower:26,alphaUpper:26,alpha:52,alphanumeric:62,digits:10,symbols:33};return t in n?n[t]**s.length:"recentYear"===t?Math.max(Math.abs(parseInt(e[0],10)-f),20):0},repeat:({baseGuesses:t,repeatCount:e})=>t*e,sequence:({token:t,ascending:e})=>{const s=t.charAt(0);let n=0;return n=["a","A","z","Z","0","1","9"].includes(s)?4:s.match(/\d/)?10:26,e||(n*=2),n*t.length},spatial:({graph:t,token:e,shiftedCount:s,turns:n},r)=>{let i=L(r.graphs[t],{token:e,turns:n});if(s){const t=e.length-s;if(0===s||0===t)i*=2;else{let e=0;for(let n=1;n<=Math.min(s,t);n+=1)e+=I.nCk(s+t,n);i*=e}}return Math.round(i)},separator:()=>k};var R=(t,e,s)=>{const n={};if("guesses"in e&&null!=e.guesses)return e;const r=((t,e)=>{let s=1;return t.token.length<e.length&&(s=1===t.token.length?10:50),s})(e,s),i=((t,e,s)=>P[e]?P[e](s,t):t.matchers[e]&&"scoring"in t.matchers[e]?t.matchers[e].scoring(s,t):0)(t,e.pattern,e);let o=0;"number"==typeof i?o=i:"dictionary"===e.pattern&&(o=i.calculation,n.baseGuesses=i.baseGuesses,n.uppercaseVariations=i.uppercaseVariations,n.l33tVariations=i.l33tVariations);const a=Math.max(o,r);return{...e,...n,guesses:a,guessesLog10:I.log10(a)}};const O={password:"",optimal:{},excludeAdditive:!1,separatorRegex:void 0,fillArray(t,e){const s=[];for(let n=0;n<t;n+=1){let t=[];"object"===e&&(t={}),s.push(t)}return s},makeBruteforceMatch(t,e){return{pattern:"bruteforce",token:this.password.slice(t,+e+1||9e9),i:t,j:e}},update(t,e,s){const n=e.j,r=R(t,e,this.password);let i=r.guesses;s>1&&(i*=this.optimal.pi[r.i-1][s-1]);let o=I.factorial(s)*i;this.excludeAdditive||(o+=1e4**(s-1));let a=!1;Object.keys(this.optimal.g[n]).forEach((t=>{const e=this.optimal.g[n][t];parseInt(t,10)<=s&&e<=o&&(a=!0)})),a||(this.optimal.g[n][s]=o,this.optimal.m[n][s]=r,this.optimal.pi[n][s]=i)},bruteforceUpdate(t,e){let s=this.makeBruteforceMatch(0,e);this.update(t,s,1);for(let n=1;n<=e;n+=1){s=this.makeBruteforceMatch(n,e);const r=this.optimal.m[n-1];Object.keys(r).forEach((e=>{"bruteforce"!==r[e].pattern&&this.update(t,s,parseInt(e,10)+1)}))}},unwind(t){const e=[];let s=t-1,n=0,r=Infinity;const i=this.optimal.g[s];for(i&&Object.keys(i).forEach((t=>{const e=i[t];e<r&&(n=parseInt(t,10),r=e)}));s>=0;){const t=this.optimal.m[s][n];e.unshift(t),s=t.i-1,n-=1}return e}};class F{constructor(t){this.options=t}mostGuessableMatchSequence(t,e,s=!1){O.password=t,O.excludeAdditive=s;const n=t.length;let r=O.fillArray(n,"array");e.forEach((t=>{r[t.j].push(t)})),r=r.map((t=>t.sort(((t,e)=>t.i-e.i)))),O.optimal={m:O.fillArray(n,"object"),pi:O.fillArray(n,"object"),g:O.fillArray(n,"object")};for(let t=0;t<n;t+=1)r[t].forEach((t=>{t.i>0?Object.keys(O.optimal.m[t.i-1]).forEach((e=>{O.update(this.options,t,parseInt(e,10)+1)})):O.update(this.options,t,1)})),O.bruteforceUpdate(this.options,t);const i=O.unwind(n),o=i.length,a=this.getGuesses(t,o);return{password:t,guesses:a,guessesLog10:I.log10(a),sequence:i}}getGuesses(t,e){const s=t.length;let n=0;return n=0===t.length?1:O.optimal.g[s-1][e],n}}class z{constructor(t){this.options=t,this.scoring=new F(t)}match({password:t,omniMatch:e}){const s=[];let n=0;for(;n<t.length;){const r=this.getGreedyMatch(t,n),i=this.getLazyMatch(t,n);if(null==r)break;const{match:o,baseToken:a}=this.setMatchToken(r,i);if(o){const t=o.index+o[0].length-1,r=this.getBaseGuesses(a,e);s.push(this.normalizeMatch(a,t,o,r)),n=t+1}}return s.some((t=>t instanceof Promise))?Promise.all(s):s}normalizeMatch(t,e,s,n){const r={pattern:"repeat",i:s.index,j:e,token:s[0],baseToken:t,baseGuesses:0,repeatCount:s[0].length/t.length};return n instanceof Promise?n.then((t=>({...r,baseGuesses:t}))):{...r,baseGuesses:n}}getGreedyMatch(t,e){const s=/(.+)\1+/g;return s.lastIndex=e,s.exec(t)}getLazyMatch(t,e){const s=/(.+?)\1+/g;return s.lastIndex=e,s.exec(t)}setMatchToken(t,e){const s=/^(.+?)\1+$/;let n,r="";if(e&&t[0].length>e[0].length){n=t;const e=s.exec(n[0]);e&&(r=e[1])}else n=e,n&&(r=n[1]);return{match:n,baseToken:r}}getBaseGuesses(t,e){const s=e.match(t);if(s instanceof Promise)return s.then((e=>this.scoring.mostGuessableMatchSequence(t,e).guesses));return this.scoring.mostGuessableMatchSequence(t,s).guesses}}class N{constructor(t){this.options=t,this.MAX_DELTA=5}match({password:t}){const e=[];if(1===t.length)return[];let s=0,n=null;const r=t.length;for(let i=1;i<r;i+=1){const r=t.charCodeAt(i)-t.charCodeAt(i-1);if(null==n&&(n=r),r!==n){const o=i-1;this.update({i:s,j:o,delta:n,password:t,result:e}),s=o,n=r}}return this.update({i:s,j:r-1,delta:n,password:t,result:e}),e}update({i:t,j:e,delta:s,password:n,result:r}){if(e-t>1||1===Math.abs(s)){const i=Math.abs(s);if(i>0&&i<=this.MAX_DELTA){const i=n.slice(t,+e+1||9e9),{sequenceName:o,sequenceSpace:a}=this.getSequence(i);return r.push({pattern:"sequence",i:t,j:e,token:n.slice(t,+e+1||9e9),sequenceName:o,sequenceSpace:a,ascending:s>0})}}return null}getSequence(t){let e="unicode",s=26;return l.test(t)?(e="lower",s=26):a.test(t)?(e="upper",s=26):p.test(t)&&(e="digits",s=10),{sequenceName:e,sequenceSpace:s}}}class W{constructor(t){this.options=t,this.SHIFTED_RX=/[~!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL:"ZXCVBNM<>?]/}match({password:t}){const n=[];return Object.keys(this.options.graphs).forEach((s=>{const r=this.options.graphs[s];e(n,this.helper(t,r,s))})),s(n)}checkIfShifted(t,e,s){return!t.includes("keypad")&&this.SHIFTED_RX.test(e.charAt(s))?1:0}helper(t,e,s){let n;const r=[];let i=0;const o=t.length;for(;i<o-1;){let a=i+1,c=null,l=0;for(n=this.checkIfShifted(s,t,i);;){const h=e[t.charAt(a-1)]||[];let u=!1,d=-1,g=-1;if(a<o){const e=t.charAt(a),s=h.length;for(let t=0;t<s;t+=1){const s=h[t];if(g+=1,s){const t=s.indexOf(e);if(-1!==t){u=!0,d=g,1===t&&(n+=1),c!==d&&(l+=1,c=d);break}}}}if(!u){a-i>2&&r.push({pattern:"spatial",i:i,j:a-1,token:t.slice(i,a),graph:s,turns:l,shiftedCount:n}),i=a;break}a+=1}}return r}}const q=new RegExp(`[${b.join("")}]`);class Y{constructor(t){this.options=t}static getMostUsedSeparatorChar(t){const e=[...t.split("").filter((t=>q.test(t))).reduce(((t,e)=>{const s=t.get(e);return s?t.set(e,s+1):t.set(e,1),t}),new Map).entries()].sort((([t,e],[s,n])=>n-e));if(!e.length)return;const s=e[0];return s[1]<2?void 0:s[0]}static getSeparatorRegex(t){return new RegExp(`([^${t}\n])(${t})(?!${t})`,"g")}match({password:t}){const e=[];if(0===t.length)return e;const s=Y.getMostUsedSeparatorChar(t);if(void 0===s)return e;const n=Y.getSeparatorRegex(s);for(const r of t.matchAll(n)){if(void 0===r.index)continue;const t=r.index+1;e.push({pattern:"separator",token:s,i:t,j:t})}return e}}class V{constructor(t){this.options=t,this.matchers={date:y,dictionary:j,regex:A,repeat:z,sequence:N,spatial:W,separator:Y}}matcherFactory(t){if(!this.matchers[t]&&!this.options.matchers[t])return null;return new(this.matchers[t]?this.matchers[t]:this.options.matchers[t].Matching)(this.options)}processResult(t,s,n){n instanceof Promise?(n.then((s=>{e(t,s)})),s.push(n)):e(t,n)}handlePromises(t,e){return e.length>0?new Promise(((n,r)=>{Promise.all(e).then((()=>{n(s(t))})).catch((t=>{r(t)}))})):s(t)}match(t,e){const s=[],n=[];return[...Object.keys(this.matchers),...Object.keys(this.options.matchers)].forEach((r=>{const i=this.matcherFactory(r);if(!i)return;const o=i.match({password:t,omniMatch:this,userInputsOptions:e});this.processResult(s,n,o)})),this.handlePromises(s,n)}}const $=2678400,G=32140800,X={second:1,minute:60,hour:3600,day:86400,month:$,year:G,century:100*G},H={scoring:{0:1e3,1:1e6,2:1e8,3:1e10},attackTime:{onlineThrottlingXPerHour:100,onlineNoThrottlingXPerSecond:10,offlineSlowHashingXPerSecond:1e4,offlineFastHashingXPerSecond:1e10}};class U{constructor(t){this.options=t}estimateAttackTimes(t){const e=this.calculateCrackTimesSeconds(t);return{crackTimes:Object.keys(e).reduce(((t,s)=>{const n=s,r=e[n],{base:i,displayStr:o}=this.displayTime(r);return t[n]={base:i,seconds:r,display:this.translate(o,i)},t}),{}),score:this.guessesToScore(t)}}calculateCrackTimesSeconds(t){const e=this.options.timeEstimationValues.attackTime;return{onlineThrottlingXPerHour:t/(e.onlineThrottlingXPerHour/3600),onlineNoThrottlingXPerSecond:t/e.onlineNoThrottlingXPerSecond,offlineSlowHashingXPerSecond:t/e.offlineSlowHashingXPerSecond,offlineFastHashingXPerSecond:t/e.offlineFastHashingXPerSecond}}guessesToScore(t){const e=this.options.timeEstimationValues.scoring;return t<e[0]+5?0:t<e[1]+5?1:t<e[2]+5?2:t<e[3]+5?3:4}displayTime(t){let e="centuries",s=null;const n=Object.keys(X),r=n.findIndex((e=>t<X[e]));return r>-1&&(e=n[r-1],0!==r?s=Math.round(t/X[e]):e="ltSecond"),{base:s,displayStr:e}}translate(t,e){let s=t;null!==e&&1!==e&&(s+="s");const{timeEstimation:n}=this.options.translations;return n[s].replace("{base}",`${e}`)}}var B=()=>null,Z=t=>({warning:t.translations.warnings.dates,suggestions:[t.translations.suggestions.dates]});var _=(t,e,s)=>{const n=((t,e,s)=>{const n=e.dictionaryName,r=n.toLowerCase().includes("lastnames")||n.toLowerCase().includes("firstnames")||n.toLowerCase().includes("names");return n.includes("passwords")?((t,e,s)=>{let n=null;return!s||e.l33t||e.reversed?e.guessesLog10<=4&&(n=t.translations.warnings.similarToCommon):n=e.rank<=10?t.translations.warnings.topTen:e.rank<=100?t.translations.warnings.topHundred:t.translations.warnings.common,n})(t,e,s):n.includes("wikipedia")?((t,e,s)=>{let n=null;return s&&(n=t.translations.warnings.wordByItself),n})(t,0,s):r?((t,e,s)=>s?t.translations.warnings.namesByThemselves:t.translations.warnings.commonNames)(t,0,s):n.includes("userInputs")?t.translations.warnings.userInputs:null})(t,e,s),r=[],o=e.token;return o.match(i)?r.push(t.translations.suggestions.capitalization):o.match(c)&&o.toLowerCase()!==o&&r.push(t.translations.suggestions.allUppercase),e.reversed&&e.token.length>=4&&r.push(t.translations.suggestions.reverseWords),e.l33t&&r.push(t.translations.suggestions.l33t),{warning:n,suggestions:r}},K=(t,e)=>"recentYear"===e.regexName?{warning:t.translations.warnings.recentYears,suggestions:[t.translations.suggestions.recentYears,t.translations.suggestions.associatedYears]}:{warning:null,suggestions:[]},J=(t,e)=>{let s=t.translations.warnings.extendedRepeat;return 1===e.baseToken.length&&(s=t.translations.warnings.simpleRepeat),{warning:s,suggestions:[t.translations.suggestions.repeated]}},Q=t=>({warning:t.translations.warnings.sequences,suggestions:[t.translations.suggestions.sequences]}),tt=(t,e)=>{let s=t.translations.warnings.keyPattern;return 1===e.turns&&(s=t.translations.warnings.straightRow),{warning:s,suggestions:[t.translations.suggestions.longerKeyboardPattern]}},et=()=>null;const st={warning:null,suggestions:[]};class nt{constructor(t){this.options=t,this.matchers={bruteforce:B,date:Z,dictionary:_,regex:K,repeat:J,sequence:Q,spatial:tt,separator:et},this.defaultFeedback={warning:null,suggestions:[]},this.setDefaultSuggestions()}setDefaultSuggestions(){this.defaultFeedback.suggestions.push(this.options.translations.suggestions.useWords,this.options.translations.suggestions.noNeed)}getFeedback(t,e){if(0===e.length)return this.defaultFeedback;if(t>2)return st;const s=this.options.translations.suggestions.anotherWord,n=this.getLongestMatch(e);let r=this.getMatchFeedback(n,1===e.length);return null!=r?r.suggestions.unshift(s):r={warning:null,suggestions:[s]},r}getLongestMatch(t){let e=t[0];return t.slice(1).forEach((t=>{t.token.length>e.token.length&&(e=t)})),e}getMatchFeedback(t,e){return this.matchers[t.pattern]?this.matchers[t.pattern](this.options,t,e):this.options.matchers[t.pattern]&&"feedback"in this.options.matchers[t.pattern]?this.options.matchers[t.pattern].feedback(this.options,t,e):st}}var rt={a:["4","@"],b:["8"],c:["(","{","[","<"],d:["6","|)"],e:["3"],f:["#"],g:["6","9","&"],h:["#","|-|"],i:["1","!","|"],k:["<","|<"],l:["!","1","|","7"],m:["^^","nn","2n","/\\\\/\\\\"],n:["//"],o:["0","()"],q:["9"],u:["|_|"],s:["$","5"],t:["+","7"],v:["<",">","/"],w:["^/","uu","vv","2u","2v","\\\\/\\\\/"],x:["%","><"],z:["2"]},it={warnings:{straightRow:"straightRow",keyPattern:"keyPattern",simpleRepeat:"simpleRepeat",extendedRepeat:"extendedRepeat",sequences:"sequences",recentYears:"recentYears",dates:"dates",topTen:"topTen",topHundred:"topHundred",common:"common",similarToCommon:"similarToCommon",wordByItself:"wordByItself",namesByThemselves:"namesByThemselves",commonNames:"commonNames",userInputs:"userInputs",pwned:"pwned"},suggestions:{l33t:"l33t",reverseWords:"reverseWords",allUppercase:"allUppercase",capitalization:"capitalization",dates:"dates",recentYears:"recentYears",associatedYears:"associatedYears",sequences:"sequences",repeated:"repeated",longerKeyboardPattern:"longerKeyboardPattern",anotherWord:"anotherWord",useWords:"useWords",noNeed:"noNeed",pwned:"pwned"},timeEstimation:{ltSecond:"ltSecond",second:"second",seconds:"seconds",minute:"minute",minutes:"minutes",hour:"hour",hours:"hours",day:"day",days:"days",month:"month",months:"months",year:"year",years:"years",centuries:"centuries"}};class ot{constructor(t=[]){this.parents=t,this.children=new Map}addSub(t,...e){const s=t.charAt(0);this.children.has(s)||this.children.set(s,new ot([...this.parents,s]));let n=this.children.get(s);for(let e=1;e<t.length;e+=1){const s=t.charAt(e);n.hasChild(s)||n.addChild(s),n=n.getChild(s)}return n.subs=(n.subs||[]).concat(e),this}getChild(t){return this.children.get(t)}isTerminal(){return!!this.subs}addChild(t){this.hasChild(t)||this.children.set(t,new ot([...this.parents,t]))}hasChild(t){return this.children.has(t)}}var at=(t,e)=>(Object.entries(t).forEach((([t,s])=>{s.forEach((s=>{e.addSub(s,t)}))})),e);class ct{constructor(t={},e={}){this.matchers={},this.l33tTable=rt,this.trieNodeRoot=at(rt,new ot),this.dictionary={userInputs:[]},this.rankedDictionaries={},this.rankedDictionariesMaxWordSize={},this.translations=it,this.graphs={},this.useLevenshteinDistance=!1,this.levenshteinThreshold=2,this.l33tMaxSubstitutions=100,this.maxLength=256,this.timeEstimationValues={scoring:{...H.scoring},attackTime:{...H.attackTime}},this.setOptions(t),Object.entries(e).forEach((([t,e])=>{this.addMatcher(t,e)}))}setOptions(t={}){var e;t.l33tTable&&(this.l33tTable=t.l33tTable,this.trieNodeRoot=at(t.l33tTable,new ot)),t.dictionary&&(this.dictionary=t.dictionary,this.setRankedDictionaries()),t.translations&&this.setTranslations(t.translations),t.graphs&&(this.graphs=t.graphs),void 0!==t.useLevenshteinDistance&&(this.useLevenshteinDistance=t.useLevenshteinDistance),void 0!==t.levenshteinThreshold&&(this.levenshteinThreshold=t.levenshteinThreshold),void 0!==t.l33tMaxSubstitutions&&(this.l33tMaxSubstitutions=t.l33tMaxSubstitutions),void 0!==t.maxLength&&(this.maxLength=t.maxLength),void 0!==t.timeEstimationValues&&(e=t.timeEstimationValues,Object.entries(e).forEach((([t,e])=>{Object.entries(e).forEach((([e,s])=>{if(s<H[t][e])throw new Error("Time estimation values are not to be allowed to be less than default")}))})),this.timeEstimationValues={scoring:{...t.timeEstimationValues.scoring},attackTime:{...t.timeEstimationValues.attackTime}})}setTranslations(t){if(!this.checkCustomTranslations(t))throw new Error("Invalid translations object fallback to keys");this.translations=t}checkCustomTranslations(t){let e=!0;return Object.keys(it).forEach((s=>{if(s in t){const n=s;Object.keys(it[n]).forEach((s=>{s in t[n]||(e=!1)}))}else e=!1})),e}setRankedDictionaries(){const t={},e={};Object.keys(this.dictionary).forEach((s=>{t[s]=n(this.dictionary[s]),e[s]=this.getRankedDictionariesMaxWordSize(this.dictionary[s])})),this.rankedDictionaries=t,this.rankedDictionariesMaxWordSize=e}getRankedDictionariesMaxWordSize(t){const e=t.map((t=>"string"!=typeof t?t.toString().length:t.length));return 0===e.length?0:e.reduce(((t,e)=>Math.max(t,e)),-1/0)}buildSanitizedRankedDictionary(t){const e=[];return t.forEach((t=>{const s=typeof t;"string"!==s&&"number"!==s&&"boolean"!==s||e.push(t.toString().toLowerCase())})),n(e)}getUserInputsOptions(t){let e={},s=0;return t&&(e=this.buildSanitizedRankedDictionary(t),s=this.getRankedDictionariesMaxWordSize(t)),{rankedDictionary:e,rankedDictionaryMaxWordSize:s}}addMatcher(t,e){this.matchers[t]?console.info(`Matcher ${t} already exists`):this.matchers[t]=e}}const lt=()=>(new Date).getTime();return t.ZxcvbnFactory=class{constructor(t={},e={}){this.options=new ct(t,e),this.scoring=new F(this.options)}estimateAttackTimes(t){return new U(this.options).estimateAttackTimes(t)}getFeedback(t,e){return new nt(this.options).getFeedback(t,e)}createReturnValue(t,e,s){const n=this.scoring.mostGuessableMatchSequence(e,t),r=lt()-s,i=this.estimateAttackTimes(n.guesses);return{calcTime:r,...n,...i,feedback:this.getFeedback(i.score,n.sequence)}}main(t,e){const s=this.options.getUserInputsOptions(e);return new V(this.options).match(t,s)}check(t,e){const s=t.substring(0,this.options.maxLength),n=lt(),r=this.main(s,e);if(r instanceof Promise)throw new Error("You are using a Promised matcher, please use `zxcvbnAsync` for it.");return this.createReturnValue(r,s,n)}async checkAsync(t,e){const s=t.substring(0,this.options.maxLength),n=lt(),r=await this.main(s,e);return this.createReturnValue(r,s,n)}},t.debounce=(t,e,s)=>{let n;return function(...r){const i=this,o=s&&!n;if(void 0!==n&&clearTimeout(n),n=setTimeout((()=>{n=void 0,s||t.apply(i,r)}),e),o)return t.apply(i,r)}},t}({});
//# sourceMappingURL=zxcvbn-ts.min.js.map
{
"name": "@zxcvbn-ts/core",
"version": "3.0.4",
"version": "4.0.0-beta.0",
"main": "dist/index.js",

@@ -40,3 +40,3 @@ "module": "dist/index.esm.js",

],
"gitHead": "ecb44e737022ff694a063d3e5e5caec94cc2d4fa"
"gitHead": "b7f8950e4a4b03716c55203815d7b58bff93b369"
}

@@ -21,7 +21,6 @@ # zxcvbn-ts

```js
import { zxcvbn, zxcvbnOptions } from '@zxcvbn-ts/core'
import { ZxcvbnFactory } from '@zxcvbn-ts/core'
import * as zxcvbnCommonPackage from '@zxcvbn-ts/language-common'
import * as zxcvbnEnPackage from '@zxcvbn-ts/language-en'
const password = 'somePassword'
const options = {

@@ -35,5 +34,6 @@ dictionary: {

}
zxcvbnOptions.setOptions(options)
const zxcvbn = new ZxcvbnFactory(options)
const password = 'somePassword'
zxcvbn(password)
```

@@ -1,2 +0,2 @@

import { zxcvbnOptions } from './Options'
import Options from './Options'
import { DefaultFeedbackFunction, FeedbackType, MatchEstimated } from './types'

@@ -26,3 +26,3 @@ import bruteforceMatcher from './matcher/bruteforce/feedback'

class Feedback {
readonly matchers: Matchers = {
private readonly matchers: Matchers = {
bruteforce: bruteforceMatcher,

@@ -38,3 +38,3 @@ date: dateMatcher,

defaultFeedback: FeedbackType = {
private defaultFeedback: FeedbackType = {
warning: null,

@@ -44,14 +44,14 @@ suggestions: [],

constructor() {
constructor(private options: Options) {
this.setDefaultSuggestions()
}
setDefaultSuggestions() {
private setDefaultSuggestions() {
this.defaultFeedback.suggestions.push(
zxcvbnOptions.translations.suggestions.useWords,
zxcvbnOptions.translations.suggestions.noNeed,
this.options.translations.suggestions.useWords,
this.options.translations.suggestions.noNeed,
)
}
getFeedback(score: number, sequence: MatchEstimated[]) {
public getFeedback(score: number, sequence: MatchEstimated[]) {
if (sequence.length === 0) {

@@ -63,3 +63,3 @@ return this.defaultFeedback

}
const extraFeedback = zxcvbnOptions.translations.suggestions.anotherWord
const extraFeedback = this.options.translations.suggestions.anotherWord
const longestMatch = this.getLongestMatch(sequence)

@@ -78,3 +78,3 @@ let feedback = this.getMatchFeedback(longestMatch, sequence.length === 1)

getLongestMatch(sequence: MatchEstimated[]) {
private getLongestMatch(sequence: MatchEstimated[]) {
let longestMatch = sequence[0]

@@ -90,11 +90,15 @@ const slicedSequence = sequence.slice(1)

getMatchFeedback(match: MatchEstimated, isSoleMatch: boolean) {
private getMatchFeedback(match: MatchEstimated, isSoleMatch: boolean) {
if (this.matchers[match.pattern]) {
return this.matchers[match.pattern](match, isSoleMatch)
return this.matchers[match.pattern](this.options, match, isSoleMatch)
}
if (
zxcvbnOptions.matchers[match.pattern] &&
'feedback' in zxcvbnOptions.matchers[match.pattern]
this.options.matchers[match.pattern] &&
'feedback' in this.options.matchers[match.pattern]
) {
return zxcvbnOptions.matchers[match.pattern].feedback(match, isSoleMatch)
return this.options.matchers[match.pattern].feedback(
this.options,
match,
isSoleMatch,
)
}

@@ -101,0 +105,0 @@ return defaultFeedback

import Matching from './Matching'
import scoring from './scoring'
import TimeEstimates from './TimeEstimates'
import { TimeEstimates } from './TimeEstimates'
import Feedback from './Feedback'
import { zxcvbnOptions, Options } from './Options'
import debounce from './debounce'
import { MatchExtended, ZxcvbnResult } from './types'
import Options from './Options'
import debounce from './utils/debounce'
import {
Matcher,
MatchEstimated,
MatchExtended,
OptionsType,
ZxcvbnResult,
} from './types'
import Scoring from './scoring'
const time = () => new Date().getTime()
const createReturnValue = (
resolvedMatches: MatchExtended[],
password: string,
start: number,
): ZxcvbnResult => {
const feedback = new Feedback()
const timeEstimates = new TimeEstimates()
const matchSequence = scoring.mostGuessableMatchSequence(
password,
resolvedMatches,
)
const calcTime = time() - start
const attackTimes = timeEstimates.estimateAttackTimes(matchSequence.guesses)
class ZxcvbnFactory {
private options: Options
return {
calcTime,
...matchSequence,
...attackTimes,
feedback: feedback.getFeedback(attackTimes.score, matchSequence.sequence),
private scoring: Scoring
constructor(
options: OptionsType = {},
customMatchers: Record<string, Matcher> = {},
) {
this.options = new Options(options, customMatchers)
this.scoring = new Scoring(this.options)
}
}
const main = (password: string, userInputs?: (string | number)[]) => {
if (userInputs) {
zxcvbnOptions.extendUserInputsDictionary(userInputs)
private estimateAttackTimes(guesses: number) {
const timeEstimates = new TimeEstimates(this.options)
return timeEstimates.estimateAttackTimes(guesses)
}
const matching = new Matching()
private getFeedback(score: number, sequence: MatchEstimated[]) {
const feedback = new Feedback(this.options)
return feedback.getFeedback(score, sequence)
}
return matching.match(password)
}
private createReturnValue(
resolvedMatches: MatchExtended[],
password: string,
start: number,
): ZxcvbnResult {
const matchSequence = this.scoring.mostGuessableMatchSequence(
password,
resolvedMatches,
)
const calcTime = time() - start
const attackTimes = this.estimateAttackTimes(matchSequence.guesses)
export const zxcvbn = (password: string, userInputs?: (string | number)[]) => {
const start = time()
const matches = main(password, userInputs)
return {
calcTime,
...matchSequence,
...attackTimes,
feedback: this.getFeedback(attackTimes.score, matchSequence.sequence),
}
}
if (matches instanceof Promise) {
throw new Error(
'You are using a Promised matcher, please use `zxcvbnAsync` for it.',
)
private main(password: string, userInputs?: (string | number)[]) {
const userInputsOptions = this.options.getUserInputsOptions(userInputs)
const matching = new Matching(this.options)
return matching.match(password, userInputsOptions)
}
return createReturnValue(matches, password, start)
}
export const zxcvbnAsync = async (
password: string,
userInputs?: (string | number)[],
): Promise<ZxcvbnResult> => {
const usedPassword = password.substring(0, zxcvbnOptions.maxLength)
const start = time()
const matches = await main(usedPassword, userInputs)
public check(password: string, userInputs?: (string | number)[]) {
const reducedPassword = password.substring(0, this.options.maxLength)
const start = time()
const matches = this.main(reducedPassword, userInputs)
return createReturnValue(matches, usedPassword, start)
if (matches instanceof Promise) {
throw new Error(
'You are using a Promised matcher, please use `zxcvbnAsync` for it.',
)
}
return this.createReturnValue(matches, reducedPassword, start)
}
public async checkAsync(password: string, userInputs?: (string | number)[]) {
const reducedPassword = password.substring(0, this.options.maxLength)
const start = time()
const matches = await this.main(reducedPassword, userInputs)
return this.createReturnValue(matches, reducedPassword, start)
}
}
export * from './types'
export { zxcvbnOptions, Options, debounce }
export { ZxcvbnFactory, debounce }

@@ -1,8 +0,8 @@

import { zxcvbnOptions } from '../../Options'
import Options from '../../Options'
export default () => {
export default (options: Options) => {
return {
warning: zxcvbnOptions.translations.warnings.dates,
suggestions: [zxcvbnOptions.translations.suggestions.dates],
warning: options.translations.warnings.dates,
suggestions: [options.translations.suggestions.dates],
}
}

@@ -7,8 +7,7 @@ import {

} from '../../data/const'
import { sorted } from '../../helper'
import { DateMatch } from '../../types'
import { sorted } from '../../utils/helper'
import { DateMatch, MatchOptions } from '../../types'
import Options from '../../Options'
interface DateMatchOptions {
password: string
}
type DateMatchOptions = Pick<MatchOptions, 'password'>

@@ -21,2 +20,4 @@ /*

class MatchDate {
constructor(private options: Options) {}
/*

@@ -23,0 +24,0 @@ * a "date" is recognized as:

@@ -1,2 +0,2 @@

import { zxcvbnOptions } from '../../Options'
import Options from '../../Options'
import { MatchEstimated } from '../../types'

@@ -6,2 +6,3 @@ import { ALL_UPPER_INVERTED, START_UPPER } from '../../data/const'

const getDictionaryWarningPassword = (
options: Options,
match: MatchEstimated,

@@ -13,10 +14,10 @@ isSoleMatch?: boolean,

if (match.rank <= 10) {
warning = zxcvbnOptions.translations.warnings.topTen
warning = options.translations.warnings.topTen
} else if (match.rank <= 100) {
warning = zxcvbnOptions.translations.warnings.topHundred
warning = options.translations.warnings.topHundred
} else {
warning = zxcvbnOptions.translations.warnings.common
warning = options.translations.warnings.common
}
} else if (match.guessesLog10 <= 4) {
warning = zxcvbnOptions.translations.warnings.similarToCommon
warning = options.translations.warnings.similarToCommon
}

@@ -27,2 +28,3 @@ return warning

const getDictionaryWarningWikipedia = (
options: Options,
match: MatchEstimated,

@@ -33,3 +35,3 @@ isSoleMatch?: boolean,

if (isSoleMatch) {
warning = zxcvbnOptions.translations.warnings.wordByItself
warning = options.translations.warnings.wordByItself
}

@@ -40,2 +42,3 @@ return warning

const getDictionaryWarningNames = (
options: Options,
match: MatchEstimated,

@@ -45,26 +48,40 @@ isSoleMatch?: boolean,

if (isSoleMatch) {
return zxcvbnOptions.translations.warnings.namesByThemselves
return options.translations.warnings.namesByThemselves
}
return zxcvbnOptions.translations.warnings.commonNames
return options.translations.warnings.commonNames
}
const getDictionaryWarning = (match: MatchEstimated, isSoleMatch?: boolean) => {
let warning: string | null = null
const getDictionaryWarning = (
options: Options,
match: MatchEstimated,
isSoleMatch?: boolean,
) => {
const dictName = match.dictionaryName
const isAName =
dictName === 'lastnames' || dictName.toLowerCase().includes('firstnames')
if (dictName === 'passwords') {
warning = getDictionaryWarningPassword(match, isSoleMatch)
} else if (dictName.includes('wikipedia')) {
warning = getDictionaryWarningWikipedia(match, isSoleMatch)
} else if (isAName) {
warning = getDictionaryWarningNames(match, isSoleMatch)
} else if (dictName === 'userInputs') {
warning = zxcvbnOptions.translations.warnings.userInputs
dictName.toLowerCase().includes('lastnames') ||
dictName.toLowerCase().includes('firstnames') ||
dictName.toLowerCase().includes('names')
if (dictName.includes('passwords')) {
return getDictionaryWarningPassword(options, match, isSoleMatch)
}
return warning
if (dictName.includes('wikipedia')) {
return getDictionaryWarningWikipedia(options, match, isSoleMatch)
}
if (isAName) {
return getDictionaryWarningNames(options, match, isSoleMatch)
}
if (dictName.includes('userInputs')) {
return options.translations.warnings.userInputs
}
return null
}
export default (match: MatchEstimated, isSoleMatch?: boolean) => {
const warning = getDictionaryWarning(match, isSoleMatch)
export default (
options: Options,
match: MatchEstimated,
isSoleMatch?: boolean,
) => {
const warning = getDictionaryWarning(options, match, isSoleMatch)
const suggestions: string[] = []

@@ -74,11 +91,11 @@ const word = match.token

if (word.match(START_UPPER)) {
suggestions.push(zxcvbnOptions.translations.suggestions.capitalization)
suggestions.push(options.translations.suggestions.capitalization)
} else if (word.match(ALL_UPPER_INVERTED) && word.toLowerCase() !== word) {
suggestions.push(zxcvbnOptions.translations.suggestions.allUppercase)
suggestions.push(options.translations.suggestions.allUppercase)
}
if (match.reversed && match.token.length >= 4) {
suggestions.push(zxcvbnOptions.translations.suggestions.reverseWords)
suggestions.push(options.translations.suggestions.reverseWords)
}
if (match.l33t) {
suggestions.push(zxcvbnOptions.translations.suggestions.l33t)
suggestions.push(options.translations.suggestions.l33t)
}

@@ -85,0 +102,0 @@ return {

import findLevenshteinDistance, {
FindLevenshteinDistanceResult,
} from '../../levenshtein'
import { sorted } from '../../helper'
import { zxcvbnOptions } from '../../Options'
} from '../../utils/levenshtein'
import { sorted } from '../../utils/helper'
import Options from '../../Options'
import { DictionaryNames, DictionaryMatch, L33tMatch } from '../../types'

@@ -10,2 +10,3 @@ import Reverse from './variants/matching/reverse'

import { DictionaryMatchOptions } from './types'
import mergeUserInputDictionary from '../../utils/mergeUserInputDictionary'

@@ -17,14 +18,12 @@ class MatchDictionary {

constructor() {
this.l33t = new L33t(this.defaultMatch)
this.reverse = new Reverse(this.defaultMatch)
constructor(private options: Options) {
this.l33t = new L33t(options, this.defaultMatch)
this.reverse = new Reverse(options, this.defaultMatch)
}
match({ password }: DictionaryMatchOptions) {
match(matchOptions: DictionaryMatchOptions) {
const matches = [
...(this.defaultMatch({
password,
}) as DictionaryMatch[]),
...(this.reverse.match({ password }) as DictionaryMatch[]),
...(this.l33t.match({ password }) as L33tMatch[]),
...(this.defaultMatch(matchOptions) as DictionaryMatch[]),
...(this.reverse.match(matchOptions) as DictionaryMatch[]),
...(this.l33t.match(matchOptions) as L33tMatch[]),
]

@@ -34,3 +33,7 @@ return sorted(matches)

defaultMatch({ password, useLevenshtein = true }: DictionaryMatchOptions) {
defaultMatch({
password,
userInputsOptions,
useLevenshtein = true,
}: DictionaryMatchOptions) {
const matches: DictionaryMatch[] = []

@@ -40,8 +43,13 @@ const passwordLength = password.length

const { rankedDictionaries, rankedDictionariesMaxWordSize } =
mergeUserInputDictionary(
this.options.rankedDictionaries,
this.options.rankedDictionariesMaxWordSize,
userInputsOptions,
)
// eslint-disable-next-line complexity,max-statements
Object.keys(zxcvbnOptions.rankedDictionaries).forEach((dictionaryName) => {
const rankedDict =
zxcvbnOptions.rankedDictionaries[dictionaryName as DictionaryNames]
Object.keys(rankedDictionaries).forEach((dictionaryName) => {
const rankedDict = rankedDictionaries[dictionaryName as DictionaryNames]
const longestDictionaryWordSize =
zxcvbnOptions.rankedDictionariesMaxWordSize[dictionaryName]
rankedDictionariesMaxWordSize[dictionaryName]
const searchWidth = Math.min(longestDictionaryWordSize, passwordLength)

@@ -59,3 +67,3 @@ for (let i = 0; i < passwordLength; i += 1) {

if (
zxcvbnOptions.useLevenshteinDistance &&
this.options.useLevenshteinDistance &&
isFullPassword &&

@@ -68,3 +76,3 @@ !isInDictionary &&

rankedDict,
zxcvbnOptions.levenshteinThreshold,
this.options.levenshteinThreshold,
)

@@ -71,0 +79,0 @@ }

@@ -1,10 +0,14 @@

import { DictionaryMatch } from '../../types'
import { DictionaryMatch, MatchOptions } from '../../types'
export interface DictionaryMatchOptions {
password: string
export interface DictionaryMatchOptionsLevenshtein extends MatchOptions {
useLevenshtein?: boolean
}
export type DictionaryMatchOptions = Pick<
DictionaryMatchOptionsLevenshtein,
'password' | 'userInputsOptions' | 'useLevenshtein'
>
export type DefaultMatch = (
options: DictionaryMatchOptions,
) => DictionaryMatch[]

@@ -1,4 +0,4 @@

import { zxcvbnOptions } from '../../../../Options'
import Options from '../../../../Options'
import { DictionaryMatch, L33tMatch } from '../../../../types'
import { DefaultMatch } from '../../types'
import { DefaultMatch, DictionaryMatchOptions } from '../../types'
import getCleanPasswords, {

@@ -57,8 +57,7 @@ PasswordChanges,

class MatchL33t {
defaultMatch: DefaultMatch
constructor(
private options: Options,
private defaultMatch: DefaultMatch,
) {}
constructor(defaultMatch: DefaultMatch) {
this.defaultMatch = defaultMatch
}
isAlreadyIncluded(matches: L33tMatch[], newMatch: L33tMatch) {

@@ -72,11 +71,10 @@ return matches.some((l33tMatch) => {

match({ password }: { password: string }) {
match(matchOptions: DictionaryMatchOptions) {
const matches: L33tMatch[] = []
const subbedPasswords = getCleanPasswords(
password,
zxcvbnOptions.l33tMaxSubstitutions,
zxcvbnOptions.trieNodeRoot,
matchOptions.password,
this.options.l33tMaxSubstitutions,
this.options.trieNodeRoot,
)
let hasFullMatch = false
let isFullSubstitution = true
subbedPasswords.forEach((subbedPassword) => {

@@ -87,13 +85,16 @@ if (hasFullMatch) {

const matchedDictionary = this.defaultMatch({
...matchOptions,
password: subbedPassword.password,
useLevenshtein: isFullSubstitution,
useLevenshtein: subbedPassword.isFullSubstitution,
})
// only the first entry has a full substitution
isFullSubstitution = false
matchedDictionary.forEach((match: DictionaryMatch) => {
if (!hasFullMatch) {
hasFullMatch = match.i === 0 && match.j === password.length - 1
hasFullMatch =
match.i === 0 && match.j === matchOptions.password.length - 1
}
const extras = getExtras(subbedPassword, match.i, match.j)
const token = password.slice(extras.i, +extras.j + 1 || 9e9)
const token = matchOptions.password.slice(
extras.i,
+extras.j + 1 || 9e9,
)
const newMatch: L33tMatch = {

@@ -100,0 +101,0 @@ ...match,

import { DictionaryMatch } from '../../../../types'
import { DefaultMatch } from '../../types'
import { DefaultMatch, DictionaryMatchOptions } from '../../types'
import Options from '../../../../Options'

@@ -10,11 +11,11 @@ /*

class MatchReverse {
defaultMatch: DefaultMatch
constructor(
private options: Options,
private defaultMatch: DefaultMatch,
) {}
constructor(defaultMatch: DefaultMatch) {
this.defaultMatch = defaultMatch
}
match({ password }: { password: string }) {
const passwordReversed = password.split('').reverse().join('')
match(matchOptions: DictionaryMatchOptions) {
const passwordReversed = matchOptions.password.split('').reverse().join('')
return this.defaultMatch({
...matchOptions,
password: passwordReversed,

@@ -26,4 +27,4 @@ }).map((match: DictionaryMatch) => ({

// map coordinates back to original string
i: password.length - 1 - match.j,
j: password.length - 1 - match.i,
i: matchOptions.password.length - 1 - match.j,
j: matchOptions.password.length - 1 - match.i,
}))

@@ -30,0 +31,0 @@ }

@@ -19,2 +19,3 @@ import TrieNode from './TrieNode'

changes: IndexedPasswordChanges[]
isFullSubstitution: boolean
}

@@ -79,3 +80,7 @@

if (onlyFullSub === isFullSub) {
this.finalPasswords.push({ password: this.buffer.join(''), changes })
this.finalPasswords.push({
password: this.buffer.join(''),
changes,
isFullSubstitution: onlyFullSub,
})
}

@@ -92,2 +97,3 @@ return

const cur = nodes[i - index]
const sub = cur.parents.join('')
if (cur.isTerminal()) {

@@ -97,6 +103,3 @@ // Skip if this would be a 4th or more consecutive substitution of the same letter

// So we can ignore the rest to save calculation time
if (
lastSubLetter === cur.parents.join('') &&
consecutiveSubCount >= 3
) {
if (lastSubLetter === sub && consecutiveSubCount >= 3) {
// eslint-disable-next-line no-continue

@@ -106,10 +109,10 @@ continue

hasSubs = true
const subs = cur.subs!
const letters = cur.subs!
// eslint-disable-next-line no-restricted-syntax
for (const sub of subs) {
this.buffer.push(sub)
for (const letter of letters) {
this.buffer.push(letter)
const newSubs = changes.concat({
i: subIndex,
letter: sub,
substitution: cur.parents.join(''),
letter,
substitution: sub,
})

@@ -121,10 +124,8 @@

isFullSub,
index: i + 1,
subIndex: subIndex + sub.length,
index: index + sub.length,
subIndex: subIndex + letter.length,
changes: newSubs,
lastSubLetter: cur.parents.join(''),
lastSubLetter: sub,
consecutiveSubCount:
lastSubLetter === cur.parents.join('')
? consecutiveSubCount + 1
: 1,
lastSubLetter === sub ? consecutiveSubCount + 1 : 1,
})

@@ -131,0 +132,0 @@ // backtrack by ignoring the added postfix

@@ -1,11 +0,11 @@

import { zxcvbnOptions } from '../../Options'
import Options from '../../Options'
import { MatchEstimated } from '../../types'
export default (match: MatchEstimated) => {
export default (options: Options, match: MatchEstimated) => {
if (match.regexName === 'recentYear') {
return {
warning: zxcvbnOptions.translations.warnings.recentYears,
warning: options.translations.warnings.recentYears,
suggestions: [
zxcvbnOptions.translations.suggestions.recentYears,
zxcvbnOptions.translations.suggestions.associatedYears,
options.translations.suggestions.recentYears,
options.translations.suggestions.associatedYears,
],

@@ -12,0 +12,0 @@ }

import { REGEXEN } from '../../data/const'
import { sorted } from '../../helper'
import { RegexMatch } from '../../types'
import { sorted } from '../../utils/helper'
import { MatchOptions, RegexMatch } from '../../types'
import Options from '../../Options'
interface RegexMatchOptions {
password: string
regexes?: typeof REGEXEN
}
type RegexMatchOptions = Pick<MatchOptions, 'password'>

@@ -17,6 +15,8 @@ type RegexesKeys = keyof typeof REGEXEN

class MatchRegex {
match({ password, regexes = REGEXEN }: RegexMatchOptions) {
constructor(private options: Options) {}
match({ password }: RegexMatchOptions) {
const matches: RegexMatch[] = []
Object.keys(regexes).forEach((name) => {
const regex = regexes[name as RegexesKeys]
Object.keys(REGEXEN).forEach((name) => {
const regex = REGEXEN[name as RegexesKeys]
regex.lastIndex = 0 // keeps regexMatch stateless

@@ -23,0 +23,0 @@

@@ -1,8 +0,8 @@

import { zxcvbnOptions } from '../../Options'
import Options from '../../Options'
import { MatchEstimated } from '../../types'
export default (match: MatchEstimated) => {
let warning = zxcvbnOptions.translations.warnings.extendedRepeat
export default (options: Options, match: MatchEstimated) => {
let warning = options.translations.warnings.extendedRepeat
if (match.baseToken.length === 1) {
warning = zxcvbnOptions.translations.warnings.simpleRepeat
warning = options.translations.warnings.simpleRepeat
}

@@ -12,4 +12,4 @@

warning,
suggestions: [zxcvbnOptions.translations.suggestions.repeated],
suggestions: [options.translations.suggestions.repeated],
}
}

@@ -1,9 +0,6 @@

import { RepeatMatch } from '../../types'
import scoring from '../../scoring'
import { MatchOptions, RepeatMatch } from '../../types'
import Scoring from '../../scoring'
import Matching from '../../Matching'
import Options from '../../Options'
interface RepeatMatchOptions {
password: string
omniMatch: Matching
}
/*

@@ -15,4 +12,10 @@ *-------------------------------------------------------------------------------

class MatchRepeat {
private scoring: Scoring
constructor(private options: Options) {
this.scoring = new Scoring(options)
}
// eslint-disable-next-line max-statements
match({ password, omniMatch }: RepeatMatchOptions) {
match({ password, omniMatch }: MatchOptions) {
const matches: (RepeatMatch | Promise<RepeatMatch>)[] = []

@@ -127,3 +130,3 @@ let lastIndex = 0

return matches.then((resolvedMatches) => {
const baseAnalysis = scoring.mostGuessableMatchSequence(
const baseAnalysis = this.scoring.mostGuessableMatchSequence(
baseToken,

@@ -135,3 +138,6 @@ resolvedMatches,

}
const baseAnalysis = scoring.mostGuessableMatchSequence(baseToken, matches)
const baseAnalysis = this.scoring.mostGuessableMatchSequence(
baseToken,
matches,
)
return baseAnalysis.guesses

@@ -138,0 +144,0 @@ }

import { SEPERATOR_CHARS } from '../../data/const'
import { SeparatorMatch } from '../../types'
import { MatchOptions, SeparatorMatch } from '../../types'
import Options from '../../Options'
interface SeparatorMatchOptions {
password: string
}
type SeparatorMatchOptions = Pick<MatchOptions, 'password'>

@@ -16,2 +15,4 @@ const separatorRegex = new RegExp(`[${SEPERATOR_CHARS.join('')}]`)

class MatchSeparator {
constructor(private options: Options) {}
static getMostUsedSeparatorChar(password: string): string | undefined {

@@ -18,0 +19,0 @@ const mostUsedSeperators = [

@@ -1,8 +0,8 @@

import { zxcvbnOptions } from '../../Options'
import Options from '../../Options'
export default () => {
export default (options: Options) => {
return {
warning: zxcvbnOptions.translations.warnings.sequences,
suggestions: [zxcvbnOptions.translations.suggestions.sequences],
warning: options.translations.warnings.sequences,
suggestions: [options.translations.suggestions.sequences],
}
}
import { ALL_UPPER, ALL_LOWER, ALL_DIGIT } from '../../data/const'
import { SequenceMatch } from '../../types'
import { MatchOptions, SequenceMatch } from '../../types'
import Options from '../../Options'

@@ -12,5 +13,3 @@ type UpdateParams = {

interface SequenceMatchOptions {
password: string
}
type SequenceMatchOptions = Pick<MatchOptions, 'password'>
/*

@@ -24,2 +23,4 @@ *-------------------------------------------------------------------------------

constructor(private options: Options) {}
// eslint-disable-next-line max-statements

@@ -26,0 +27,0 @@ match({ password }: SequenceMatchOptions) {

@@ -1,13 +0,13 @@

import { zxcvbnOptions } from '../../Options'
import Options from '../../Options'
import { MatchEstimated } from '../../types'
export default (match: MatchEstimated) => {
let warning = zxcvbnOptions.translations.warnings.keyPattern
export default (options: Options, match: MatchEstimated) => {
let warning = options.translations.warnings.keyPattern
if (match.turns === 1) {
warning = zxcvbnOptions.translations.warnings.straightRow
warning = options.translations.warnings.straightRow
}
return {
warning,
suggestions: [zxcvbnOptions.translations.suggestions.longerKeyboardPattern],
suggestions: [options.translations.suggestions.longerKeyboardPattern],
}
}

@@ -1,8 +0,6 @@

import { sorted, extend } from '../../helper'
import { zxcvbnOptions } from '../../Options'
import { LooseObject, SpatialMatch } from '../../types'
import { sorted, extend } from '../../utils/helper'
import Options from '../../Options'
import { LooseObject, MatchOptions, SpatialMatch } from '../../types'
interface SpatialMatchOptions {
password: string
}
type SpatialMatchOptions = Pick<MatchOptions, 'password'>
/*

@@ -16,6 +14,8 @@ * ------------------------------------------------------------------------------

constructor(private options: Options) {}
match({ password }: SpatialMatchOptions) {
const matches: SpatialMatch[] = []
Object.keys(zxcvbnOptions.graphs).forEach((graphName) => {
const graph = zxcvbnOptions.graphs[graphName]
Object.keys(this.options.graphs).forEach((graphName) => {
const graph = this.options.graphs[graphName]
extend(matches, this.helper(password, graph, graphName))

@@ -22,0 +22,0 @@ })

import utils from '../../scoring/utils'
import { zxcvbnOptions } from '../../Options'
import { LooseObject, MatchEstimated, MatchExtended } from '../../types'
import Options from '../../Options'
import {
LooseObject,
MatchEstimated,
MatchExtended,
OptionsGraphEntry,
} from '../../types'
interface EstimatePossiblePatternsOptions {
token: string
graph: string
turns: number

@@ -21,9 +25,8 @@ }

const estimatePossiblePatterns = ({
token,
graph,
turns,
}: EstimatePossiblePatternsOptions) => {
const startingPosition = Object.keys(zxcvbnOptions.graphs[graph]).length
const averageDegree = calcAverageDegree(zxcvbnOptions.graphs[graph])
const estimatePossiblePatterns = (
graphEntry: OptionsGraphEntry,
{ token, turns }: EstimatePossiblePatternsOptions,
) => {
const startingPosition = Object.keys(graphEntry).length
const averageDegree = calcAverageDegree(graphEntry)

@@ -42,9 +45,10 @@ let guesses = 0

export default ({
graph,
token,
shiftedCount,
turns,
}: MatchExtended | MatchEstimated) => {
let guesses = estimatePossiblePatterns({ token, graph, turns })
export default (
{ graph, token, shiftedCount, turns }: MatchExtended | MatchEstimated,
options: Options,
) => {
let guesses = estimatePossiblePatterns(options.graphs[graph], {
token,
turns,
})

@@ -51,0 +55,0 @@ // add extra guesses for shifted keys. (% instead of 5, A instead of a.)

@@ -1,3 +0,3 @@

import { extend, sorted } from './helper'
import { MatchExtended, MatchingType } from './types'
import { extend, sorted } from './utils/helper'
import { MatchExtended, MatchingType, UserInputsOptions } from './types'
import dateMatcher from './matcher/date/matching'

@@ -10,3 +10,3 @@ import dictionaryMatcher from './matcher/dictionary/matching'

import separatorMatcher from './matcher/separator/matching'
import { zxcvbnOptions } from './Options'
import Options from './Options'

@@ -24,3 +24,3 @@ /*

class Matching {
readonly matchers: Matchers = {
private readonly matchers: Matchers = {
date: dateMatcher,

@@ -36,34 +36,36 @@ dictionary: dictionaryMatcher,

match(password: string): MatchExtended[] | Promise<MatchExtended[]> {
const matches: MatchExtended[] = []
constructor(private options: Options) {}
const promises: Promise<MatchExtended[]>[] = []
const matchers = [
...Object.keys(this.matchers),
...Object.keys(zxcvbnOptions.matchers),
]
matchers.forEach((key) => {
if (!this.matchers[key] && !zxcvbnOptions.matchers[key]) {
return
}
const Matcher = this.matchers[key]
? this.matchers[key]
: zxcvbnOptions.matchers[key].Matching
const usedMatcher = new Matcher()
const result = usedMatcher.match({
password,
omniMatch: this,
private matcherFactory(name: string) {
if (!this.matchers[name] && !this.options.matchers[name]) {
return null
}
const Matcher = this.matchers[name]
? this.matchers[name]
: this.options.matchers[name].Matching
return new Matcher(this.options)
}
private processResult(
matches: MatchExtended[],
promises: Promise<MatchExtended[]>[],
result: MatchExtended[] | Promise<MatchExtended[]>,
) {
if (result instanceof Promise) {
result.then((response) => {
extend(matches, response)
})
promises.push(result)
} else {
extend(matches, result)
}
}
if (result instanceof Promise) {
result.then((response) => {
extend(matches, response)
})
promises.push(result)
} else {
extend(matches, result)
}
})
private handlePromises(
matches: MatchExtended[],
promises: Promise<MatchExtended[]>[],
) {
if (promises.length > 0) {
return new Promise((resolve, reject) => {
return new Promise<MatchExtended[]>((resolve, reject) => {
Promise.all(promises)

@@ -80,4 +82,33 @@ .then(() => {

}
match(
password: string,
userInputsOptions?: UserInputsOptions,
): MatchExtended[] | Promise<MatchExtended[]> {
const matches: MatchExtended[] = []
const promises: Promise<MatchExtended[]>[] = []
const matchers = [
...Object.keys(this.matchers),
...Object.keys(this.options.matchers),
]
matchers.forEach((key) => {
const matcher = this.matcherFactory(key)
if (!matcher) {
return
}
const result = matcher.match({
password,
omniMatch: this,
userInputsOptions,
})
// extends matches and promises by references
this.processResult(matches, promises, result)
})
return this.handlePromises(matches, promises)
}
}
export default Matching

@@ -1,2 +0,2 @@

import { buildRankedDictionary } from './helper'
import { buildRankedDictionary } from './utils/helper'
import {

@@ -11,2 +11,5 @@ TranslationKeys,

Matcher,
UserInputsOptions,
RankedDictionary,
TimeEstimationValues,
} from './types'

@@ -17,36 +20,55 @@ import l33tTable from './data/l33tTable'

import l33tTableToTrieNode from './matcher/dictionary/variants/matching/unmunger/l33tTableToTrieNode'
import {
checkTimeEstimationValues,
timeEstimationValuesDefaults,
} from './TimeEstimates'
export class Options {
matchers: Matchers = {}
export default class Options {
public matchers: Matchers = {}
l33tTable: OptionsL33tTable = l33tTable
public l33tTable: OptionsL33tTable = l33tTable
trieNodeRoot: TrieNode = l33tTableToTrieNode(l33tTable, new TrieNode())
public trieNodeRoot: TrieNode = l33tTableToTrieNode(l33tTable, new TrieNode())
dictionary: OptionsDictionary = {
public dictionary: OptionsDictionary = {
userInputs: [],
}
rankedDictionaries: RankedDictionaries = {}
public rankedDictionaries: RankedDictionaries = {}
rankedDictionariesMaxWordSize: Record<string, number> = {}
public rankedDictionariesMaxWordSize: Record<string, number> = {}
translations: TranslationKeys = translationKeys
public translations: TranslationKeys = translationKeys
graphs: OptionsGraph = {}
public graphs: OptionsGraph = {}
useLevenshteinDistance: boolean = false
public useLevenshteinDistance: boolean = false
levenshteinThreshold: number = 2
public levenshteinThreshold: number = 2
l33tMaxSubstitutions: number = 100
public l33tMaxSubstitutions: number = 100
maxLength: number = 256
public maxLength: number = 256
constructor() {
this.setRankedDictionaries()
timeEstimationValues: TimeEstimationValues = {
scoring: {
...timeEstimationValuesDefaults.scoring,
},
attackTime: {
...timeEstimationValuesDefaults.attackTime,
},
}
constructor(
options: OptionsType = {},
customMatchers: Record<string, Matcher> = {},
) {
this.setOptions(options)
Object.entries(customMatchers).forEach(([name, matcher]) => {
this.addMatcher(name, matcher)
})
}
// eslint-disable-next-line max-statements,complexity
setOptions(options: OptionsType = {}) {
private setOptions(options: OptionsType = {}) {
if (options.l33tTable) {

@@ -86,5 +108,17 @@ this.l33tTable = options.l33tTable

}
if (options.timeEstimationValues !== undefined) {
checkTimeEstimationValues(options.timeEstimationValues)
this.timeEstimationValues = {
scoring: {
...options.timeEstimationValues.scoring,
},
attackTime: {
...options.timeEstimationValues.attackTime,
},
}
}
}
setTranslations(translations: TranslationKeys) {
private setTranslations(translations: TranslationKeys) {
if (this.checkCustomTranslations(translations)) {

@@ -97,3 +131,3 @@ this.translations = translations

checkCustomTranslations(translations: TranslationKeys) {
private checkCustomTranslations(translations: TranslationKeys) {
let valid = true

@@ -115,3 +149,3 @@ Object.keys(translationKeys).forEach((type) => {

setRankedDictionaries() {
private setRankedDictionaries() {
const rankedDictionaries: RankedDictionaries = {}

@@ -128,3 +162,3 @@ const rankedDictionariesMaxWorkSize: Record<string, number> = {}

getRankedDictionariesMaxWordSize(list: (string | number)[]) {
private getRankedDictionariesMaxWordSize(list: (string | number)[]) {
const data = list.map((el) => {

@@ -144,3 +178,3 @@ if (typeof el !== 'string') {

buildSanitizedRankedDictionary(list: (string | number)[]) {
private buildSanitizedRankedDictionary(list: (string | number)[]) {
const sanitizedInputs: string[] = []

@@ -162,15 +196,20 @@

extendUserInputsDictionary(dictionary: (string | number)[]) {
if (!this.dictionary.userInputs) {
this.dictionary.userInputs = []
public getUserInputsOptions(
dictionary?: (string | number)[],
): UserInputsOptions {
let rankedDictionary: RankedDictionary = {}
let rankedDictionaryMaxWordSize: number = 0
if (dictionary) {
rankedDictionary = this.buildSanitizedRankedDictionary(dictionary)
rankedDictionaryMaxWordSize =
this.getRankedDictionariesMaxWordSize(dictionary)
}
const newList = [...this.dictionary.userInputs, ...dictionary]
this.rankedDictionaries.userInputs =
this.buildSanitizedRankedDictionary(newList)
this.rankedDictionariesMaxWordSize.userInputs =
this.getRankedDictionariesMaxWordSize(newList)
return {
rankedDictionary,
rankedDictionaryMaxWordSize,
}
}
public addMatcher(name: string, matcher: Matcher) {
private addMatcher(name: string, matcher: Matcher) {
if (this.matchers[name]) {

@@ -183,3 +222,1 @@ console.info(`Matcher ${name} already exists`)

}
export const zxcvbnOptions = new Options()

@@ -6,3 +6,3 @@ import {

import utils from './utils'
import { zxcvbnOptions } from '../Options'
import Options from '../Options'
import {

@@ -53,11 +53,12 @@ DefaultScoringFunction,

const getScoring = (name: string, match: MatchExtended | MatchEstimated) => {
const getScoring = (
options: Options,
name: string,
match: MatchExtended | MatchEstimated,
) => {
if (matchers[name]) {
return matchers[name](match)
return matchers[name](match, options)
}
if (
zxcvbnOptions.matchers[name] &&
'scoring' in zxcvbnOptions.matchers[name]
) {
return zxcvbnOptions.matchers[name].scoring(match)
if (options.matchers[name] && 'scoring' in options.matchers[name]) {
return options.matchers[name].scoring(match, options)
}

@@ -71,3 +72,7 @@ return 0

// eslint-disable-next-line complexity, max-statements
export default (match: MatchExtended | MatchEstimated, password: string) => {
export default (
options: Options,
match: MatchExtended | MatchEstimated,
password: string,
) => {
const extraData: LooseObject = {}

@@ -81,3 +86,3 @@ // a match's guess estimate doesn't change. cache it.

const estimationResult = getScoring(match.pattern, match)
const estimationResult = getScoring(options, match.pattern, match)
let guesses = 0

@@ -84,0 +89,0 @@ if (typeof estimationResult === 'number') {

@@ -9,7 +9,9 @@ import utils from './utils'

LooseObject,
Optimal,
} from '../types'
import Options from '../Options'
const scoringHelper = {
password: '',
optimal: {} as any,
optimal: {} as Optimal,
excludeAdditive: false,

@@ -41,5 +43,5 @@ separatorRegex: undefined as RegExp | null | undefined,

// than previously encountered sequences, updating state if so.
update(match: MatchExtended, sequenceLength: number) {
update(options: Options, match: MatchExtended, sequenceLength: number) {
const k = match.j
const estimatedMatch = estimateGuesses(match, this.password)
const estimatedMatch = estimateGuesses(options, match, this.password)
let pi = estimatedMatch.guesses as number

@@ -80,6 +82,6 @@ if (sequenceLength > 1) {

// helper: evaluate bruteforce matches ending at passwordCharIndex.
bruteforceUpdate(passwordCharIndex: number) {
bruteforceUpdate(options: Options, passwordCharIndex: number) {
// see if a single bruteforce match spanning the passwordCharIndex-prefix is optimal.
let match = this.makeBruteforceMatch(0, passwordCharIndex)
this.update(match, 1)
this.update(options, match, 1)

@@ -101,3 +103,3 @@ for (let i = 1; i <= passwordCharIndex; i += 1) {

// try adding m to this length-sequenceLength sequence.
this.update(match, parseInt(sequenceLength, 10) + 1)
this.update(options, match, parseInt(sequenceLength, 10) + 1)
}

@@ -138,3 +140,5 @@ })

export default {
export default class Scoring {
constructor(private options: Options) {}
// ------------------------------------------------------------------------------

@@ -201,2 +205,3 @@ // search --- most guessable match sequence -------------------------------------

// optimal.m[k][sequenceLength] is undefined.
// @ts-ignore
m: scoringHelper.fillArray(passwordLength, 'object'),

@@ -215,10 +220,14 @@ // same structure as optimal.m -- holds the product term Prod(m.guesses for m in sequence).

(sequenceLength) => {
scoringHelper.update(match, parseInt(sequenceLength, 10) + 1)
scoringHelper.update(
this.options,
match,
parseInt(sequenceLength, 10) + 1,
)
},
)
} else {
scoringHelper.update(match, 1)
scoringHelper.update(this.options, match, 1)
}
})
scoringHelper.bruteforceUpdate(k)
scoringHelper.bruteforceUpdate(this.options, k)
}

@@ -234,3 +243,3 @@ const optimalMatchSequence = scoringHelper.unwind(passwordLength)

}
},
}

@@ -247,3 +256,3 @@ getGuesses(password: string, optimalSequenceLength: number) {

return guesses
},
}
}

@@ -1,3 +0,8 @@

import { zxcvbnOptions } from './Options'
import { CrackTimesDisplay, CrackTimesSeconds, Score } from './types'
import Options from './Options'
import {
CrackTimes,
CrackTimesSeconds,
Score,
TimeEstimationValues,
} from './types'

@@ -22,2 +27,32 @@ const SECOND = 1

export const timeEstimationValuesDefaults: TimeEstimationValues = {
scoring: {
0: 1e3,
1: 1e6,
2: 1e8,
3: 1e10,
},
attackTime: {
onlineThrottlingXPerHour: 100,
onlineNoThrottlingXPerSecond: 10,
offlineSlowHashingXPerSecond: 1e4,
offlineFastHashingXPerSecond: 1e10,
},
}
export const checkTimeEstimationValues = (
timeEstimationValues: TimeEstimationValues,
) => {
Object.entries(timeEstimationValues).forEach(([key, data]) => {
Object.entries(data).forEach(([subKey, value]) => {
// @ts-ignore
if (value < timeEstimationValuesDefaults[key][subKey]) {
throw new Error(
'Time estimation values are not to be allowed to be less than default',
)
}
})
})
}
/*

@@ -28,55 +63,59 @@ * -------------------------------------------------------------------------------

*/
class TimeEstimates {
translate(displayStr: string, value: number | undefined) {
let key = displayStr
if (value !== undefined && value !== 1) {
key += 's'
export class TimeEstimates {
constructor(private options: Options) {}
public estimateAttackTimes(guesses: number) {
const crackTimesSeconds = this.calculateCrackTimesSeconds(guesses)
const crackTimes = Object.keys(crackTimesSeconds).reduce(
(previousValue, crackTime) => {
const usedScenario = crackTime as keyof CrackTimesSeconds
const seconds = crackTimesSeconds[usedScenario]
const { base, displayStr } = this.displayTime(seconds)
// eslint-disable-next-line no-param-reassign
previousValue[usedScenario] = {
base,
seconds,
display: this.translate(displayStr, base),
}
return previousValue
},
{} as CrackTimes,
)
return {
crackTimes,
score: this.guessesToScore(guesses),
}
const { timeEstimation } = zxcvbnOptions.translations
return timeEstimation[key as keyof typeof timeEstimation].replace(
'{base}',
`${value}`,
)
}
estimateAttackTimes(guesses: number) {
const crackTimesSeconds: CrackTimesSeconds = {
onlineThrottling100PerHour: guesses / (100 / 3600),
onlineNoThrottling10PerSecond: guesses / 10,
offlineSlowHashing1e4PerSecond: guesses / 1e4,
offlineFastHashing1e10PerSecond: guesses / 1e10,
}
const crackTimesDisplay: CrackTimesDisplay = {
onlineThrottling100PerHour: '',
onlineNoThrottling10PerSecond: '',
offlineSlowHashing1e4PerSecond: '',
offlineFastHashing1e10PerSecond: '',
}
Object.keys(crackTimesSeconds).forEach((scenario) => {
const seconds = crackTimesSeconds[scenario as keyof CrackTimesSeconds]
crackTimesDisplay[scenario as keyof CrackTimesDisplay] =
this.displayTime(seconds)
})
private calculateCrackTimesSeconds(guesses: number): CrackTimesSeconds {
const attackTimesOptions = this.options.timeEstimationValues.attackTime
return {
crackTimesSeconds,
crackTimesDisplay,
score: this.guessesToScore(guesses),
onlineThrottlingXPerHour:
guesses / (attackTimesOptions.onlineThrottlingXPerHour / 3600),
onlineNoThrottlingXPerSecond:
guesses / attackTimesOptions.onlineNoThrottlingXPerSecond,
offlineSlowHashingXPerSecond:
guesses / attackTimesOptions.offlineSlowHashingXPerSecond,
offlineFastHashingXPerSecond:
guesses / attackTimesOptions.offlineFastHashingXPerSecond,
}
}
guessesToScore(guesses: number): Score {
private guessesToScore(guesses: number): Score {
const scoringOptions = this.options.timeEstimationValues.scoring
const DELTA = 5
if (guesses < 1e3 + DELTA) {
if (guesses < scoringOptions[0] + DELTA) {
// risky password: "too guessable"
return 0
}
if (guesses < 1e6 + DELTA) {
if (guesses < scoringOptions[1] + DELTA) {
// modest protection from throttled online attacks: "very guessable"
return 1
}
if (guesses < 1e8 + DELTA) {
if (guesses < scoringOptions[2] + DELTA) {
// modest protection from unthrottled online attacks: "somewhat guessable"
return 2
}
if (guesses < 1e10 + DELTA) {
if (guesses < scoringOptions[3] + DELTA) {
// modest protection from offline attacks: "safely unguessable"

@@ -90,5 +129,5 @@ // assuming a salted, slow hash function like bcrypt, scrypt, PBKDF2, argon, etc

displayTime(seconds: number) {
private displayTime(seconds: number) {
let displayStr = 'centuries'
let base
let base = null
const timeKeys = Object.keys(times)

@@ -106,6 +145,19 @@ const foundIndex = timeKeys.findIndex(

}
return this.translate(displayStr, base)
return {
base,
displayStr,
}
}
private translate(displayStr: string, value: number | null) {
let key = displayStr
if (value !== null && value !== 1) {
key += 's'
}
const { timeEstimation } = this.options.translations
return timeEstimation[key as keyof typeof timeEstimation].replace(
'{base}',
`${value}`,
)
}
}
export default TimeEstimates

@@ -7,2 +7,3 @@ import translationKeys from './data/translationKeys'

import { PasswordChanges } from './matcher/dictionary/variants/matching/unmunger/getCleanPasswords'
import Options from './Options'

@@ -59,3 +60,3 @@ export type TranslationKeys = typeof translationKeys

rank: number
dictionaryName: DictionaryNames
dictionaryName: DictionaryNames | string
reversed: boolean

@@ -135,21 +136,27 @@ l33t: boolean

export interface Optimal {
m: Match
pi: Match
g: Match
m: Match[]
pi: Record<string, number>[]
g: Record<string, number>[]
}
export interface CrackTimesSeconds {
onlineThrottling100PerHour: number
onlineNoThrottling10PerSecond: number
offlineSlowHashing1e4PerSecond: number
offlineFastHashing1e10PerSecond: number
export interface CrackTime {
base: number | null
seconds: number
display: string
}
export interface CrackTimesDisplay {
onlineThrottling100PerHour: string
onlineNoThrottling10PerSecond: string
offlineSlowHashing1e4PerSecond: string
offlineFastHashing1e10PerSecond: string
export interface CrackTimes {
onlineThrottlingXPerHour: CrackTime
onlineNoThrottlingXPerSecond: CrackTime
offlineSlowHashingXPerSecond: CrackTime
offlineFastHashingXPerSecond: CrackTime
}
export interface CrackTimesSeconds {
onlineThrottlingXPerHour: number
onlineNoThrottlingXPerSecond: number
offlineSlowHashingXPerSecond: number
offlineFastHashingXPerSecond: number
}
export interface FeedbackType {

@@ -178,2 +185,17 @@ warning: string | null

export interface TimeEstimationValues {
scoring: {
0: number
1: number
2: number
3: number
}
attackTime: {
onlineThrottlingXPerHour: number
onlineNoThrottlingXPerSecond: number
offlineSlowHashingXPerSecond: number
offlineFastHashingXPerSecond: number
}
}
export interface OptionsType {

@@ -216,2 +238,6 @@ /**

maxLength?: number
/**
* @description Define the values to calculate the scoring and attack times. DO NOT CHANGE unless you know what you are doing. The default values are just fine as long as you are using a strong, slow hash function. Can be adjusted to account for increasingly powerful attacker hardware.
*/
timeEstimationValues?: TimeEstimationValues
}

@@ -228,2 +254,3 @@

export type DefaultFeedbackFunction = (
options: Options,
match: MatchEstimated,

@@ -235,4 +262,9 @@ isSoleMatch?: boolean,

match: MatchExtended | MatchEstimated,
options: Options,
) => number | DictionaryReturn
export interface UserInputsOptions {
rankedDictionary: RankedDictionary
rankedDictionaryMaxWordSize: number
}
export interface MatchOptions {

@@ -244,8 +276,10 @@ password: string

omniMatch: Matching
userInputsOptions?: UserInputsOptions
}
export type MatchingType = new () => {
export type MatchingType = new (options: Options) => {
match({
password,
omniMatch,
userInputsOptions,
}: MatchOptions): MatchExtended[] | Promise<MatchExtended[]>

@@ -268,4 +302,3 @@ }

feedback: FeedbackType
crackTimesSeconds: CrackTimesSeconds
crackTimesDisplay: CrackTimesDisplay
crackTimes: CrackTimes
score: Score

@@ -272,0 +305,0 @@ password: string

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc