strong_password 0.0.5 → 0.0.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 21042c15094f1a9a0f960b16435fee882ac7e517
4
- data.tar.gz: f4e9cf9c311aa13a7cf591b866d1f43a91c91290
2
+ SHA256:
3
+ metadata.gz: 0506c07961d8cf54ee30542579493af60a4a508c176f7de7c0131d8082bcdf35
4
+ data.tar.gz: 132ea4c4bc8a96f44b687a342f418ef9c183efb696618cf6b6a7f14a3558c590
5
5
  SHA512:
6
- metadata.gz: b5be352df9e03643ab21a4c10ba1bdc9561607776faa8974ea59563b43432cfc62a6824d86365811875672a57028c99887df4aee172618f5c0c407d2312ed974
7
- data.tar.gz: 67032a9ff38562e8893a9dbd581d55a79531a9b83165029bdbcf05fcf883ab4effd8a8021996c79167fc3d8fc1193d25bfda9b71b2e894c4a31a4a264d244806
6
+ metadata.gz: 9342ba93d158425cb8623662341f43fda16c33102590c846571767f09aa8def4a34f4d0d03eb150ea468b3f3c2de58a4cdf2f57c29367fbbc402a3e797b4f893
7
+ data.tar.gz: 5ba21b3b35527fc07fe5744dc9560e083fe0844a316c88ef22249aeacea0f77ec1b3082a6fb20b4eb6cc7f4cabb63560433023c1c438a73a28e60ec46e64b4aa
data/Gemfile CHANGED
@@ -12,4 +12,7 @@ else
12
12
  end
13
13
 
14
14
  gem "rails", rails
15
- gem "codeclimate-test-reporter", group: :test, require: nil
15
+ group :test do
16
+ gem "simplecov", "~> 0.16.1", require: false
17
+ gem "simplecov-console", "~> 0.4.2", require: false
18
+ end
data/README.md CHANGED
@@ -19,7 +19,7 @@ NOTE: StrongPassword requires the use of Ruby 2.0. Upgrade if you haven't alrea
19
19
 
20
20
  Add this line to your application's Gemfile:
21
21
 
22
- gem 'strong_password', '~> 0.0.5'
22
+ gem 'strong_password', '~> 0.0.6'
23
23
 
24
24
  And then execute:
25
25
 
@@ -40,4 +40,4 @@ module ActiveModel
40
40
  end
41
41
  end
42
42
  end
43
- end
43
+ end
@@ -997,4 +997,4 @@ module StrongPassword
997
997
  end
998
998
  end
999
999
  end
1000
- end
1000
+ end
@@ -8,7 +8,7 @@ module StrongPassword
8
8
  bits(password)
9
9
  end
10
10
  end
11
-
11
+
12
12
  # The basic NIST entropy calculation is based solely
13
13
  # on the length of the password in question.
14
14
  def self.bits(password)
@@ -24,7 +24,7 @@ module StrongPassword
24
24
  end
25
25
  bits + NistBonusBits.bonus_bits(password)
26
26
  end
27
-
27
+
28
28
  # A modified version of the basic entropy calculation
29
29
  # which lowers the amount of entropy gained for each
30
30
  # repeated character in the password
@@ -36,9 +36,9 @@ module StrongPassword
36
36
  end
37
37
  bits + NistBonusBits.bonus_bits(password)
38
38
  end
39
-
39
+
40
40
  private
41
-
41
+
42
42
  def self.bit_value_at_position(position, base = 1)
43
43
  if position > 19
44
44
  return base
@@ -50,17 +50,17 @@ module StrongPassword
50
50
  return 4
51
51
  end
52
52
  end
53
-
53
+
54
54
  class EntropyResolver
55
55
  BASE_VALUE = 1
56
56
  REPEAT_WEAKENING_FACTOR = 0.75
57
-
57
+
58
58
  attr_reader :char_multiplier
59
-
59
+
60
60
  def initialize
61
61
  @char_multiplier = {}
62
62
  end
63
-
63
+
64
64
  # Returns the current entropy value for a character and weakens the entropy
65
65
  # for future calls for the same character.
66
66
  def entropy_for(char)
@@ -1,26 +1,26 @@
1
1
  module StrongPassword
2
2
  module NistBonusBits
3
3
  @@bonus_bits_for_password = {}
4
-
4
+
5
5
  # NIST password strength rules allow up to 6 bonus bits for mixed case and non-alphabetic
6
6
  def self.bonus_bits(password)
7
7
  @@bonus_bits_for_password[password] ||= begin
8
8
  calculate_bonus_bits_for(password)
9
9
  end
10
10
  end
11
-
11
+
12
12
  # This smells bad as it's only used for testing...
13
13
  def self.reset_bonus_cache!
14
14
  @@bonus_bits_for_password = {}
15
15
  end
16
-
16
+
17
17
  def self.calculate_bonus_bits_for(password)
18
18
  upper = !!(password =~ /[[:upper:]]/)
19
19
  lower = !!(password =~ /[[:lower:]]/)
20
20
  numeric = !!(password =~ /[[:digit:]]/)
21
21
  other = !!(password =~ /[^a-zA-Z0-9 ]/)
22
22
  space = !!(password =~ / /)
23
-
23
+
24
24
  # I had this condensed to nested ternaries but that shit was ugly
25
25
  bonus_bits = if upper && lower && other && numeric
26
26
  6
@@ -42,4 +42,4 @@ module StrongPassword
42
42
  bonus_bits
43
43
  end
44
44
  end
45
- end
45
+ end
@@ -16,21 +16,21 @@ module StrongPassword
16
16
  "014725836914702583697894561230258/369*+-*/",
17
17
  "abcdefghijklmnopqrstuvwxyz"
18
18
  ]
19
-
19
+
20
20
  attr_reader :base_password
21
-
21
+
22
22
  def initialize(password)
23
23
  @base_password = password.downcase
24
24
  end
25
-
25
+
26
26
  def is_strong?(min_entropy: 18)
27
27
  adjusted_entropy(entropy_threshhold: min_entropy) >= min_entropy
28
28
  end
29
-
29
+
30
30
  def is_weak?(min_entropy: 18)
31
31
  !is_strong?(min_entropy: min_entropy)
32
32
  end
33
-
33
+
34
34
  # Returns the minimum entropy for the password's qwerty locality
35
35
  # adjustments. If a threshhold is specified we will bail
36
36
  # early to avoid unnecessary processing.
@@ -53,9 +53,9 @@ module StrongPassword
53
53
  end
54
54
  min_entropy
55
55
  end
56
-
56
+
57
57
  private
58
-
58
+
59
59
  def mask_qwerty_strings(password, qwerty_string)
60
60
  masked_password = password
61
61
  z = 6
@@ -70,4 +70,4 @@ module StrongPassword
70
70
  masked_password
71
71
  end
72
72
  end
73
- end
73
+ end
@@ -1,13 +1,15 @@
1
1
  module StrongPassword
2
2
  class StrengthChecker
3
3
  BASE_ENTROPY = 18
4
-
4
+ PASSWORD_LIMIT = 1_000
5
+ EXTRA_WORDS_LIMIT = 1_000
6
+
5
7
  attr_reader :base_password
6
8
 
7
9
  def initialize(password)
8
- @base_password = password.dup
10
+ @base_password = password.dup[0...PASSWORD_LIMIT]
9
11
  end
10
-
12
+
11
13
  def is_weak?(min_entropy: BASE_ENTROPY, use_dictionary: false, min_word_length: 4, extra_dictionary_words: [])
12
14
  !is_strong?(min_entropy: min_entropy,
13
15
  use_dictionary: use_dictionary,
@@ -27,11 +29,12 @@ module StrongPassword
27
29
  return !weak
28
30
  end
29
31
  end
30
-
32
+
31
33
  def calculate_entropy(use_dictionary: false, min_word_length: 4, extra_dictionary_words: [])
34
+ extra_dictionary_words.collect! { |w| w[0...EXTRA_WORDS_LIMIT] }
32
35
  entropies = [EntropyCalculator.calculate(base_password), EntropyCalculator.calculate(base_password.downcase), QwertyAdjuster.new(base_password).adjusted_entropy]
33
36
  entropies << DictionaryAdjuster.new(base_password).adjusted_entropy(min_word_length: min_word_length, extra_dictionary_words: extra_dictionary_words) if use_dictionary
34
37
  entropies.min
35
38
  end
36
39
  end
37
- end
40
+ end
@@ -1,3 +1,3 @@
1
1
  module StrongPassword
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.6"
3
3
  end
@@ -1,5 +1,8 @@
1
- require "codeclimate-test-reporter"
2
- CodeClimate::TestReporter.start
1
+ require 'simplecov'
2
+ require 'simplecov-console'
3
+ SimpleCov.formatter = SimpleCov::Formatter::Console
4
+ SimpleCov.start
5
+
3
6
  require 'bundler/setup'
4
7
  require 'pry'
5
8
  require 'active_model'
@@ -36,9 +36,9 @@ module StrongPassword
36
36
  end
37
37
  end
38
38
  end
39
-
39
+
40
40
  describe '.bits_with_repeats_weakened' do
41
- before(:each) { NistBonusBits.stub(bonus_bits: 0) }
41
+ before(:each) { allow(NistBonusBits).to receive(:bonus_bits).and_return(0) }
42
42
  {
43
43
  '' => 0,
44
44
  '*' => 4,
@@ -52,7 +52,7 @@ module StrongPassword
52
52
  expect(subject.bits_with_repeats_weakened(password)).to eq(bits)
53
53
  end
54
54
  end
55
-
55
+
56
56
  it 'returns the same value for repeated calls on a password' do
57
57
  password = 'password'
58
58
  initial_value = subject.bits_with_repeats_weakened(password)
@@ -8,7 +8,7 @@ module StrongPassword
8
8
  NistBonusBits.should_receive(:calculate_bonus_bits_for).and_return(1)
9
9
  expect(NistBonusBits.bonus_bits('password')).to eq(1)
10
10
  end
11
-
11
+
12
12
  it 'caches the bonus bits for a password for later use' do
13
13
  NistBonusBits.reset_bonus_cache!
14
14
  NistBonusBits.stub(calculate_bonus_bits_for: 1)
@@ -17,7 +17,7 @@ module StrongPassword
17
17
  expect(NistBonusBits.bonus_bits('password')).to eq(1)
18
18
  end
19
19
  end
20
-
20
+
21
21
  describe '.calculate_bonus_bits_for' do
22
22
  {
23
23
  'Ab$9' => 4,
@@ -6,59 +6,59 @@ module StrongPassword
6
6
  it 'includes the lowercase password' do
7
7
  expect(subject.all_variants("PASSWORD")).to include('password')
8
8
  end
9
-
9
+
10
10
  it 'includes keyboard shift variants' do
11
11
  subject.stub(keyboard_shift_variants: ['foo', 'bar'])
12
12
  expect(subject.all_variants("password")).to include('foo', 'bar')
13
13
  end
14
-
14
+
15
15
  it 'includes leet speak variants' do
16
16
  subject.stub(leet_speak_variants: ['foo', 'bar'])
17
17
  expect(subject.all_variants("password")).to include('foo', 'bar')
18
18
  end
19
-
19
+
20
20
  it 'does not mutate the password' do
21
21
  password = 'PASSWORD'
22
22
  subject.all_variants(password)
23
23
  expect(password).to eq('PASSWORD')
24
24
  end
25
25
  end
26
-
26
+
27
27
  describe '.keyboard_shift_variants' do
28
28
  it 'returns no variants if password includes only bottom row characters' do
29
29
  expect(subject.keyboard_shift_variants('zxcvbnm,./')).to eq([])
30
30
  end
31
-
31
+
32
32
  it 'maps down-right passwords' do
33
33
  expect(subject.keyboard_shift_variants('qwerty')).to include('asdfgh')
34
34
  end
35
-
35
+
36
36
  it 'includes reversed down-right password' do
37
37
  expect(subject.keyboard_shift_variants('qwerty')).to include('hgfdsa')
38
38
  end
39
-
39
+
40
40
  it 'maps down-left passwords' do
41
41
  expect(subject.keyboard_shift_variants('sdfghj')).to include('zxcvbn')
42
42
  end
43
-
43
+
44
44
  it 'maps reversed down-left passwords' do
45
45
  expect(subject.keyboard_shift_variants('sdfghj')).to include('nbvcxz')
46
46
  end
47
47
  end
48
-
48
+
49
49
  describe '.leet_speak_variants' do
50
50
  it 'returns no variants if the password includes no leet speak' do
51
51
  expect(subject.leet_speak_variants('password')).to eq([])
52
52
  end
53
-
53
+
54
54
  it 'returns standard leet speak variants' do
55
55
  expect(subject.leet_speak_variants('p4ssw0rd')).to include('password')
56
56
  end
57
-
57
+
58
58
  it 'returns reversed standard leet speak variants' do
59
59
  expect(subject.leet_speak_variants('p4ssw0rd')).to include('drowssap')
60
60
  end
61
-
61
+
62
62
  it 'returns both i and l variants when given a 1' do
63
63
  expect(subject.leet_speak_variants('h1b0b')).to include('hibob', 'hlbob')
64
64
  end
@@ -15,7 +15,7 @@ module StrongPassword
15
15
  end
16
16
  end
17
17
  end
18
-
18
+
19
19
  context 'with lowered entropy requirement and dictionary checking' do
20
20
  {
21
21
  'blahblah' => true,
@@ -30,7 +30,7 @@ module StrongPassword
30
30
  end
31
31
  end
32
32
  end
33
-
33
+
34
34
  context 'with standard entropy requirement and dictionary checking' do
35
35
  {
36
36
  'blahblah' => false,
@@ -45,7 +45,7 @@ module StrongPassword
45
45
  end
46
46
  end
47
47
  end
48
-
48
+
49
49
  context 'with crazy entropy requirement and dictionary checking' do
50
50
  {
51
51
  'blahblah' => false,
@@ -61,5 +61,25 @@ module StrongPassword
61
61
  end
62
62
  end
63
63
  end
64
+
65
+ context 'with long password' do
66
+ let(:strength_checker) { StrengthChecker.new("ba"*500_000) }
67
+ it 'should be truncated' do
68
+ expect(strength_checker.instance_variable_get(:@base_password).length).to eq StrengthChecker::PASSWORD_LIMIT
69
+ end
70
+ end
71
+
72
+ context 'with long extra words' do
73
+ let(:strength_checker) { StrengthChecker.new("$tr0NgP4s$w0rd91d£") }
74
+ let(:exta_limit) { StrengthChecker::EXTRA_WORDS_LIMIT }
75
+ it 'should be truncated' do
76
+ expect_any_instance_of(DictionaryAdjuster).to receive(:adjusted_entropy).with({
77
+ min_word_length: 4,
78
+ extra_dictionary_words:
79
+ ["a"*StrengthChecker::EXTRA_WORDS_LIMIT, "b"*StrengthChecker::EXTRA_WORDS_LIMIT, "c"*10]
80
+ }).and_call_original
81
+ strength_checker.calculate_entropy(use_dictionary: true, extra_dictionary_words: ["a"*1_000_000, "b"*10_000_000, "c"*10])
82
+ end
83
+ end
64
84
  end
65
- end
85
+ end
@@ -20,6 +20,6 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency 'bundler', '~> 1.3'
22
22
  spec.add_development_dependency 'rake'
23
- spec.add_development_dependency 'rspec', '~> 2.12'
23
+ spec.add_development_dependency 'rspec', '~> 3.8'
24
24
  spec.add_development_dependency 'pry'
25
25
  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.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian McManus
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-28 00:00:00.000000000 Z
11
+ date: 2018-10-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '2.12'
47
+ version: '3.8'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '2.12'
54
+ version: '3.8'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: pry
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -120,7 +120,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
120
120
  version: '0'
121
121
  requirements: []
122
122
  rubyforge_project:
123
- rubygems_version: 2.4.5
123
+ rubygems_version: 2.7.6
124
124
  signing_key:
125
125
  specification_version: 4
126
126
  summary: StrongPassword adds a class to check password strength and a validator for
@@ -134,4 +134,3 @@ test_files:
134
134
  - spec/strong_password/qwerty_adjuster_spec.rb
135
135
  - spec/strong_password/strength_checker_spec.rb
136
136
  - spec/validation/strength_validator_spec.rb
137
- has_rdoc: