zxcvbn-ruby 0.1.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|