@zxcvbn-ts/core
Advanced tools
Comparing version 3.0.2 to 3.0.3
@@ -29,4 +29,5 @@ import dateSplits from './dateSplits.esm.js'; | ||
const SEPERATOR_CHARS = [' ', ',', ';', ':', '|', '/', '\\', '_', '.', '-']; | ||
const SEPERATOR_CHAR_COUNT = SEPERATOR_CHARS.length; | ||
export { ALL_DIGIT, ALL_LOWER, ALL_LOWER_INVERTED, ALL_UPPER, ALL_UPPER_INVERTED, ALPHA_INVERTED, BRUTEFORCE_CARDINALITY, DATE_MAX_YEAR, DATE_MIN_YEAR, DATE_SPLITS, END_UPPER, MIN_GUESSES_BEFORE_GROWING_SEQUENCE, MIN_SUBMATCH_GUESSES_MULTI_CHAR, MIN_SUBMATCH_GUESSES_SINGLE_CHAR, MIN_YEAR_SPACE, ONE_LOWER, ONE_UPPER, REFERENCE_YEAR, REGEXEN, SEPERATOR_CHARS, START_UPPER }; | ||
export { ALL_DIGIT, ALL_LOWER, ALL_LOWER_INVERTED, ALL_UPPER, ALL_UPPER_INVERTED, ALPHA_INVERTED, BRUTEFORCE_CARDINALITY, DATE_MAX_YEAR, DATE_MIN_YEAR, DATE_SPLITS, END_UPPER, MIN_GUESSES_BEFORE_GROWING_SEQUENCE, MIN_SUBMATCH_GUESSES_MULTI_CHAR, MIN_SUBMATCH_GUESSES_SINGLE_CHAR, MIN_YEAR_SPACE, ONE_LOWER, ONE_UPPER, REFERENCE_YEAR, REGEXEN, SEPERATOR_CHARS, SEPERATOR_CHAR_COUNT, START_UPPER }; | ||
//# sourceMappingURL=const.esm.js.map |
@@ -31,2 +31,3 @@ 'use strict'; | ||
const SEPERATOR_CHARS = [' ', ',', ';', ':', '|', '/', '\\', '_', '.', '-']; | ||
const SEPERATOR_CHAR_COUNT = SEPERATOR_CHARS.length; | ||
@@ -53,3 +54,4 @@ exports.ALL_DIGIT = ALL_DIGIT; | ||
exports.SEPERATOR_CHARS = SEPERATOR_CHARS; | ||
exports.SEPERATOR_CHAR_COUNT = SEPERATOR_CHAR_COUNT; | ||
exports.START_UPPER = START_UPPER; | ||
//# sourceMappingURL=const.js.map |
@@ -5,2 +5,8 @@ import { zxcvbnOptions } from '../../../../Options.esm.js'; | ||
const getExtras = (passwordWithSubs, i, j) => { | ||
const previousChanges = passwordWithSubs.changes.filter(changes => { | ||
return changes.i < i; | ||
}); | ||
const iUnsubbed = previousChanges.reduce((value, change) => { | ||
return value - change.letter.length + change.substitution.length; | ||
}, i); | ||
const usedChanges = passwordWithSubs.changes.filter(changes => { | ||
@@ -11,3 +17,3 @@ return changes.i >= i && changes.i <= j; | ||
return value - change.letter.length + change.substitution.length; | ||
}, j); | ||
}, j - i + iUnsubbed); | ||
const filtered = []; | ||
@@ -28,2 +34,3 @@ const subDisplay = []; | ||
return { | ||
i: iUnsubbed, | ||
j: jUnsubbed, | ||
@@ -72,3 +79,3 @@ subs: filtered, | ||
const extras = getExtras(subbedPassword, match.i, match.j); | ||
const token = password.slice(match.i, +extras.j + 1 || 9e9); | ||
const token = password.slice(extras.i, +extras.j + 1 || 9e9); | ||
const newMatch = { | ||
@@ -75,0 +82,0 @@ ...match, |
@@ -7,2 +7,8 @@ 'use strict'; | ||
const getExtras = (passwordWithSubs, i, j) => { | ||
const previousChanges = passwordWithSubs.changes.filter(changes => { | ||
return changes.i < i; | ||
}); | ||
const iUnsubbed = previousChanges.reduce((value, change) => { | ||
return value - change.letter.length + change.substitution.length; | ||
}, i); | ||
const usedChanges = passwordWithSubs.changes.filter(changes => { | ||
@@ -13,3 +19,3 @@ return changes.i >= i && changes.i <= j; | ||
return value - change.letter.length + change.substitution.length; | ||
}, j); | ||
}, j - i + iUnsubbed); | ||
const filtered = []; | ||
@@ -30,2 +36,3 @@ const subDisplay = []; | ||
return { | ||
i: iUnsubbed, | ||
j: jUnsubbed, | ||
@@ -74,3 +81,3 @@ subs: filtered, | ||
const extras = getExtras(subbedPassword, match.i, match.j); | ||
const token = password.slice(match.i, +extras.j + 1 || 9e9); | ||
const token = password.slice(extras.i, +extras.j + 1 || 9e9); | ||
const newMatch = { | ||
@@ -77,0 +84,0 @@ ...match, |
@@ -1,17 +0,42 @@ | ||
const getAllSubCombosHelper = ({ | ||
substr, | ||
buffer, | ||
limit, | ||
trieRoot | ||
}) => { | ||
const finalPasswords = []; | ||
// eslint-disable-next-line max-statements | ||
const helper = (onlyFullSub, isFullSub, index, subIndex, changes) => { | ||
if (finalPasswords.length >= limit) { | ||
class CleanPasswords { | ||
constructor({ | ||
substr, | ||
buffer, | ||
limit, | ||
trieRoot | ||
}) { | ||
this.finalPasswords = []; | ||
this.substr = substr; | ||
this.buffer = buffer; | ||
this.limit = limit; | ||
this.trieRoot = trieRoot; | ||
} | ||
getAllPossibleSubsAtIndex(index) { | ||
const nodes = []; | ||
let cur = this.trieRoot; | ||
for (let i = index; i < this.substr.length; i += 1) { | ||
const character = this.substr.charAt(i); | ||
cur = cur.getChild(character); | ||
if (!cur) { | ||
break; | ||
} | ||
nodes.push(cur); | ||
} | ||
return nodes; | ||
} | ||
// eslint-disable-next-line complexity,max-statements | ||
helper({ | ||
onlyFullSub, | ||
isFullSub, | ||
index, | ||
subIndex, | ||
changes | ||
}) { | ||
if (this.finalPasswords.length >= this.limit) { | ||
return; | ||
} | ||
if (index === substr.length) { | ||
if (index === this.substr.length) { | ||
if (onlyFullSub === isFullSub) { | ||
finalPasswords.push({ | ||
password: buffer.join(''), | ||
this.finalPasswords.push({ | ||
password: this.buffer.join(''), | ||
changes | ||
@@ -23,16 +48,7 @@ }); | ||
// first, exhaust all possible substitutions at this index | ||
const nodes = []; | ||
let cur = trieRoot; | ||
for (let i = index; i < substr.length; i += 1) { | ||
const character = substr.charAt(i); | ||
cur = cur.getChild(character); | ||
if (!cur) { | ||
break; | ||
} | ||
nodes.push(cur); | ||
} | ||
const nodes = [...this.getAllPossibleSubsAtIndex(index)]; | ||
let hasSubs = false; | ||
// iterate backward to get wider substitutions first | ||
for (let i = index + nodes.length - 1; i >= index; i -= 1) { | ||
cur = nodes[i - index]; | ||
const cur = nodes[i - index]; | ||
if (cur.isTerminal()) { | ||
@@ -43,3 +59,3 @@ hasSubs = true; | ||
for (const sub of subs) { | ||
buffer.push(sub); | ||
this.buffer.push(sub); | ||
const newSubs = changes.concat({ | ||
@@ -51,6 +67,12 @@ i: subIndex, | ||
// recursively build the rest of the string | ||
helper(onlyFullSub, isFullSub, i + 1, subIndex + sub.length, newSubs); | ||
this.helper({ | ||
onlyFullSub, | ||
isFullSub, | ||
index: i + 1, | ||
subIndex: subIndex + sub.length, | ||
changes: newSubs | ||
}); | ||
// backtrack by ignoring the added postfix | ||
buffer.pop(); | ||
if (finalPasswords.length >= limit) { | ||
this.buffer.pop(); | ||
if (this.finalPasswords.length >= this.limit) { | ||
return; | ||
@@ -64,16 +86,36 @@ } | ||
if (!onlyFullSub || !hasSubs) { | ||
const firstChar = substr.charAt(index); | ||
buffer.push(firstChar); | ||
helper(onlyFullSub, isFullSub && !hasSubs, index + 1, subIndex + 1, changes); | ||
buffer.pop(); | ||
const firstChar = this.substr.charAt(index); | ||
this.buffer.push(firstChar); | ||
this.helper({ | ||
onlyFullSub, | ||
isFullSub: isFullSub && !hasSubs, | ||
index: index + 1, | ||
subIndex: subIndex + 1, | ||
changes | ||
}); | ||
this.buffer.pop(); | ||
} | ||
}; | ||
// only full substitution | ||
helper(true, true, 0, 0, []); | ||
// only partial substitution | ||
helper(false, true, 0, 0, []); | ||
return finalPasswords; | ||
}; | ||
} | ||
getAll() { | ||
// only full substitution | ||
this.helper({ | ||
onlyFullSub: true, | ||
isFullSub: true, | ||
index: 0, | ||
subIndex: 0, | ||
changes: [] | ||
}); | ||
// only partial substitution | ||
this.helper({ | ||
onlyFullSub: false, | ||
isFullSub: true, | ||
index: 0, | ||
subIndex: 0, | ||
changes: [] | ||
}); | ||
return this.finalPasswords; | ||
} | ||
} | ||
const getCleanPasswords = (string, limit, trieRoot) => { | ||
return getAllSubCombosHelper({ | ||
const helper = new CleanPasswords({ | ||
substr: string, | ||
@@ -84,2 +126,3 @@ buffer: [], | ||
}); | ||
return helper.getAll(); | ||
}; | ||
@@ -86,0 +129,0 @@ |
'use strict'; | ||
const getAllSubCombosHelper = ({ | ||
substr, | ||
buffer, | ||
limit, | ||
trieRoot | ||
}) => { | ||
const finalPasswords = []; | ||
// eslint-disable-next-line max-statements | ||
const helper = (onlyFullSub, isFullSub, index, subIndex, changes) => { | ||
if (finalPasswords.length >= limit) { | ||
class CleanPasswords { | ||
constructor({ | ||
substr, | ||
buffer, | ||
limit, | ||
trieRoot | ||
}) { | ||
this.finalPasswords = []; | ||
this.substr = substr; | ||
this.buffer = buffer; | ||
this.limit = limit; | ||
this.trieRoot = trieRoot; | ||
} | ||
getAllPossibleSubsAtIndex(index) { | ||
const nodes = []; | ||
let cur = this.trieRoot; | ||
for (let i = index; i < this.substr.length; i += 1) { | ||
const character = this.substr.charAt(i); | ||
cur = cur.getChild(character); | ||
if (!cur) { | ||
break; | ||
} | ||
nodes.push(cur); | ||
} | ||
return nodes; | ||
} | ||
// eslint-disable-next-line complexity,max-statements | ||
helper({ | ||
onlyFullSub, | ||
isFullSub, | ||
index, | ||
subIndex, | ||
changes | ||
}) { | ||
if (this.finalPasswords.length >= this.limit) { | ||
return; | ||
} | ||
if (index === substr.length) { | ||
if (index === this.substr.length) { | ||
if (onlyFullSub === isFullSub) { | ||
finalPasswords.push({ | ||
password: buffer.join(''), | ||
this.finalPasswords.push({ | ||
password: this.buffer.join(''), | ||
changes | ||
@@ -25,16 +50,7 @@ }); | ||
// first, exhaust all possible substitutions at this index | ||
const nodes = []; | ||
let cur = trieRoot; | ||
for (let i = index; i < substr.length; i += 1) { | ||
const character = substr.charAt(i); | ||
cur = cur.getChild(character); | ||
if (!cur) { | ||
break; | ||
} | ||
nodes.push(cur); | ||
} | ||
const nodes = [...this.getAllPossibleSubsAtIndex(index)]; | ||
let hasSubs = false; | ||
// iterate backward to get wider substitutions first | ||
for (let i = index + nodes.length - 1; i >= index; i -= 1) { | ||
cur = nodes[i - index]; | ||
const cur = nodes[i - index]; | ||
if (cur.isTerminal()) { | ||
@@ -45,3 +61,3 @@ hasSubs = true; | ||
for (const sub of subs) { | ||
buffer.push(sub); | ||
this.buffer.push(sub); | ||
const newSubs = changes.concat({ | ||
@@ -53,6 +69,12 @@ i: subIndex, | ||
// recursively build the rest of the string | ||
helper(onlyFullSub, isFullSub, i + 1, subIndex + sub.length, newSubs); | ||
this.helper({ | ||
onlyFullSub, | ||
isFullSub, | ||
index: i + 1, | ||
subIndex: subIndex + sub.length, | ||
changes: newSubs | ||
}); | ||
// backtrack by ignoring the added postfix | ||
buffer.pop(); | ||
if (finalPasswords.length >= limit) { | ||
this.buffer.pop(); | ||
if (this.finalPasswords.length >= this.limit) { | ||
return; | ||
@@ -66,16 +88,36 @@ } | ||
if (!onlyFullSub || !hasSubs) { | ||
const firstChar = substr.charAt(index); | ||
buffer.push(firstChar); | ||
helper(onlyFullSub, isFullSub && !hasSubs, index + 1, subIndex + 1, changes); | ||
buffer.pop(); | ||
const firstChar = this.substr.charAt(index); | ||
this.buffer.push(firstChar); | ||
this.helper({ | ||
onlyFullSub, | ||
isFullSub: isFullSub && !hasSubs, | ||
index: index + 1, | ||
subIndex: subIndex + 1, | ||
changes | ||
}); | ||
this.buffer.pop(); | ||
} | ||
}; | ||
// only full substitution | ||
helper(true, true, 0, 0, []); | ||
// only partial substitution | ||
helper(false, true, 0, 0, []); | ||
return finalPasswords; | ||
}; | ||
} | ||
getAll() { | ||
// only full substitution | ||
this.helper({ | ||
onlyFullSub: true, | ||
isFullSub: true, | ||
index: 0, | ||
subIndex: 0, | ||
changes: [] | ||
}); | ||
// only partial substitution | ||
this.helper({ | ||
onlyFullSub: false, | ||
isFullSub: true, | ||
index: 0, | ||
subIndex: 0, | ||
changes: [] | ||
}); | ||
return this.finalPasswords; | ||
} | ||
} | ||
const getCleanPasswords = (string, limit, trieRoot) => { | ||
return getAllSubCombosHelper({ | ||
const helper = new CleanPasswords({ | ||
substr: string, | ||
@@ -86,2 +128,3 @@ buffer: [], | ||
}); | ||
return helper.getAll(); | ||
}; | ||
@@ -88,0 +131,0 @@ |
import utils from '../../../../scoring/utils.esm.js'; | ||
const countSubstring = (string, substring) => { | ||
let count = 0; | ||
let pos = string.indexOf(substring); | ||
while (pos >= 0) { | ||
count += 1; | ||
pos = string.indexOf(substring, pos + substring.length); | ||
} | ||
return count; | ||
}; | ||
const getCounts = ({ | ||
@@ -8,7 +17,7 @@ sub, | ||
// lower-case match.token before calculating: capitalization shouldn't affect l33t calc. | ||
const chrs = token.toLowerCase().split(''); | ||
const tokenLower = token.toLowerCase(); | ||
// num of subbed chars | ||
const subbedCount = chrs.filter(char => char === sub.substitution).length; | ||
const subbedCount = countSubstring(tokenLower, sub.substitution); | ||
// num of unsubbed chars | ||
const unsubbedCount = chrs.filter(char => char === sub.letter).length; | ||
const unsubbedCount = countSubstring(tokenLower, sub.letter); | ||
return { | ||
@@ -15,0 +24,0 @@ subbedCount, |
@@ -5,2 +5,11 @@ 'use strict'; | ||
const countSubstring = (string, substring) => { | ||
let count = 0; | ||
let pos = string.indexOf(substring); | ||
while (pos >= 0) { | ||
count += 1; | ||
pos = string.indexOf(substring, pos + substring.length); | ||
} | ||
return count; | ||
}; | ||
const getCounts = ({ | ||
@@ -11,7 +20,7 @@ sub, | ||
// lower-case match.token before calculating: capitalization shouldn't affect l33t calc. | ||
const chrs = token.toLowerCase().split(''); | ||
const tokenLower = token.toLowerCase(); | ||
// num of subbed chars | ||
const subbedCount = chrs.filter(char => char === sub.substitution).length; | ||
const subbedCount = countSubstring(tokenLower, sub.substitution); | ||
// num of unsubbed chars | ||
const unsubbedCount = chrs.filter(char => char === sub.letter).length; | ||
const unsubbedCount = countSubstring(tokenLower, sub.letter); | ||
return { | ||
@@ -18,0 +27,0 @@ subbedCount, |
@@ -18,13 +18,16 @@ import { REGEXEN } from '../../data/const.esm.js'; | ||
regex.lastIndex = 0; // keeps regexMatch stateless | ||
const regexMatch = regex.exec(password); | ||
if (regexMatch) { | ||
const token = regexMatch[0]; | ||
matches.push({ | ||
pattern: 'regex', | ||
token, | ||
i: regexMatch.index, | ||
j: regexMatch.index + regexMatch[0].length - 1, | ||
regexName: name, | ||
regexMatch | ||
}); | ||
let regexMatch; | ||
// eslint-disable-next-line no-cond-assign | ||
while (regexMatch = regex.exec(password)) { | ||
if (regexMatch) { | ||
const token = regexMatch[0]; | ||
matches.push({ | ||
pattern: 'regex', | ||
token, | ||
i: regexMatch.index, | ||
j: regexMatch.index + regexMatch[0].length - 1, | ||
regexName: name, | ||
regexMatch | ||
}); | ||
} | ||
} | ||
@@ -31,0 +34,0 @@ }); |
@@ -20,13 +20,16 @@ 'use strict'; | ||
regex.lastIndex = 0; // keeps regexMatch stateless | ||
const regexMatch = regex.exec(password); | ||
if (regexMatch) { | ||
const token = regexMatch[0]; | ||
matches.push({ | ||
pattern: 'regex', | ||
token, | ||
i: regexMatch.index, | ||
j: regexMatch.index + regexMatch[0].length - 1, | ||
regexName: name, | ||
regexMatch | ||
}); | ||
let regexMatch; | ||
// eslint-disable-next-line no-cond-assign | ||
while (regexMatch = regex.exec(password)) { | ||
if (regexMatch) { | ||
const token = regexMatch[0]; | ||
matches.push({ | ||
pattern: 'regex', | ||
token, | ||
i: regexMatch.index, | ||
j: regexMatch.index + regexMatch[0].length - 1, | ||
regexName: name, | ||
regexMatch | ||
}); | ||
} | ||
} | ||
@@ -33,0 +36,0 @@ }); |
@@ -39,3 +39,3 @@ import { extend, sorted } from '../../helper.esm.js'; | ||
let j = i + 1; | ||
let lastDirection = 0; | ||
let lastDirection = null; | ||
let turns = 0; | ||
@@ -42,0 +42,0 @@ shiftedCount = this.checkIfShifted(graphName, password, i); |
@@ -41,3 +41,3 @@ 'use strict'; | ||
let j = i + 1; | ||
let lastDirection = 0; | ||
let lastDirection = null; | ||
let turns = 0; | ||
@@ -44,0 +44,0 @@ shiftedCount = this.checkIfShifted(graphName, password, i); |
@@ -11,2 +11,3 @@ import { MIN_SUBMATCH_GUESSES_SINGLE_CHAR, MIN_SUBMATCH_GUESSES_MULTI_CHAR } from '../data/const.esm.js'; | ||
import spatialMatcher from '../matcher/spatial/scoring.esm.js'; | ||
import separatorMatcher from '../matcher/separator/scoring.esm.js'; | ||
@@ -31,3 +32,4 @@ const getMinGuesses = (match, password) => { | ||
sequence: sequenceMatcher, | ||
spatial: spatialMatcher | ||
spatial: spatialMatcher, | ||
separator: separatorMatcher | ||
}; | ||
@@ -34,0 +36,0 @@ const getScoring = (name, match) => { |
@@ -13,2 +13,3 @@ 'use strict'; | ||
var scoring$6 = require('../matcher/spatial/scoring.js'); | ||
var scoring$7 = require('../matcher/separator/scoring.js'); | ||
@@ -33,3 +34,4 @@ const getMinGuesses = (match, password) => { | ||
sequence: scoring$5, | ||
spatial: scoring$6 | ||
spatial: scoring$6, | ||
separator: scoring$7 | ||
}; | ||
@@ -36,0 +38,0 @@ const getScoring = (name, match) => { |
{ | ||
"name": "@zxcvbn-ts/core", | ||
"version": "3.0.2", | ||
"version": "3.0.3", | ||
"main": "dist/index.js", | ||
@@ -40,3 +40,3 @@ "module": "dist/index.esm.js", | ||
], | ||
"gitHead": "ee53a5361aedf35717fd8b10f8868db432e66686" | ||
"gitHead": "ce3aed62ab3e95ebf89cec0ece737eb8579149a9" | ||
} |
@@ -9,9 +9,22 @@ import { zxcvbnOptions } from '../../../../Options' | ||
const getExtras = (passwordWithSubs: PasswordWithSubs, i: number, j: number) => { | ||
const getExtras = ( | ||
passwordWithSubs: PasswordWithSubs, | ||
i: number, | ||
j: number, | ||
) => { | ||
const previousChanges = passwordWithSubs.changes.filter((changes) => { | ||
return changes.i < i | ||
}) | ||
const iUnsubbed = previousChanges.reduce((value, change) => { | ||
return value - change.letter.length + change.substitution.length | ||
}, i) | ||
const usedChanges = passwordWithSubs.changes.filter((changes) => { | ||
return changes.i >= i && changes.i <= j | ||
}) | ||
const jUnsubbed = usedChanges.reduce((value, change) => { | ||
return value - change.letter.length + change.substitution.length | ||
}, j) | ||
const jUnsubbed = usedChanges.reduce( | ||
(value, change) => { | ||
return value - change.letter.length + change.substitution.length | ||
}, | ||
j - i + iUnsubbed, | ||
) | ||
const filtered: PasswordChanges[] = [] | ||
@@ -32,2 +45,3 @@ const subDisplay: string[] = [] | ||
return { | ||
i: iUnsubbed, | ||
j: jUnsubbed, | ||
@@ -83,3 +97,3 @@ subs: filtered, | ||
const extras = getExtras(subbedPassword, match.i, match.j) | ||
const token = password.slice(match.i, +extras.j + 1 || 9e9) | ||
const token = password.slice(extras.i, +extras.j + 1 || 9e9) | ||
const newMatch: L33tMatch = { | ||
@@ -86,0 +100,0 @@ ...match, |
@@ -15,3 +15,3 @@ import TrieNode from './TrieNode' | ||
export type IndexedPasswordChanges = PasswordChanges & {i: number} | ||
export type IndexedPasswordChanges = PasswordChanges & { i: number } | ||
@@ -23,25 +23,62 @@ export interface PasswordWithSubs { | ||
const getAllSubCombosHelper = ({ | ||
substr, | ||
buffer, | ||
limit, | ||
trieRoot, | ||
}: GetAllSubCombosHelperOptions): PasswordWithSubs[] => { | ||
const finalPasswords: PasswordWithSubs[] = [] | ||
interface HelperOptions { | ||
onlyFullSub: boolean | ||
isFullSub: boolean | ||
index: number | ||
subIndex: number | ||
changes: IndexedPasswordChanges[] | ||
} | ||
// eslint-disable-next-line max-statements | ||
const helper = ( | ||
onlyFullSub: boolean, | ||
isFullSub: boolean, | ||
index: number, | ||
subIndex: number, | ||
changes: IndexedPasswordChanges[], | ||
): void => { | ||
if (finalPasswords.length >= limit) { | ||
class CleanPasswords { | ||
private substr: string | ||
private buffer: string[] | ||
private limit: number | ||
private trieRoot: TrieNode | ||
private finalPasswords: PasswordWithSubs[] = [] | ||
constructor({ | ||
substr, | ||
buffer, | ||
limit, | ||
trieRoot, | ||
}: GetAllSubCombosHelperOptions) { | ||
this.substr = substr | ||
this.buffer = buffer | ||
this.limit = limit | ||
this.trieRoot = trieRoot | ||
} | ||
private getAllPossibleSubsAtIndex(index: number) { | ||
const nodes: TrieNode[] = [] | ||
let cur = this.trieRoot | ||
for (let i = index; i < this.substr.length; i += 1) { | ||
const character = this.substr.charAt(i) | ||
cur = cur.getChild(character)! | ||
if (!cur) { | ||
break | ||
} | ||
nodes.push(cur) | ||
} | ||
return nodes | ||
} | ||
// eslint-disable-next-line complexity,max-statements | ||
private helper({ | ||
onlyFullSub, | ||
isFullSub, | ||
index, | ||
subIndex, | ||
changes, | ||
}: HelperOptions): void { | ||
if (this.finalPasswords.length >= this.limit) { | ||
return | ||
} | ||
if (index === substr.length) { | ||
if (index === this.substr.length) { | ||
if (onlyFullSub === isFullSub) { | ||
finalPasswords.push({ password: buffer.join(''), changes }) | ||
this.finalPasswords.push({ password: this.buffer.join(''), changes }) | ||
} | ||
@@ -52,16 +89,8 @@ return | ||
// first, exhaust all possible substitutions at this index | ||
const nodes: TrieNode[] = [] | ||
let cur = trieRoot | ||
for (let i = index; i < substr.length; i += 1) { | ||
const character = substr.charAt(i) | ||
cur = cur.getChild(character)! | ||
if (!cur) { | ||
break | ||
} | ||
nodes.push(cur) | ||
} | ||
const nodes: TrieNode[] = [...this.getAllPossibleSubsAtIndex(index)] | ||
let hasSubs = false | ||
// iterate backward to get wider substitutions first | ||
for (let i = index + nodes.length - 1; i >= index; i -= 1) { | ||
cur = nodes[i - index] | ||
const cur = nodes[i - index] | ||
if (cur.isTerminal()) { | ||
@@ -72,3 +101,3 @@ hasSubs = true | ||
for (const sub of subs) { | ||
buffer.push(sub) | ||
this.buffer.push(sub) | ||
const newSubs = changes.concat({ | ||
@@ -81,6 +110,12 @@ i: subIndex, | ||
// recursively build the rest of the string | ||
helper(onlyFullSub, isFullSub, i + 1, subIndex + sub.length, newSubs) | ||
this.helper({ | ||
onlyFullSub, | ||
isFullSub, | ||
index: i + 1, | ||
subIndex: subIndex + sub.length, | ||
changes: newSubs, | ||
}) | ||
// backtrack by ignoring the added postfix | ||
buffer.pop() | ||
if (finalPasswords.length >= limit) { | ||
this.buffer.pop() | ||
if (this.finalPasswords.length >= this.limit) { | ||
return | ||
@@ -94,15 +129,35 @@ } | ||
if (!onlyFullSub || !hasSubs) { | ||
const firstChar = substr.charAt(index) | ||
buffer.push(firstChar) | ||
helper(onlyFullSub, isFullSub && !hasSubs, index + 1, subIndex + 1, changes) | ||
buffer.pop() | ||
const firstChar = this.substr.charAt(index) | ||
this.buffer.push(firstChar) | ||
this.helper({ | ||
onlyFullSub, | ||
isFullSub: isFullSub && !hasSubs, | ||
index: index + 1, | ||
subIndex: subIndex + 1, | ||
changes, | ||
}) | ||
this.buffer.pop() | ||
} | ||
} | ||
// only full substitution | ||
helper(true, true, 0, 0, []) | ||
// only partial substitution | ||
helper(false, true, 0, 0, []) | ||
getAll() { | ||
// only full substitution | ||
this.helper({ | ||
onlyFullSub: true, | ||
isFullSub: true, | ||
index: 0, | ||
subIndex: 0, | ||
changes: [], | ||
}) | ||
// only partial substitution | ||
this.helper({ | ||
onlyFullSub: false, | ||
isFullSub: true, | ||
index: 0, | ||
subIndex: 0, | ||
changes: [], | ||
}) | ||
return finalPasswords | ||
return this.finalPasswords | ||
} | ||
} | ||
@@ -115,3 +170,3 @@ | ||
): PasswordWithSubs[] => { | ||
return getAllSubCombosHelper({ | ||
const helper = new CleanPasswords({ | ||
substr: string, | ||
@@ -122,3 +177,5 @@ buffer: [], | ||
}) | ||
return helper.getAll() | ||
} | ||
export default getCleanPasswords |
@@ -15,10 +15,20 @@ import utils from '../../../../scoring/utils' | ||
const countSubstring = (string: string, substring: string) => { | ||
let count = 0 | ||
let pos = string.indexOf(substring) | ||
while (pos >= 0) { | ||
count += 1 | ||
pos = string.indexOf(substring, pos + substring.length) | ||
} | ||
return count | ||
} | ||
const getCounts = ({ sub, token }: GetCountsOptions) => { | ||
// lower-case match.token before calculating: capitalization shouldn't affect l33t calc. | ||
const chrs = token.toLowerCase().split('') | ||
const tokenLower = token.toLowerCase() | ||
// num of subbed chars | ||
const subbedCount = chrs.filter((char) => char === sub.substitution).length | ||
const subbedCount = countSubstring(tokenLower, sub.substitution) | ||
// num of unsubbed chars | ||
const unsubbedCount = chrs.filter((char) => char === sub.letter).length | ||
const unsubbedCount = countSubstring(tokenLower, sub.letter) | ||
return { | ||
@@ -25,0 +35,0 @@ subbedCount, |
@@ -22,13 +22,17 @@ import { REGEXEN } from '../../data/const' | ||
regex.lastIndex = 0 // keeps regexMatch stateless | ||
const regexMatch = regex.exec(password) | ||
if (regexMatch) { | ||
const token = regexMatch[0] | ||
matches.push({ | ||
pattern: 'regex', | ||
token, | ||
i: regexMatch.index, | ||
j: regexMatch.index + regexMatch[0].length - 1, | ||
regexName: name as RegexesKeys, | ||
regexMatch, | ||
}) | ||
let regexMatch: RegExpExecArray | null | ||
// eslint-disable-next-line no-cond-assign | ||
while ((regexMatch = regex.exec(password))) { | ||
if (regexMatch) { | ||
const token = regexMatch[0] | ||
matches.push({ | ||
pattern: 'regex', | ||
token, | ||
i: regexMatch.index, | ||
j: regexMatch.index + regexMatch[0].length - 1, | ||
regexName: name as RegexesKeys, | ||
regexMatch, | ||
}) | ||
} | ||
} | ||
@@ -35,0 +39,0 @@ }) |
@@ -44,3 +44,3 @@ import { sorted, extend } from '../../helper' | ||
let j = i + 1 | ||
let lastDirection = 0 | ||
let lastDirection = null | ||
let turns = 0 | ||
@@ -47,0 +47,0 @@ shiftedCount = this.checkIfShifted(graphName, password, i) |
@@ -20,2 +20,3 @@ import { | ||
import spatialMatcher from '../matcher/spatial/scoring' | ||
import separatorMatcher from '../matcher/separator/scoring' | ||
@@ -49,2 +50,3 @@ const getMinGuesses = ( | ||
spatial: spatialMatcher, | ||
separator: separatorMatcher, | ||
} | ||
@@ -51,0 +53,0 @@ |
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
654318
279
10937