84 lines
3.3 KiB
Plaintext
84 lines
3.3 KiB
Plaintext
'use strict';
|
|
|
|
var levenshtein = require('../../utils/levenshtein.cjs');
|
|
var types = require('../../types.cjs');
|
|
var mergeUserInputDictionary = require('../../utils/mergeUserInputDictionary.cjs');
|
|
|
|
class MatchDictionary extends types.MatcherBaseClass {
|
|
constructor(options, wordSequenceCheck) {
|
|
super(options);
|
|
this.wordSequenceCheck = wordSequenceCheck;
|
|
}
|
|
getRangedDictionaries(userInputsOptions) {
|
|
if (this.wordSequenceCheck) {
|
|
const rankedDictionaries = {};
|
|
const rankedDictionariesMaxWordSize = {};
|
|
Object.keys(this.options.rankedDictionaries).forEach(key => {
|
|
if (this.options.isWordSequence(key)) {
|
|
rankedDictionaries[key] = this.options.rankedDictionaries[key];
|
|
rankedDictionariesMaxWordSize[key] = this.options.rankedDictionariesMaxWordSize[key];
|
|
}
|
|
});
|
|
return {
|
|
rankedDictionaries,
|
|
rankedDictionariesMaxWordSize
|
|
};
|
|
}
|
|
return mergeUserInputDictionary(this.options.rankedDictionaries, this.options.rankedDictionariesMaxWordSize, userInputsOptions);
|
|
}
|
|
match({
|
|
password,
|
|
userInputsOptions,
|
|
useLevenshtein = true
|
|
}) {
|
|
const matches = [];
|
|
const passwordLength = password.length;
|
|
const passwordLower = password.toLowerCase();
|
|
const {
|
|
rankedDictionaries,
|
|
rankedDictionariesMaxWordSize
|
|
} = this.getRangedDictionaries(userInputsOptions);
|
|
// eslint-disable-next-line complexity,max-statements
|
|
Object.keys(rankedDictionaries).forEach(dictionaryName => {
|
|
const rankedDict = rankedDictionaries[dictionaryName];
|
|
const longestDictionaryWordSize = rankedDictionariesMaxWordSize[dictionaryName];
|
|
const searchWidth = Math.min(longestDictionaryWordSize, passwordLength);
|
|
for (let i = 0; i < passwordLength; i += 1) {
|
|
const searchEnd = Math.min(i + searchWidth, passwordLength);
|
|
for (let j = i; j < searchEnd; j += 1) {
|
|
const usedPassword = passwordLower.slice(i, j + 1 || 9e9);
|
|
const isInDictionary = usedPassword in rankedDict;
|
|
let foundLevenshteinDistance = {};
|
|
// only use levenshtein distance on full password to minimize the performance drop
|
|
// and because otherwise there would be to many false positives
|
|
const isFullPassword = i === 0 && j === passwordLength - 1;
|
|
if (this.options.useLevenshteinDistance && isFullPassword && !isInDictionary && useLevenshtein) {
|
|
foundLevenshteinDistance = levenshtein(usedPassword, rankedDict, this.options.levenshteinThreshold);
|
|
}
|
|
const isLevenshteinMatch = Object.keys(foundLevenshteinDistance).length !== 0;
|
|
if (isInDictionary || isLevenshteinMatch) {
|
|
const usedRankPassword = isLevenshteinMatch ? foundLevenshteinDistance.levenshteinDistanceEntry : usedPassword;
|
|
const rank = rankedDict[usedRankPassword];
|
|
matches.push({
|
|
pattern: 'dictionary',
|
|
i,
|
|
j,
|
|
token: password.slice(i, j + 1 || 9e9),
|
|
matchedWord: usedPassword,
|
|
rank,
|
|
dictionaryName: dictionaryName,
|
|
reversed: false,
|
|
l33t: false,
|
|
...foundLevenshteinDistance
|
|
});
|
|
}
|
|
}
|
|
}
|
|
});
|
|
return matches;
|
|
}
|
|
}
|
|
|
|
module.exports = MatchDictionary;
|
|
//# sourceMappingURL=matching.cjs.map
|