• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nulab / zxcvbn4j / #141

pending completion
#141

push

github-actions

web-flow
Merge pull request #135 from manchilop/master

Added feedback messages translated into Spanish

1392 of 1507 relevant lines covered (92.37%)

0.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.63
/src/main/java/com/nulabinc/zxcvbn/Feedback.java
1
package com.nulabinc.zxcvbn;
2

3
import com.nulabinc.zxcvbn.guesses.DictionaryGuess;
4
import com.nulabinc.zxcvbn.matchers.Match;
5

6
import java.util.ArrayList;
7
import java.util.Arrays;
8
import java.util.List;
9
import java.util.Locale;
10
import java.util.Map;
11
import java.util.MissingResourceException;
12
import java.util.ResourceBundle;
13

14
public class Feedback {
15

16
    private static final String DEFAULT_BUNDLE_NAME = "com/nulabinc/zxcvbn/messages";
17

18
    private static final ResourceBundle.Control CONTROL =
1✔
19
            ResourceBundle.Control.getNoFallbackControl(
1✔
20
                    ResourceBundle.Control.FORMAT_DEFAULT);
21

22
    public static final String DEFAULT_SUGGESTIONS_USE_FEW_WORDS = "feedback.default.suggestions.useFewWords";
23
    public static final String DEFAULT_SUGGESTIONS_NO_NEED_SYMBOLS = "feedback.default.suggestions.noNeedSymbols";
24
    public static final String EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD = "feedback.extra.suggestions.addAnotherWord";
25
    public static final String SPATIAL_WARNING_STRAIGHT_ROWS_OF_KEYS = "feedback.spatial.warning.straightRowsOfKeys";
26
    public static final String SPATIAL_WARNING_SHORT_KEYBOARD_PATTERNS = "feedback.spatial.warning.shortKeyboardPatterns";
27
    public static final String SPATIAL_SUGGESTIONS_USE_LONGER_KEYBOARD_PATTERN = "feedback.spatial.suggestions.UseLongerKeyboardPattern";
28
    public static final String REPEAT_WARNING_LIKE_AAA = "feedback.repeat.warning.likeAAA";
29
    public static final String REPEAT_WARNING_LIKE_ABCABCABC = "feedback.repeat.warning.likeABCABCABC";
30
    public static final String REPEAT_SUGGESTIONS_AVOID_REPEATED_WORDS = "feedback.repeat.suggestions.avoidRepeatedWords";
31
    public static final String SEQUENCE_WARNING_LIKE_ABCOR6543 = "feedback.sequence.warning.likeABCor6543";
32
    public static final String SEQUENCE_SUGGESTIONS_AVOID_SEQUENCES = "feedback.sequence.suggestions.avoidSequences";
33
    public static final String REGEX_WARNING_RECENT_YEARS = "feedback.regex.warning.recentYears";
34
    public static final String REGEX_SUGGESTIONS_AVOID_RECENT_YEARS = "feedback.regex.suggestions.avoidRecentYears";
35
    public static final String DATE_WARNING_DATES = "feedback.date.warning.dates";
36
    public static final String DATE_SUGGESTIONS_AVOID_DATES = "feedback.date.suggestions.avoidDates";
37
    public static final String DICTIONARY_WARNING_PASSWORDS_TOP10 = "feedback.dictionary.warning.passwords.top10";
38
    public static final String DICTIONARY_WARNING_PASSWORDS_TOP100 = "feedback.dictionary.warning.passwords.top100";
39
    public static final String DICTIONARY_WARNING_PASSWORDS_VERY_COMMON = "feedback.dictionary.warning.passwords.veryCommon";
40
    public static final String DICTIONARY_WARNING_PASSWORDS_SIMILAR = "feedback.dictionary.warning.passwords.similar";
41
    public static final String DICTIONARY_WARNING_ENGLISH_WIKIPEDIA_ITSELF = "feedback.dictionary.warning.englishWikipedia.itself";
42
    public static final String DICTIONARY_WARNING_ETC_NAMES_THEMSELVES = "feedback.dictionary.warning.etc.namesThemselves";
43
    public static final String DICTIONARY_WARNING_ETC_NAMES_COMMON = "feedback.dictionary.warning.etc.namesCommon";
44
    public static final String DICTIONARY_SUGGESTIONS_CAPITALIZATION = "feedback.dictionary.suggestions.capitalization";
45
    public static final String DICTIONARY_SUGGESTIONS_ALL_UPPERCASE = "feedback.dictionary.suggestions.allUppercase";
46
    public static final String DICTIONARY_SUGGESTIONS_REVERSED = "feedback.dictionary.suggestions.reversed";
47
    public static final String DICTIONARY_SUGGESTIONS_L33T = "feedback.dictionary.suggestions.l33t";
48

49
    final private String warning;
50
    final private String[] suggestions;
51

52
    private Feedback(String warning, String... suggestions) {
1✔
53
        this.warning = warning;
1✔
54
        this.suggestions = suggestions;
1✔
55
    }
1✔
56

57
    public String getWarning() {
58
        return getWarning(Locale.getDefault());
1✔
59
    }
60

61
    public String getWarning(Locale locale) {
62
        if (this.warning == null) {
1✔
63
            return "";
1✔
64
        }
65
        ResourceBundle messages = resolveResourceBundle(locale);
1✔
66
        return l10n(messages, this.warning);
1✔
67
    }
68

69
    public List<String> getSuggestions() {
70
        return getSuggestions(Locale.getDefault());
1✔
71
    }
72

73
    public List<String> getSuggestions(Locale locale) {
74
        List<String> suggestionTexts = new ArrayList<>(this.suggestions.length);
1✔
75
        ResourceBundle messages = resolveResourceBundle(locale);
1✔
76
        for (String suggestion : this.suggestions) {
1✔
77
            suggestionTexts.add(l10n(messages, suggestion));
1✔
78
        }
79
        return suggestionTexts;
1✔
80
    }
81

82
    protected ResourceBundle resolveResourceBundle(Locale locale) {
83
        try {
84
            return ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale, CONTROL);
1✔
85
        } catch (MissingResourceException e) {
×
86
            // Fix for issue of Android refs: https://github.com/nulab/zxcvbn4j/issues/21
87
            return ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale);
×
88
        } catch (UnsupportedOperationException e) {
×
89
            // Fix for issue of JDK 9 refs: https://github.com/nulab/zxcvbn4j/issues/45
90
            // ResourceBundle.Control is not supported in named modules.
91
            // See https://docs.oracle.com/javase/9/docs/api/java/util/ResourceBundle.html#bundleprovider for more details
92
            return ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale);
×
93
        }
94
    }
95

96
    public Feedback withResourceBundle(ResourceBundle messages) {
97
        return new ResourceBundleFeedback(messages, warning, suggestions);
1✔
98
    }
99

100
    public Feedback replaceResourceBundle(Map<Locale, ResourceBundle> messages) {
101
        return new ReplacedMessagesFeedback(messages, warning, suggestions);
1✔
102
    }
103

104
    private String l10n(ResourceBundle messages, String messageId) {
105
        return messages != null ? messages.getString(messageId) : messageId;
1✔
106
    }
107

108
    static Feedback getFeedback(int score, List<Match> sequence) {
109
        if (sequence.size() == 0) {
1✔
110
            return getFeedbackWithoutWarnings(
1✔
111
                    DEFAULT_SUGGESTIONS_USE_FEW_WORDS,
112
                    DEFAULT_SUGGESTIONS_NO_NEED_SYMBOLS);
113
        }
114
        if (score > 2) {
1✔
115
            return getEmptyFeedback();
1✔
116
        }
117
        Match longestMatch = sequence.get(0);
1✔
118
        if (sequence.size() > 1) {
1✔
119
            for (Match match : sequence.subList(1, sequence.size())) {
1✔
120
                if (match.tokenLength() > longestMatch.tokenLength()) longestMatch = match;
1✔
121
            }
1✔
122
        }
123

124
        return getMatchFeedback(longestMatch, sequence.size() == 1);
1✔
125
    }
126

127
    private static Feedback getFeedbackWithoutWarnings(String... suggestions) {
128
        return new Feedback(null, suggestions);
1✔
129
    }
130

131
    private static Feedback getEmptyFeedback() {
132
        return new Feedback(null);
1✔
133
    }
134

135
    private static Feedback getMatchFeedback(Match match, boolean isSoleMatch) {
136
        switch (match.pattern) {
1✔
137
            case Dictionary:
138
                return getDictionaryMatchFeedback(match, isSoleMatch);
1✔
139
            case Spatial:
140
                return new Feedback(match.turns == 1
1✔
141
                        ? SPATIAL_WARNING_STRAIGHT_ROWS_OF_KEYS
1✔
142
                        : SPATIAL_WARNING_SHORT_KEYBOARD_PATTERNS,
1✔
143
                        EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD,
144
                        SPATIAL_SUGGESTIONS_USE_LONGER_KEYBOARD_PATTERN
145
                );
146
            case Repeat:
147
                return new Feedback(match.baseToken.length() == 1
1✔
148
                        ? REPEAT_WARNING_LIKE_AAA
1✔
149
                        : REPEAT_WARNING_LIKE_ABCABCABC,
1✔
150
                        EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD,
151
                        REPEAT_SUGGESTIONS_AVOID_REPEATED_WORDS
152
                );
153
            case Sequence:
154
                return new Feedback(SEQUENCE_WARNING_LIKE_ABCOR6543,
1✔
155
                        EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD,
156
                        SEQUENCE_SUGGESTIONS_AVOID_SEQUENCES
157
                );
158
            case Regex:
159
                return new Feedback("recent_year".equals(match.regexName)
1✔
160
                        ? REGEX_WARNING_RECENT_YEARS
1✔
161
                        : null,
1✔
162
                        EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD,
163
                        REGEX_SUGGESTIONS_AVOID_RECENT_YEARS
164
                );
165
            case Date:
166
                return new Feedback(
1✔
167
                        DATE_WARNING_DATES,
168
                        EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD,
169
                        DATE_SUGGESTIONS_AVOID_DATES
170
                );
171
            default:
172
                return getFeedbackWithoutWarnings(EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD);
1✔
173
        }
174
    }
175

176
    private static Feedback getDictionaryMatchFeedback(Match match, boolean isSoleMatch) {
177
        String warning = null;
1✔
178
        if ("passwords".equals(match.dictionaryName)) {
1✔
179
            if (isSoleMatch && !match.l33t && !match.reversed) {
1✔
180
                if (match.rank <= 10) {
1✔
181
                    warning = DICTIONARY_WARNING_PASSWORDS_TOP10;
1✔
182
                } else if (match.rank <= 100) {
1✔
183
                    warning = DICTIONARY_WARNING_PASSWORDS_TOP100;
1✔
184
                } else {
185
                    warning = DICTIONARY_WARNING_PASSWORDS_VERY_COMMON;
1✔
186
                }
187
            } else if (match.guessesLog10 <= 4) {
1✔
188
                warning = DICTIONARY_WARNING_PASSWORDS_SIMILAR;
1✔
189
            }
190
        } else if ("english_wikipedia".equals(match.dictionaryName)) {
1✔
191
            if (isSoleMatch) {
1✔
192
                warning = DICTIONARY_WARNING_ENGLISH_WIKIPEDIA_ITSELF;
1✔
193
            }
194
        } else if (Arrays.asList(new String[]{"surnames", "male_names", "female_names"}).contains(match.dictionaryName)) {
1✔
195
            if (isSoleMatch) {
1✔
196
                warning = DICTIONARY_WARNING_ETC_NAMES_THEMSELVES;
1✔
197
            } else {
198
                warning = DICTIONARY_WARNING_ETC_NAMES_COMMON;
1✔
199
            }
200
        }
201

202
        List<String> suggestions = new ArrayList<>();
1✔
203
        suggestions.add(EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD);
1✔
204

205
        CharSequence word = match.token;
1✔
206
        WipeableString lower = WipeableString.lowerCase(word);
1✔
207
        if (DictionaryGuess.START_UPPER.matcher(word).find()) {
1✔
208
            suggestions.add(DICTIONARY_SUGGESTIONS_CAPITALIZATION);
1✔
209
        } else if (DictionaryGuess.ALL_UPPER.matcher(word).find() && !lower.equals(word)) {
1✔
210
            suggestions.add(DICTIONARY_SUGGESTIONS_ALL_UPPERCASE);
1✔
211
        }
212
        if (match.reversed && match.tokenLength() >= 4) {
1✔
213
            suggestions.add(DICTIONARY_SUGGESTIONS_REVERSED);
1✔
214
        }
215
        if (match.l33t) {
1✔
216
            suggestions.add(DICTIONARY_SUGGESTIONS_L33T);
1✔
217
        }
218
        lower.wipe();
1✔
219
        return new Feedback(warning, suggestions.toArray(new String[suggestions.size()]));
1✔
220
    }
221

222
    private static class ResourceBundleFeedback extends Feedback {
223
        private ResourceBundle messages;
224

225
        private ResourceBundleFeedback(ResourceBundle messages, String warning, String... suggestions) {
226
            super(warning, suggestions);
1✔
227
            this.messages = messages;
1✔
228
        }
1✔
229

230
        @Override
231
        protected ResourceBundle resolveResourceBundle(Locale locale) {
232
            return messages;
1✔
233
        }
234
    }
235

236
    private static class ReplacedMessagesFeedback extends Feedback {
237
        private final Map<Locale, ResourceBundle> messages;
238

239
        private ReplacedMessagesFeedback(Map<Locale, ResourceBundle> messages, String warning, String... suggestions) {
240
            super(warning, suggestions);
1✔
241
            this.messages = messages;
1✔
242
        }
1✔
243

244
        @Override
245
        protected ResourceBundle resolveResourceBundle(Locale locale) {
246
            try {
247
                if (messages.containsKey(locale)) {
1✔
248
                    return messages.get(locale);
1✔
249
                }
250
                return ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale, CONTROL);
×
251
            } catch (MissingResourceException e) {
×
252
                return ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale);
×
253
            } catch (UnsupportedOperationException e) {
×
254
                return ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale);
×
255
            }
256
        }
257
    }
258
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc