zxcvbn-ruby 0.1.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +4 -6
- data/CHANGELOG.md +19 -0
- data/README.md +71 -8
- data/lib/zxcvbn/feedback.rb +10 -0
- data/lib/zxcvbn/feedback_giver.rb +133 -0
- data/lib/zxcvbn/password_strength.rb +2 -0
- data/lib/zxcvbn/score.rb +1 -1
- data/lib/zxcvbn/version.rb +1 -1
- data/spec/feedback_giver_spec.rb +212 -0
- data/spec/tester_spec.rb +5 -0
- data/zxcvbn-ruby.gemspec +3 -0
- metadata +12 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3766aaa59eeb07a17e377051ad39091f2c96d52a76a5851476b7b163f81fdb10
|
4
|
+
data.tar.gz: d4b054399683992fca7845b4e175d372dd98a2e8bba7338841938529a7b80853
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1860d4d74a4bc1f578c888fb9bd7f825ea05b36bcf5770a238be2630b1b928176908477650228522a30a5dc423ee25b45012bb091f72d1eafb951a7c0fef4657
|
7
|
+
data.tar.gz: 6453adfe0fc5c05a2a84ea319df97bd46a41ff0aed7ac875d19637e7aace42f8cc81df992fe3f454ed30e5c00d84113e9ceeecea2972336f562a18c39a81cb38
|
data/.travis.yml
CHANGED
@@ -2,14 +2,12 @@ before_install:
|
|
2
2
|
- gem install bundler
|
3
3
|
language: ruby
|
4
4
|
rvm:
|
5
|
-
- '
|
6
|
-
- '2.
|
7
|
-
- '2.
|
8
|
-
- '2.
|
9
|
-
- '2.3.0'
|
5
|
+
- '2.3'
|
6
|
+
- '2.4'
|
7
|
+
- '2.5'
|
8
|
+
- '2.6'
|
10
9
|
- ruby-head
|
11
10
|
matrix:
|
12
11
|
allow_failures:
|
13
12
|
- rvm: ruby-head
|
14
13
|
fast_finish: true
|
15
|
-
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Changelog
|
2
|
+
All notable changes to this project will be documented in this file.
|
3
|
+
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
5
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
|
+
|
7
|
+
## [Unreleased]
|
8
|
+
|
9
|
+
## [1.0.0] - 2019-05-14
|
10
|
+
### Added
|
11
|
+
Adds more ported password checking features to bring this gem more up to date.
|
12
|
+
spatial - Keyboard patterns
|
13
|
+
repeat - Repeated characters
|
14
|
+
sequence - easily guessable sequences
|
15
|
+
date - date associations
|
16
|
+
[PR for further details](https://github.com/envato/zxcvbn-ruby/pull/22)
|
17
|
+
|
18
|
+
### Removed
|
19
|
+
- This gem will no longer run on Ruby versions < 2.3
|
data/README.md
CHANGED
@@ -18,10 +18,41 @@ Example usage:
|
|
18
18
|
$ irb
|
19
19
|
>> require 'zxcvbn'
|
20
20
|
=> true
|
21
|
-
>> Zxcvbn.test('@lfred2004', ['alfred'])
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
>> pp Zxcvbn.test('@lfred2004', ['alfred'])
|
22
|
+
#<Zxcvbn::Score:0x00007f7f590610c8
|
23
|
+
@calc_time=0.0055760000250302255,
|
24
|
+
@crack_time=0.012,
|
25
|
+
@crack_time_display="instant",
|
26
|
+
@entropy=7.895,
|
27
|
+
@feedback=
|
28
|
+
#<Zxcvbn::Feedback:0x00007f7f59060150
|
29
|
+
@suggestions=
|
30
|
+
["Add another word or two. Uncommon words are better.",
|
31
|
+
"Predictable substitutions like '@' instead of 'a' don't help very much"],
|
32
|
+
@warning=nil>,
|
33
|
+
@match_sequence=
|
34
|
+
[#<Zxcvbn::Match matched_word="alfred", token="@lfred", i=0, j=5, rank=1, pattern="dictionary", dictionary_name="user_inputs", l33t=true, sub={"@"=>"a"}, sub_display="@ -> a", base_entropy=0.0, uppercase_entropy=0.0, l33t_entropy=1, entropy=1.0>,
|
35
|
+
#<Zxcvbn::Match i=6, j=9, token="2004", pattern="year", entropy=6.894817763307944>],
|
36
|
+
@password="@lfred2004",
|
37
|
+
@score=0>
|
38
|
+
=> #<Zxcvbn::Score:0x00007f7f59060150>
|
39
|
+
>> pp Zxcvbn.test('asdfghju7654rewq', ['alfred'])
|
40
|
+
#<Zxcvbn::Score:0x00007f7f5a9e9248
|
41
|
+
@calc_time=0.007504999986849725,
|
42
|
+
@crack_time=46159.451,
|
43
|
+
@crack_time_display="14 hours",
|
44
|
+
@entropy=29.782,
|
45
|
+
@feedback=
|
46
|
+
#<Zxcvbn::Feedback:0x00007f7f5a9e9130
|
47
|
+
@suggestions=
|
48
|
+
["Add another word or two. Uncommon words are better.",
|
49
|
+
"Use a longer keyboard pattern with more turns"],
|
50
|
+
@warning="Short keyboard patterns are easy to guess">,
|
51
|
+
@match_sequence=
|
52
|
+
[#<Zxcvbn::Match pattern="spatial", i=0, j=15, token="asdfghju7654rewq", graph="qwerty", turns=5, shifted_count=0, entropy=29.7820508329166>],
|
53
|
+
@password="asdfghju7654rewq",
|
54
|
+
@score=2>
|
55
|
+
=> #<Zxcvbn::Score:0x00007f7f5a9e9248>
|
25
56
|
```
|
26
57
|
|
27
58
|
## Testing Multiple Passwords
|
@@ -34,10 +65,42 @@ $ irb
|
|
34
65
|
=> true
|
35
66
|
>> tester = Zxcvbn::Tester.new
|
36
67
|
=> #<Zxcvbn::Tester:0x3fe99d869aa4>
|
37
|
-
>> tester.test('@lfred2004', ['alfred'])
|
38
|
-
|
39
|
-
|
40
|
-
|
68
|
+
>> pp tester.test('@lfred2004', ['alfred'])
|
69
|
+
#<Zxcvbn::Score:0x00007f7f586fcf50
|
70
|
+
@calc_time=0.00631899997824803,
|
71
|
+
@crack_time=0.012,
|
72
|
+
@crack_time_display="instant",
|
73
|
+
@entropy=7.895,
|
74
|
+
@feedback=
|
75
|
+
#<Zxcvbn::Feedback:0x00007f7f586fcac8
|
76
|
+
@suggestions=
|
77
|
+
["Add another word or two. Uncommon words are better.",
|
78
|
+
"Predictable substitutions like '@' instead of 'a' don't help very much"],
|
79
|
+
@warning=nil>,
|
80
|
+
@match_sequence=
|
81
|
+
[#<Zxcvbn::Match matched_word="alfred", token="@lfred", i=0, j=5, rank=1, pattern="dictionary", dictionary_name="user_inputs", l33t=true, sub={"@"=>"a"}, sub_display="@ -> a", base_entropy=0.0, uppercase_entropy=0.0, l33t_entropy=1, entropy=1.0>,
|
82
|
+
#<Zxcvbn::Match i=6, j=9, token="2004", pattern="year", entropy=6.894817763307944>],
|
83
|
+
@password="@lfred2004",
|
84
|
+
@score=0>
|
85
|
+
=> #<Zxcvbn::Score:0x00007f7f586fcf50>
|
86
|
+
>> pp tester.test('@lfred2004', ['alfred'])
|
87
|
+
#<Zxcvbn::Score:0x00007f7f56d57438
|
88
|
+
@calc_time=0.001986999996006489,
|
89
|
+
@crack_time=0.012,
|
90
|
+
@crack_time_display="instant",
|
91
|
+
@entropy=7.895,
|
92
|
+
@feedback=
|
93
|
+
#<Zxcvbn::Feedback:0x00007f7f56d56bf0
|
94
|
+
@suggestions=
|
95
|
+
["Add another word or two. Uncommon words are better.",
|
96
|
+
"Predictable substitutions like '@' instead of 'a' don't help very much"],
|
97
|
+
@warning=nil>,
|
98
|
+
@match_sequence=
|
99
|
+
[#<Zxcvbn::Match matched_word="alfred", token="@lfred", i=0, j=5, rank=1, pattern="dictionary", dictionary_name="user_inputs", l33t=true, sub={"@"=>"a"}, sub_display="@ -> a", base_entropy=0.0, uppercase_entropy=0.0, l33t_entropy=1, entropy=1.0>,
|
100
|
+
#<Zxcvbn::Match i=6, j=9, token="2004", pattern="year", entropy=6.894817763307944>],
|
101
|
+
@password="@lfred2004",
|
102
|
+
@score=0>
|
103
|
+
=> #<Zxcvbn::Score:0x00007f7f56d57438>
|
41
104
|
```
|
42
105
|
|
43
106
|
**Note**: Storing the entropy of an encrypted or hashed value provides
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'zxcvbn/entropy'
|
2
|
+
require 'zxcvbn/feedback'
|
3
|
+
|
4
|
+
module Zxcvbn
|
5
|
+
class FeedbackGiver
|
6
|
+
NAME_DICTIONARIES = %w[surnames male_names female_names].freeze
|
7
|
+
|
8
|
+
DEFAULT_FEEDBACK = Feedback.new(
|
9
|
+
suggestions: [
|
10
|
+
'Use a few words, avoid common phrases',
|
11
|
+
'No need for symbols, digits, or uppercase letters'
|
12
|
+
]
|
13
|
+
).freeze
|
14
|
+
|
15
|
+
EMPTY_FEEDBACK = Feedback.new.freeze
|
16
|
+
|
17
|
+
def self.get_feedback(score, sequence)
|
18
|
+
# starting feedback
|
19
|
+
return DEFAULT_FEEDBACK if sequence.length.zero?
|
20
|
+
|
21
|
+
# no feedback if score is good or great.
|
22
|
+
return EMPTY_FEEDBACK if score > 2
|
23
|
+
|
24
|
+
# tie feedback to the longest match for longer sequences
|
25
|
+
longest_match = sequence[0]
|
26
|
+
for match in sequence[1..-1]
|
27
|
+
longest_match = match if match.token.length > longest_match.token.length
|
28
|
+
end
|
29
|
+
|
30
|
+
feedback = get_match_feedback(longest_match, sequence.length == 1)
|
31
|
+
extra_feedback = 'Add another word or two. Uncommon words are better.'
|
32
|
+
|
33
|
+
if feedback.nil?
|
34
|
+
feedback = Feedback.new(suggestions: [extra_feedback])
|
35
|
+
else
|
36
|
+
feedback.suggestions.unshift extra_feedback
|
37
|
+
end
|
38
|
+
|
39
|
+
feedback
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.get_match_feedback(match, is_sole_match)
|
43
|
+
case match.pattern
|
44
|
+
when 'dictionary'
|
45
|
+
get_dictionary_match_feedback match, is_sole_match
|
46
|
+
|
47
|
+
when 'spatial'
|
48
|
+
layout = match.graph.upcase
|
49
|
+
warning = if match.turns == 1
|
50
|
+
'Straight rows of keys are easy to guess'
|
51
|
+
else
|
52
|
+
'Short keyboard patterns are easy to guess'
|
53
|
+
end
|
54
|
+
|
55
|
+
Feedback.new(
|
56
|
+
warning: warning,
|
57
|
+
suggestions: [
|
58
|
+
'Use a longer keyboard pattern with more turns'
|
59
|
+
]
|
60
|
+
)
|
61
|
+
|
62
|
+
when 'repeat'
|
63
|
+
Feedback.new(
|
64
|
+
warning: 'Repeats like "aaa" are easy to guess',
|
65
|
+
suggestions: [
|
66
|
+
'Avoid repeated words and characters'
|
67
|
+
]
|
68
|
+
)
|
69
|
+
|
70
|
+
when 'sequence'
|
71
|
+
Feedback.new(
|
72
|
+
warning: 'Sequences like abc or 6543 are easy to guess',
|
73
|
+
suggestions: [
|
74
|
+
'Avoid sequences'
|
75
|
+
]
|
76
|
+
)
|
77
|
+
|
78
|
+
when 'date'
|
79
|
+
Feedback.new(
|
80
|
+
warning: 'Dates are often easy to guess',
|
81
|
+
suggestions: [
|
82
|
+
'Avoid dates and years that are associated with you'
|
83
|
+
]
|
84
|
+
)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.get_dictionary_match_feedback(match, is_sole_match)
|
89
|
+
warning = if match.dictionary_name == 'passwords'
|
90
|
+
if is_sole_match && !match.l33t && !match.reversed
|
91
|
+
if match.rank <= 10
|
92
|
+
'This is a top-10 common password'
|
93
|
+
elsif match.rank <= 100
|
94
|
+
'This is a top-100 common password'
|
95
|
+
else
|
96
|
+
'This is a very common password'
|
97
|
+
end
|
98
|
+
else
|
99
|
+
'This is similar to a commonly used password'
|
100
|
+
end
|
101
|
+
elsif NAME_DICTIONARIES.include? match.dictionary_name
|
102
|
+
if is_sole_match
|
103
|
+
'Names and surnames by themselves are easy to guess'
|
104
|
+
else
|
105
|
+
'Common names and surnames are easy to guess'
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
suggestions = []
|
110
|
+
word = match.token
|
111
|
+
|
112
|
+
if word =~ Zxcvbn::Entropy::START_UPPER
|
113
|
+
suggestions.push "Capitalization doesn't help very much"
|
114
|
+
elsif word =~ Zxcvbn::Entropy::ALL_UPPER && word.downcase != word
|
115
|
+
suggestions.push(
|
116
|
+
'All-uppercase is almost as easy to guess as all-lowercase'
|
117
|
+
)
|
118
|
+
end
|
119
|
+
|
120
|
+
if match.l33t
|
121
|
+
suggestions.push(
|
122
|
+
"Predictable substitutions like '@' instead of 'a' \
|
123
|
+
don't help very much"
|
124
|
+
)
|
125
|
+
end
|
126
|
+
|
127
|
+
Feedback.new(
|
128
|
+
warning: warning,
|
129
|
+
suggestions: suggestions
|
130
|
+
)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'benchmark'
|
2
|
+
require 'zxcvbn/feedback_giver'
|
2
3
|
require 'zxcvbn/omnimatch'
|
3
4
|
require 'zxcvbn/scorer'
|
4
5
|
|
@@ -17,6 +18,7 @@ module Zxcvbn
|
|
17
18
|
result = @scorer.minimum_entropy_match_sequence(password, matches)
|
18
19
|
end
|
19
20
|
result.calc_time = calc_time
|
21
|
+
result.feedback = FeedbackGiver.get_feedback(result.score, result.match_sequence)
|
20
22
|
result
|
21
23
|
end
|
22
24
|
end
|
data/lib/zxcvbn/score.rb
CHANGED
data/lib/zxcvbn/version.rb
CHANGED
@@ -0,0 +1,212 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Zxcvbn::FeedbackGiver do
|
4
|
+
# NOTE: We go in via the tester because the `FeedbackGiver` relies on both
|
5
|
+
# Omnimatch and the Scorer, which are troublesome to wire up for tests
|
6
|
+
let(:tester) { Zxcvbn::Tester.new }
|
7
|
+
|
8
|
+
describe '.get_feedback' do
|
9
|
+
it "gives empty feedback when a password's score is good" do
|
10
|
+
feedback = tester.test('5815A30BE798').feedback
|
11
|
+
|
12
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
13
|
+
expect(feedback.warning).to be_nil
|
14
|
+
expect(feedback.suggestions).to be_empty
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'gives general feedback when a password is empty' do
|
18
|
+
feedback = tester.test('').feedback
|
19
|
+
|
20
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
21
|
+
expect(feedback.warning).to be_nil
|
22
|
+
expect(feedback.suggestions).to contain_exactly(
|
23
|
+
'Use a few words, avoid common phrases',
|
24
|
+
'No need for symbols, digits, or uppercase letters'
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "gives general feedback when a password is poor but doesn't match any heuristics" do
|
29
|
+
feedback = tester.test(':005:0').feedback
|
30
|
+
|
31
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
32
|
+
expect(feedback.warning).to be_nil
|
33
|
+
expect(feedback.suggestions).to contain_exactly(
|
34
|
+
'Add another word or two. Uncommon words are better.'
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'gives specific feedback' do
|
39
|
+
describe 'for dictionary passwords' do
|
40
|
+
it 'that are extremely common' do
|
41
|
+
feedback = tester.test('password').feedback
|
42
|
+
|
43
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
44
|
+
expect(feedback.warning).to eql('This is a top-10 common password')
|
45
|
+
expect(feedback.suggestions).to contain_exactly(
|
46
|
+
'Add another word or two. Uncommon words are better.'
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'that are very, very common' do
|
51
|
+
feedback = tester.test('letmein').feedback
|
52
|
+
|
53
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
54
|
+
expect(feedback.warning).to eql('This is a top-100 common password')
|
55
|
+
expect(feedback.suggestions).to contain_exactly(
|
56
|
+
'Add another word or two. Uncommon words are better.'
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'that are very common' do
|
61
|
+
feedback = tester.test('playstation').feedback
|
62
|
+
|
63
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
64
|
+
expect(feedback.warning).to eql('This is a very common password')
|
65
|
+
expect(feedback.suggestions).to contain_exactly(
|
66
|
+
'Add another word or two. Uncommon words are better.'
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'that are common and you tried to use l33tsp33k' do
|
71
|
+
feedback = tester.test('pl4yst4ti0n').feedback
|
72
|
+
|
73
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
74
|
+
expect(feedback.warning).to eql(
|
75
|
+
'This is similar to a commonly used password'
|
76
|
+
)
|
77
|
+
expect(feedback.suggestions).to contain_exactly(
|
78
|
+
'Add another word or two. Uncommon words are better.',
|
79
|
+
"Predictable substitutions like '@' instead of 'a' don't help very much"
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'that are common and you capitalised the start' do
|
84
|
+
feedback = tester.test('Password').feedback
|
85
|
+
|
86
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
87
|
+
expect(feedback.warning).to eql(
|
88
|
+
'This is a top-10 common password'
|
89
|
+
)
|
90
|
+
expect(feedback.suggestions).to contain_exactly(
|
91
|
+
'Add another word or two. Uncommon words are better.',
|
92
|
+
"Capitalization doesn't help very much"
|
93
|
+
)
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'that are common and you capitalised the whole thing' do
|
97
|
+
feedback = tester.test('PASSWORD').feedback
|
98
|
+
|
99
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
100
|
+
expect(feedback.warning).to eql(
|
101
|
+
'This is a top-10 common password'
|
102
|
+
)
|
103
|
+
expect(feedback.suggestions).to contain_exactly(
|
104
|
+
'Add another word or two. Uncommon words are better.',
|
105
|
+
'All-uppercase is almost as easy to guess as all-lowercase'
|
106
|
+
)
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'that contain a common first name or last name' do
|
110
|
+
feedback = tester.test('jessica').feedback
|
111
|
+
|
112
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
113
|
+
expect(feedback.warning).to eql(
|
114
|
+
'Names and surnames by themselves are easy to guess'
|
115
|
+
)
|
116
|
+
expect(feedback.suggestions).to contain_exactly(
|
117
|
+
'Add another word or two. Uncommon words are better.'
|
118
|
+
)
|
119
|
+
|
120
|
+
feedback = tester.test('smith').feedback
|
121
|
+
|
122
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
123
|
+
expect(feedback.warning).to eql(
|
124
|
+
'Names and surnames by themselves are easy to guess'
|
125
|
+
)
|
126
|
+
expect(feedback.suggestions).to contain_exactly(
|
127
|
+
'Add another word or two. Uncommon words are better.'
|
128
|
+
)
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'that contain a common name and surname' do
|
132
|
+
feedback = tester.test('jessica smith').feedback
|
133
|
+
|
134
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
135
|
+
expect(feedback.warning).to eql(
|
136
|
+
'Common names and surnames are easy to guess'
|
137
|
+
)
|
138
|
+
expect(feedback.suggestions).to contain_exactly(
|
139
|
+
'Add another word or two. Uncommon words are better.'
|
140
|
+
)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe 'for spatial passwords' do
|
145
|
+
it 'that contain a straight keyboard row' do
|
146
|
+
feedback = tester.test('1qaz').feedback
|
147
|
+
|
148
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
149
|
+
expect(feedback.warning).to eql(
|
150
|
+
'Straight rows of keys are easy to guess'
|
151
|
+
)
|
152
|
+
expect(feedback.suggestions).to contain_exactly(
|
153
|
+
'Add another word or two. Uncommon words are better.',
|
154
|
+
'Use a longer keyboard pattern with more turns'
|
155
|
+
)
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'that contain a keyboard pattern with one turn' do
|
159
|
+
feedback = tester.test('zaqwer').feedback
|
160
|
+
|
161
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
162
|
+
expect(feedback.warning).to eql(
|
163
|
+
'Short keyboard patterns are easy to guess'
|
164
|
+
)
|
165
|
+
expect(feedback.suggestions).to contain_exactly(
|
166
|
+
'Add another word or two. Uncommon words are better.',
|
167
|
+
'Use a longer keyboard pattern with more turns'
|
168
|
+
)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'for passwords with repeated characters' do
|
173
|
+
feedback = tester.test('zzz').feedback
|
174
|
+
|
175
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
176
|
+
expect(feedback.warning).to eql(
|
177
|
+
'Repeats like "aaa" are easy to guess'
|
178
|
+
)
|
179
|
+
expect(feedback.suggestions).to contain_exactly(
|
180
|
+
'Add another word or two. Uncommon words are better.',
|
181
|
+
'Avoid repeated words and characters'
|
182
|
+
)
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'for passwords with sequential characters' do
|
186
|
+
feedback = tester.test('pqrpqrpqr').feedback
|
187
|
+
|
188
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
189
|
+
expect(feedback.warning).to eql(
|
190
|
+
'Sequences like abc or 6543 are easy to guess'
|
191
|
+
)
|
192
|
+
expect(feedback.suggestions).to contain_exactly(
|
193
|
+
'Add another word or two. Uncommon words are better.',
|
194
|
+
'Avoid sequences'
|
195
|
+
)
|
196
|
+
end
|
197
|
+
|
198
|
+
it 'for passwords containing dates' do
|
199
|
+
feedback = tester.test('testing02\12\1997').feedback
|
200
|
+
|
201
|
+
expect(feedback).to be_a Zxcvbn::Feedback
|
202
|
+
expect(feedback.warning).to eql(
|
203
|
+
'Dates are often easy to guess'
|
204
|
+
)
|
205
|
+
expect(feedback.suggestions).to contain_exactly(
|
206
|
+
'Add another word or two. Uncommon words are better.',
|
207
|
+
'Avoid dates and years that are associated with you'
|
208
|
+
)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
data/spec/tester_spec.rb
CHANGED
@@ -16,6 +16,11 @@ describe Zxcvbn::Tester do
|
|
16
16
|
expect(ruby_result.score).to eq js_result['score']
|
17
17
|
expect(ruby_result.pattern).to eq js_result['pattern']
|
18
18
|
expect(ruby_result.match_sequence.count).to eq js_result['match_sequence'].count
|
19
|
+
|
20
|
+
# NOTE: feedback didn't exist in the version of the JS library this gem
|
21
|
+
# is based on, so instead we just check that it put `Feedback` in
|
22
|
+
# there. Real tests for its values go in `feedback_giver_spec.rb`.
|
23
|
+
expect(ruby_result.feedback).to be_a Zxcvbn::Feedback
|
19
24
|
end
|
20
25
|
end
|
21
26
|
|
data/zxcvbn-ruby.gemspec
CHANGED
@@ -14,6 +14,9 @@ Gem::Specification.new do |gem|
|
|
14
14
|
gem.name = "zxcvbn-ruby"
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
gem.version = Zxcvbn::VERSION
|
17
|
+
gem.license = 'MIT'
|
18
|
+
|
19
|
+
gem.required_ruby_version = '~> 2.3'
|
17
20
|
|
18
21
|
gem.add_development_dependency 'therubyracer'
|
19
22
|
gem.add_development_dependency 'rspec'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zxcvbn-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steve Hodgkiss
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2019-05-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: therubyracer
|
@@ -50,6 +50,7 @@ files:
|
|
50
50
|
- ".gitignore"
|
51
51
|
- ".rspec"
|
52
52
|
- ".travis.yml"
|
53
|
+
- CHANGELOG.md
|
53
54
|
- Gemfile
|
54
55
|
- LICENSE
|
55
56
|
- README.md
|
@@ -65,6 +66,8 @@ files:
|
|
65
66
|
- lib/zxcvbn/data.rb
|
66
67
|
- lib/zxcvbn/dictionary_ranker.rb
|
67
68
|
- lib/zxcvbn/entropy.rb
|
69
|
+
- lib/zxcvbn/feedback.rb
|
70
|
+
- lib/zxcvbn/feedback_giver.rb
|
68
71
|
- lib/zxcvbn/match.rb
|
69
72
|
- lib/zxcvbn/matchers/date.rb
|
70
73
|
- lib/zxcvbn/matchers/dictionary.rb
|
@@ -84,6 +87,7 @@ files:
|
|
84
87
|
- lib/zxcvbn/tester.rb
|
85
88
|
- lib/zxcvbn/version.rb
|
86
89
|
- spec/dictionary_ranker_spec.rb
|
90
|
+
- spec/feedback_giver_spec.rb
|
87
91
|
- spec/matchers/date_spec.rb
|
88
92
|
- spec/matchers/dictionary_spec.rb
|
89
93
|
- spec/matchers/digits_spec.rb
|
@@ -113,7 +117,8 @@ files:
|
|
113
117
|
- spec/zxcvbn_spec.rb
|
114
118
|
- zxcvbn-ruby.gemspec
|
115
119
|
homepage: http://github.com/envato/zxcvbn-ruby
|
116
|
-
licenses:
|
120
|
+
licenses:
|
121
|
+
- MIT
|
117
122
|
metadata: {}
|
118
123
|
post_install_message:
|
119
124
|
rdoc_options: []
|
@@ -121,22 +126,22 @@ require_paths:
|
|
121
126
|
- lib
|
122
127
|
required_ruby_version: !ruby/object:Gem::Requirement
|
123
128
|
requirements:
|
124
|
-
- - "
|
129
|
+
- - "~>"
|
125
130
|
- !ruby/object:Gem::Version
|
126
|
-
version: '
|
131
|
+
version: '2.3'
|
127
132
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
133
|
requirements:
|
129
134
|
- - ">="
|
130
135
|
- !ruby/object:Gem::Version
|
131
136
|
version: '0'
|
132
137
|
requirements: []
|
133
|
-
|
134
|
-
rubygems_version: 2.6.11
|
138
|
+
rubygems_version: 3.0.3
|
135
139
|
signing_key:
|
136
140
|
specification_version: 4
|
137
141
|
summary: ''
|
138
142
|
test_files:
|
139
143
|
- spec/dictionary_ranker_spec.rb
|
144
|
+
- spec/feedback_giver_spec.rb
|
140
145
|
- spec/matchers/date_spec.rb
|
141
146
|
- spec/matchers/dictionary_spec.rb
|
142
147
|
- spec/matchers/digits_spec.rb
|