match method
Should return the matches for the password
.
A synchronous matcher should return a list (usually of length 1) of lists of matches. An asynchronous matcher can return a list of futures that completes with a list of matches.
Implementation
@override
List<List<L33tMatch>> match(String password) {
final List<L33tMatch> matches = <L33tMatch>[];
final List<PasswordWithChanges> cleanedPasswords = cleanPassword(
password,
options.l33tMaxSubstitutions,
options.trieNodeRoot,
);
bool hasFullMatch = false;
bool isFullSubstitution = true;
for (final PasswordWithChanges cleanedPassword in cleanedPasswords) {
if (hasFullMatch) break;
final List<DictionaryMatch> dictionaryMatches = dictionaryMatcher.match(
cleanedPassword.password,
useLevenshtein: isFullSubstitution,
)[0];
// Only the first entry has a full substitution.
isFullSubstitution = false;
for (final DictionaryMatch match in dictionaryMatches) {
if (!hasFullMatch) {
hasFullMatch = match.start == 0 && match.end == password.length;
}
final Iterable<IndexedPasswordChange> previousChanges =
cleanedPassword.changes.where((IndexedPasswordChange changes) {
return changes.start < match.start;
});
final int i = previousChanges.fold(match.start,
(int val, IndexedPasswordChange change) {
return val - change.clean.length + change.l33t.length;
});
final Iterable<IndexedPasswordChange> usedChanges =
cleanedPassword.changes.where((IndexedPasswordChange changes) {
return changes.start >= match.start && changes.start < match.end;
});
final int j = usedChanges.fold(match.end - match.start + i,
(int val, IndexedPasswordChange change) {
return val - change.clean.length + change.l33t.length;
});
final String token = password.substring(i, j);
final Set<String> seen = <String>{};
// Filter duplicates.
final List<PasswordChange> changes = <PasswordChange>[
for (final IndexedPasswordChange change in usedChanges)
if (seen.add('${change.l33t} -> ${change.clean}'))
PasswordChange(
l33t: change.l33t,
clean: change.clean,
),
];
final L33tMatch newMatch = match.toL33tMatch(
password: password,
start: i,
end: j,
changes: changes,
changesDisplay: changes.join(', '),
);
// Ignore single-character l33t matches to reduce noise.
// Otherwise '1' matches 'i', '4' matches 'a', both very common
// English words with low dictionary rank.
// Only return the matches that contain an actual substitution.
if (token.length > 1 &&
token.toLowerCase() != match.matchedWord &&
!matches.any(newMatch.isDuplicateOf)) {
matches.add(newMatch);
}
}
}
return <List<L33tMatch>>[matches];
}