-
Notifications
You must be signed in to change notification settings - Fork 954
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Custom feedback messages #124
Changes from all commits
360b1c0
685206a
0137ce6
5cd5183
2f35bdd
607c2c0
ace4b01
37d11e1
5fc8ac9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,120 +1,144 @@ | ||
scoring = require('./scoring') | ||
|
||
feedback = | ||
default_feedback: | ||
warning: '' | ||
suggestions: [ | ||
"Use a few words, avoid common phrases" | ||
"No need for symbols, digits, or uppercase letters" | ||
] | ||
|
||
get_feedback: (score, sequence) -> | ||
messages: | ||
use_a_few_words: 'Use a few words, avoid common phrases' | ||
no_need_for_mixed_chars: 'No need for symbols, digits, or uppercase letters' | ||
uncommon_words_are_better: 'Add another word or two. Uncommon words are better.' | ||
straight_rows_of_keys_are_easy: 'Straight rows of keys are easy to guess' | ||
short_keyboard_patterns_are_easy: 'Short keyboard patterns are easy to guess' | ||
use_longer_keyboard_patterns: 'Use a longer keyboard pattern with more turns' | ||
repeated_chars_are_easy: 'Repeats like "aaa" are easy to guess' | ||
repeated_patterns_are_easy: 'Repeats like "abcabcabc" are only slightly harder to guess than "abc"' | ||
avoid_repeated_chars: 'Avoid repeated words and characters' | ||
sequences_are_easy: 'Sequences like abc or 6543 are easy to guess' | ||
avoid_sequences: 'Avoid sequences' | ||
recent_years_are_easy: 'Recent years are easy to guess' | ||
avoid_recent_years: 'Avoid recent years' | ||
avoid_associated_years: 'Avoid years that are associated with you' | ||
dates_are_easy: 'Dates are often easy to guess' | ||
avoid_associated_dates_and_years: 'Avoid dates and years that are associated with you' | ||
top10_common_password: 'This is a top-10 common password' | ||
top100_common_password: 'This is a top-100 common password' | ||
very_common_password: 'This is a very common password' | ||
similar_to_common_password: 'This is similar to a commonly used password' | ||
a_word_is_easy: 'A word by itself is easy to guess' | ||
names_are_easy: 'Names and surnames by themselves are easy to guess' | ||
common_names_are_easy: 'Common names and surnames are easy to guess' | ||
capitalization_doesnt_help: 'Capitalization doesn\'t help very much' | ||
all_uppercase_doesnt_help: 'All-uppercase is almost as easy to guess as all-lowercase' | ||
reverse_doesnt_help: 'Reversed words aren\'t much harder to guess' | ||
substitution_doesnt_help: 'Predictable substitutions like \'@\' instead of \'a\' don\'t help very much' | ||
|
||
get_feedback: (score, sequence, custom_messages) -> | ||
@custom_messages = custom_messages | ||
|
||
# starting feedback | ||
return @default_feedback if sequence.length == 0 | ||
return if sequence.length == 0 | ||
@build_feedback(null, ['use_a_few_words', 'no_need_for_mixed_chars']) | ||
|
||
# no feedback if score is good or great. | ||
return if score > 2 | ||
warning: '' | ||
suggestions: [] | ||
@build_feedback() | ||
|
||
# tie feedback to the longest match for longer sequences | ||
longest_match = sequence[0] | ||
for match in sequence[1..] | ||
longest_match = match if match.token.length > longest_match.token.length | ||
feedback = @get_match_feedback(longest_match, sequence.length == 1) | ||
extra_feedback = 'Add another word or two. Uncommon words are better.' | ||
extra_feedback = ['uncommon_words_are_better'] | ||
if feedback? | ||
feedback.suggestions.unshift extra_feedback | ||
feedback.warning = '' unless feedback.warning? | ||
@build_feedback(feedback.warning, extra_feedback.concat feedback.suggestions) | ||
else | ||
feedback = | ||
warning: '' | ||
suggestions: [extra_feedback] | ||
feedback | ||
@build_feedback(null, extra_feedback) | ||
|
||
get_match_feedback: (match, is_sole_match) -> | ||
switch match.pattern | ||
when 'dictionary' | ||
@get_dictionary_match_feedback match, is_sole_match | ||
|
||
when 'spatial' | ||
layout = match.graph.toUpperCase() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was this used somewhere? |
||
warning = if match.turns == 1 | ||
'Straight rows of keys are easy to guess' | ||
'straight_rows_of_keys_are_easy' | ||
else | ||
'Short keyboard patterns are easy to guess' | ||
'short_keyboard_patterns_are_easy' | ||
warning: warning | ||
suggestions: [ | ||
'Use a longer keyboard pattern with more turns' | ||
] | ||
suggestions: ['use_longer_keyboard_patterns'] | ||
|
||
when 'repeat' | ||
warning = if match.base_token.length == 1 | ||
'Repeats like "aaa" are easy to guess' | ||
'repeated_chars_are_easy' | ||
else | ||
'Repeats like "abcabcabc" are only slightly harder to guess than "abc"' | ||
'repeated_patterns_are_easy' | ||
warning: warning | ||
suggestions: [ | ||
'Avoid repeated words and characters' | ||
] | ||
suggestions: ['avoid_repeated_chars'] | ||
|
||
when 'sequence' | ||
warning: "Sequences like abc or 6543 are easy to guess" | ||
suggestions: [ | ||
'Avoid sequences' | ||
] | ||
warning: 'sequences_are_easy' | ||
suggestions: ['avoid_sequences'] | ||
|
||
when 'regex' | ||
if match.regex_name == 'recent_year' | ||
warning: "Recent years are easy to guess" | ||
suggestions: [ | ||
'Avoid recent years' | ||
'Avoid years that are associated with you' | ||
] | ||
warning: 'recent_years_are_easy' | ||
suggestions: ['avoid_recent_years', 'avoid_associated_years'] | ||
|
||
when 'date' | ||
warning: "Dates are often easy to guess" | ||
suggestions: [ | ||
'Avoid dates and years that are associated with you' | ||
] | ||
warning: 'dates_are_easy' | ||
suggestions: ['avoid_associated_dates_and_years'] | ||
|
||
get_dictionary_match_feedback: (match, is_sole_match) -> | ||
warning = if match.dictionary_name == 'passwords' | ||
if is_sole_match and not match.l33t and not match.reversed | ||
if match.rank <= 10 | ||
'This is a top-10 common password' | ||
'top10_common_password' | ||
else if match.rank <= 100 | ||
'This is a top-100 common password' | ||
'top100_common_password' | ||
else | ||
'This is a very common password' | ||
'very_common_password' | ||
else if match.guesses_log10 <= 4 | ||
'This is similar to a commonly used password' | ||
'similar_to_common_password' | ||
else if match.dictionary_name == 'english_wikipedia' | ||
if is_sole_match | ||
'A word by itself is easy to guess' | ||
'a_word_is_easy' | ||
else if match.dictionary_name in ['surnames', 'male_names', 'female_names'] | ||
if is_sole_match | ||
'Names and surnames by themselves are easy to guess' | ||
'names_are_easy' | ||
else | ||
'Common names and surnames are easy to guess' | ||
else | ||
'' | ||
'common_names_are_easy' | ||
|
||
suggestions = [] | ||
word = match.token | ||
if word.match(scoring.START_UPPER) | ||
suggestions.push "Capitalization doesn't help very much" | ||
suggestions.push 'capitalization_doesnt_help' | ||
else if word.match(scoring.ALL_UPPER) and word.toLowerCase() != word | ||
suggestions.push "All-uppercase is almost as easy to guess as all-lowercase" | ||
suggestions.push 'all_uppercase_doesnt_help' | ||
|
||
if match.reversed and match.token.length >= 4 | ||
suggestions.push "Reversed words aren't much harder to guess" | ||
suggestions.push 'reverse_doesnt_help' | ||
if match.l33t | ||
suggestions.push "Predictable substitutions like '@' instead of 'a' don't help very much" | ||
suggestions.push 'substitution_doesnt_help' | ||
|
||
result = | ||
warning: warning | ||
suggestions: suggestions | ||
result | ||
|
||
get_message: (key) -> | ||
if @custom_messages? and key of @custom_messages | ||
@custom_messages[key] or '' | ||
else if @messages[key]? | ||
@messages[key] | ||
else | ||
throw new Error("unknown message: #{key}") | ||
|
||
build_feedback: (warning_key = null, suggestion_keys = []) -> | ||
suggestions = [] | ||
for suggestion_key in suggestion_keys | ||
message = @get_message(suggestion_key) | ||
suggestions.push message if message? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add message to suggestions only if not empty: Array of suggestions with empty string like |
||
feedback = | ||
warning: if warning_key then @get_message(warning_key) else '' | ||
suggestions: suggestions | ||
feedback | ||
|
||
module.exports = feedback |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,15 @@ feedback = require './feedback' | |
|
||
time = -> (new Date()).getTime() | ||
|
||
zxcvbn = (password, user_inputs = []) -> | ||
zxcvbn = (password, options = {}) -> | ||
if options instanceof Array | ||
user_inputs = options # backward-compatibility | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
else if typeof options == 'object' | ||
{ user_inputs = [], feedback_messages = {}} = options | ||
else | ||
user_inputs = [] | ||
feedback_messages = {} | ||
|
||
start = time() | ||
# reset the user inputs matcher on a per-request basis to keep things stateless | ||
sanitized_inputs = [] | ||
|
@@ -19,7 +27,7 @@ zxcvbn = (password, user_inputs = []) -> | |
attack_times = time_estimates.estimate_attack_times result.guesses | ||
for prop, val of attack_times | ||
result[prop] = val | ||
result.feedback = feedback.get_feedback result.score, result.sequence | ||
result.feedback = feedback.get_feedback result.score, result.sequence, feedback_messages | ||
result | ||
|
||
module.exports = zxcvbn |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should probably be
userInputs
andfeedbackMessages
respectively... this is JavaScript.