strong_password 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 90a81a17ad95741f57771cb8708cdf93f570ee20910d55b86c0c6c4104694fe0
|
4
|
+
data.tar.gz: 34f7e8e5e38eca56283e260895f5d0d54711e201344ba891861af7e53852b7ba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d5212716ea15246134da0ade5cf70497a78bf4ecf29198254cdc2ce2199717fb9af759212c0d327492a51a0d00e213ff0afc00c7fc3955fedfb5b875a8a6608e
|
7
|
+
data.tar.gz: 64e6df41029e605517e80a37f821c778964a6509a25bc5f975a3ac605b111d4e04ed229dab27cdc5789a9b88e431865ba848a0a67652a15621b56485feeb94e0
|
data/CHANGELOG
CHANGED
@@ -4,7 +4,7 @@ module ActiveModel
|
|
4
4
|
module Validations
|
5
5
|
class PasswordStrengthValidator < ActiveModel::EachValidator
|
6
6
|
def validate_each(object, attribute, value)
|
7
|
-
ps = ::StrongPassword::StrengthChecker.new(strength_options(options, object))
|
7
|
+
ps = ::StrongPassword::StrengthChecker.new(**strength_options(options, object))
|
8
8
|
unless ps.is_strong?(value.to_s)
|
9
9
|
object.errors.add(attribute, :'password.password_strength', options.merge(:value => value.to_s))
|
10
10
|
end
|
@@ -1,25 +1,25 @@
|
|
1
1
|
module StrongPassword
|
2
2
|
class QwertyAdjuster
|
3
3
|
QWERTY_STRINGS = [
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
'1234567890-',
|
5
|
+
'qwertyuiop',
|
6
|
+
'asdfghjkl;',
|
7
|
+
'zxcvbnm,./',
|
8
8
|
"1qaz2wsx3edc4rfv5tgb6yhn7ujm8ik,9ol.0p;/-['=]:?_{\"+}",
|
9
|
-
|
9
|
+
'1qaz2wsx3edc4rfv5tgb6yhn7ujm8ik9ol0p',
|
10
10
|
"qazwsxedcrfvtgbyhnujmik,ol.p;/-['=]:?_{\"+}",
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
]
|
11
|
+
'qazwsxedcrfvtgbyhnujmikolp',
|
12
|
+
']"/=[;.-pl,0okm9ijn8uhb7ygv6tfc5rdx4esz3wa2q1',
|
13
|
+
'pl0okm9ijn8uhb7ygv6tfc5rdx4esz3wa2q1',
|
14
|
+
']"/[;.pl,okmijnuhbygvtfcrdxeszwaq',
|
15
|
+
'plokmijnuhbygvtfcrdxeszwaq',
|
16
|
+
'014725836914702583697894561230258/369*+-*/',
|
17
|
+
'abcdefghijklmnopqrstuvwxyz'
|
18
|
+
].freeze
|
19
19
|
|
20
20
|
attr_reader :min_entropy, :entropy_threshhold
|
21
21
|
|
22
|
-
def initialize(min_entropy: 18, entropy_threshhold:
|
22
|
+
def initialize(min_entropy: 18, entropy_threshhold: min_entropy)
|
23
23
|
@min_entropy = min_entropy
|
24
24
|
@entropy_threshhold = entropy_threshhold
|
25
25
|
end
|
@@ -37,23 +37,21 @@ module StrongPassword
|
|
37
37
|
# early to avoid unnecessary processing.
|
38
38
|
def adjusted_entropy(base_password)
|
39
39
|
revpassword = base_password.reverse
|
40
|
-
|
40
|
+
lowest_entropy = [EntropyCalculator.calculate(base_password), EntropyCalculator.calculate(revpassword)].min
|
41
|
+
# If our entropy is already lower than we care about then there's no reason to look further.
|
42
|
+
return lowest_entropy if lowest_entropy < entropy_threshhold
|
43
|
+
|
41
44
|
qpassword = mask_qwerty_strings(base_password)
|
45
|
+
lowest_entropy = [lowest_entropy, EntropyCalculator.calculate(qpassword)].min if qpassword != base_password
|
46
|
+
# Bail early if our entropy on the base password's masked qwerty value is less than our threshold.
|
47
|
+
return lowest_entropy if lowest_entropy < entropy_threshhold
|
48
|
+
|
42
49
|
qrevpassword = mask_qwerty_strings(revpassword)
|
43
|
-
if
|
44
|
-
|
45
|
-
min_entropy = [min_entropy, numbits].min
|
46
|
-
return min_entropy if min_entropy < entropy_threshhold
|
47
|
-
end
|
48
|
-
if qrevpassword != revpassword
|
49
|
-
numbits = EntropyCalculator.calculate(qrevpassword)
|
50
|
-
min_entropy = [min_entropy, numbits].min
|
51
|
-
return min_entropy if min_entropy < entropy_threshhold
|
52
|
-
end
|
53
|
-
min_entropy
|
50
|
+
lowest_entropy = [lowest_entropy, EntropyCalculator.calculate(qrevpassword)].min if qrevpassword != revpassword
|
51
|
+
lowest_entropy
|
54
52
|
end
|
55
53
|
|
56
|
-
|
54
|
+
private
|
57
55
|
|
58
56
|
def all_qwerty_strings
|
59
57
|
@all_qwerty_strings ||= Regexp.union(QWERTY_STRINGS.flat_map do |qwerty_string|
|
@@ -2,31 +2,29 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module StrongPassword
|
4
4
|
describe QwertyAdjuster do
|
5
|
-
|
6
|
-
let(:subject) { QwertyAdjuster.new }
|
5
|
+
subject(:qwerty_adjuster) { QwertyAdjuster.new(entropy_threshhold: 0) }
|
7
6
|
|
7
|
+
describe '#is_strong?' do
|
8
8
|
it 'returns true if the calculated entropy is >= the minimum' do
|
9
9
|
allow(subject).to receive_messages(adjusted_entropy: 18)
|
10
|
-
expect(subject.is_strong?(
|
10
|
+
expect(subject.is_strong?('password')).to be_truthy
|
11
11
|
end
|
12
12
|
|
13
13
|
it 'returns false if the calculated entropy is < the minimum' do
|
14
14
|
allow(subject).to receive_messages(adjusted_entropy: 17)
|
15
|
-
expect(subject.is_strong?(
|
15
|
+
expect(subject.is_strong?('password')).to be_falsey
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
19
|
describe '#is_weak?' do
|
20
|
-
let(:subject) { QwertyAdjuster.new }
|
21
|
-
|
22
20
|
it 'returns the opposite of is_strong?' do
|
23
21
|
allow(subject).to receive_messages(is_strong?: true)
|
24
|
-
expect(subject.is_weak?(
|
22
|
+
expect(subject.is_weak?('password')).to be_falsey
|
25
23
|
end
|
26
24
|
end
|
27
25
|
|
28
26
|
describe '#adjusted_entropy' do
|
29
|
-
before(:each) { allow(NistBonusBits).to receive_messages(bonus_bits: 0)}
|
27
|
+
before(:each) { allow(NistBonusBits).to receive_messages(bonus_bits: 0) }
|
30
28
|
{
|
31
29
|
'qwertyuio' => 5.5,
|
32
30
|
'1234567' => 6,
|
@@ -37,7 +35,19 @@ module StrongPassword
|
|
37
35
|
'password' => 17.5 # Ensure that we don't qwerty-adjust 'password'
|
38
36
|
}.each do |password, bits|
|
39
37
|
it "returns #{bits} for '#{password}'" do
|
40
|
-
expect(
|
38
|
+
expect(subject.adjusted_entropy(password)).to eq(bits)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe 'with a default entropy threshhold' do
|
43
|
+
subject(:qwerty_adjuster) { QwertyAdjuster.new }
|
44
|
+
|
45
|
+
it 'returns the higher entropy before running qwerty adjustments' do
|
46
|
+
# Default threshhold is equal to the default min_entropy for a strong password (18).
|
47
|
+
# When not set we should get the base password's entropy from this (16) instead of the
|
48
|
+
# lower qwerty-adjusted entropy (6) indicating we avoided doing additional work in the
|
49
|
+
# qwerty adjustment code since we already know the password is weak.
|
50
|
+
expect(subject.adjusted_entropy('1234567')).to eq(16)
|
41
51
|
end
|
42
52
|
end
|
43
53
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: strong_password
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian McManus
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-07-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -119,8 +119,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
119
119
|
- !ruby/object:Gem::Version
|
120
120
|
version: '0'
|
121
121
|
requirements: []
|
122
|
-
|
123
|
-
rubygems_version: 2.7.6
|
122
|
+
rubygems_version: 3.0.3
|
124
123
|
signing_key:
|
125
124
|
specification_version: 4
|
126
125
|
summary: StrongPassword adds a class to check password strength and a validator for
|