15 "Use a few words, avoid common phrases",
16 "No need for symbols, digits, or uppercase letters",
21 optional::optional<Feedback> get_match_feedback(
const Match & match,
bool is_sole_match);
24 Feedback get_dictionary_match_feedback(
const Match & match,
bool is_sole_match);
27 const std::vector<Match> & sequence) {
32 if (score > 2)
return {
"", {}};
35 auto longest_match = sequence.begin();
36 for (
auto match = longest_match + 1; match != sequence.end(); ++match) {
37 if (match->token.length() > longest_match->token.length()) {
38 longest_match = match;
42 auto maybe_feedback = get_match_feedback(*longest_match, sequence.size() == 1);
43 auto extra_feedback =
"Add another word or two. Uncommon words are better.";
45 auto & feedback = *maybe_feedback;
47 feedback.suggestions.insert(maybe_feedback->suggestions.begin(),
53 return {
"", {extra_feedback}};
57 optional::optional<Feedback> get_match_feedback(
const Match & match_,
bool is_sole_match) {
58 switch (match_.get_pattern()) {
59 case MatchPattern::DICTIONARY: {
60 return get_dictionary_match_feedback(match_, is_sole_match);
63 case MatchPattern::SPATIAL: {
64 auto & match = match_.get_spatial();
65 auto warning = (match.turns == 1)
66 ?
"Straight rows of keys are easy to guess"
67 :
"Short keyboard patterns are easy to guess";
69 return Feedback{warning, {
70 "Use a longer keyboard pattern with more turns",
74 case MatchPattern::REPEAT: {
75 auto warning = (match_.get_repeat().base_token.length() == 1)
76 ?
"Repeats like \"aaa\" are easy to guess"
77 :
"Repeats like \"abcabcabc\" are only slightly harder to guess than \"abc\"";
79 return Feedback{warning, {
80 "Avoid repeated words and characters",
84 case MatchPattern::SEQUENCE: {
85 return Feedback{
"Sequences like abc or 6543 are easy to guess",
90 case MatchPattern::REGEX: {
91 auto & match = match_.get_regex();
93 return Feedback{
"Recent years are easy to guess", {
95 "Avoid years that are associated with you",
101 case MatchPattern::DATE: {
102 return Feedback{
"Dates are often easy to guess", {
103 "Avoid dates and years that are associated with you",
113 Feedback get_dictionary_match_feedback(
const Match & match_,
bool is_sole_match) {
114 assert(match_.get_pattern() == MatchPattern::DICTIONARY);
115 auto & match = match_.get_dictionary();
117 if (match.dictionary_tag == DictionaryTag::PASSWORDS) {
118 if (is_sole_match and !match.l33t and !match.reversed) {
119 if (match.rank <= 10) {
120 return "This is a top-10 common password";
122 else if (match.rank <= 100) {
123 return "This is a top-100 common password";
126 return "This is a very common password";
129 else if (match_.guesses_log10 <= 4) {
130 return "This is similar to a commonly used password";
133 else if (match.dictionary_tag == DictionaryTag::ENGLISH_WIKIPEDIA) {
135 return "A word by itself is easy to guess";
138 else if (match.dictionary_tag == DictionaryTag::SURNAMES ||
139 match.dictionary_tag == DictionaryTag::MALE_NAMES ||
140 match.dictionary_tag == DictionaryTag::FEMALE_NAMES) {
142 return "Names and surnames by themselves are easy to guess";
145 return "Common names and surnames are easy to guess";
152 std::vector<std::string> suggestions;
153 auto & word = match_.token;
155 suggestions.push_back(
"Capitalization doesn't help very much");
157 else if (std::regex_search(word,
ALL_UPPER) and
160 suggestions.push_back(
"All-uppercase is almost as easy to guess as all-lowercase");
163 if (match.reversed and match_.token.length() >= 4) {
164 suggestions.push_back(
"Reversed words aren't much harder to guess");
167 suggestions.push_back(
"Predictable substitutions like '@' instead of 'a' don't help very much");
170 return {warning, suggestions};