nobspw 0.4.0 → 0.6.2
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 +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +3 -3
- data/README.md +4 -3
- data/lib/active_model/validations/password_validator.rb +1 -0
- data/lib/nobspw/configuration.rb +2 -0
- data/lib/nobspw/password_checker.rb +2 -5
- data/lib/nobspw/validation_methods.rb +42 -13
- data/lib/nobspw/version.rb +1 -1
- data/misc/grep_benchmark.rb +73 -0
- data/nobspw.gemspec +4 -2
- metadata +38 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d81c15016a1b06dc3d38fc995120b526574a65cd5fd13ed98bc05e614f189b5b
|
4
|
+
data.tar.gz: a7303590e8fa254ad9718fda4444a2c3d5faabb77cc528d5a5284e52792742be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2e8b250a5a326593bab9f576cdc7ec15da4e03573c273bacc62b74f46c4977df5e323b079aa43f613d17f458f1417b1f569e2f7dd19f190be16af5b0b68e7a13
|
7
|
+
data.tar.gz: 6ab6462e6c32b58a1f79cd01ffa0a540fab446f22db43b43c52366821a3a185f7c5a1c5b78cd938f4a5be94c2f93ba1cc094f9a456c8d957c68cfa079c7a2cba
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -60,8 +60,9 @@ Optionally, you can configure some options:
|
|
60
60
|
config.min_unique_characters = 5
|
61
61
|
config.dictionary_path = 'path/to/dictionary.txt'
|
62
62
|
config.grep_path = '/usr/bin/grep'
|
63
|
+
config.use_ruby_grep = false # Defaults to false; slower when true. Uses Ruby's internal Grep method instead of shelling out.
|
63
64
|
config.domain_name = 'mywebsitedomain.com' # it is recommended you configure this
|
64
|
-
config.blacklist = ['this_password_is_not_allowed']
|
65
|
+
config.blacklist = ['this_password_is_not_allowed', /password/]
|
65
66
|
end
|
66
67
|
```
|
67
68
|
|
@@ -70,7 +71,7 @@ Optionally, you can configure some options:
|
|
70
71
|
I included `PasswordValidator` for Rails. Validating passwords in your model couldn't be easier:
|
71
72
|
|
72
73
|
```ruby
|
73
|
-
validates :password, presence: true, password: true, if: -> { new_record? || changes[:password] }
|
74
|
+
validates :password, presence: true, password: true, if: -> { new_record? || changes[:password] || changes[:password_digest] }
|
74
75
|
```
|
75
76
|
|
76
77
|
PasswordValidator will try to guess the correct field name for each `PasswordChecker` argument as follow:
|
@@ -84,7 +85,7 @@ If you have field names different than above, you can tell `PasswordValidator` w
|
|
84
85
|
```ruby
|
85
86
|
validates :password, password: { :name => :customer_name,
|
86
87
|
:email => :electronic_address },
|
87
|
-
if: -> { new_record? || changes[:password] }
|
88
|
+
if: -> { new_record? || changes[:password] || changes[:password_digest] }
|
88
89
|
```
|
89
90
|
|
90
91
|
## Validations
|
@@ -1,6 +1,7 @@
|
|
1
1
|
class ActiveModel::Validations::PasswordValidator < ActiveModel::EachValidator
|
2
2
|
DEFAULT_ERROR_MESSAGES = {
|
3
3
|
name_included_in_password: 'is too similar to your name',
|
4
|
+
username_included_in_password: 'is too similar to your username',
|
4
5
|
email_included_in_password: 'is too similar to your email',
|
5
6
|
domain_included_in_password: 'is too similar to this domain name',
|
6
7
|
password_too_short: 'is too short',
|
data/lib/nobspw/configuration.rb
CHANGED
@@ -5,6 +5,7 @@ module NOBSPW
|
|
5
5
|
attr_accessor :min_unique_characters
|
6
6
|
attr_accessor :dictionary_path
|
7
7
|
attr_accessor :grep_path
|
8
|
+
attr_accessor :use_ruby_grep
|
8
9
|
attr_accessor :domain_name
|
9
10
|
attr_accessor :blacklist
|
10
11
|
attr_accessor :validation_methods
|
@@ -16,6 +17,7 @@ module NOBSPW
|
|
16
17
|
@min_unique_characters = 5
|
17
18
|
@dictionary_path = File.join(File.dirname(__FILE__), "..", "db", "dictionary.txt")
|
18
19
|
@grep_path = `which grep`.strip
|
20
|
+
@use_ruby_grep = @grep_path.empty?
|
19
21
|
@domain_name = nil
|
20
22
|
@blacklist = nil
|
21
23
|
@validation_methods = NOBSPW::ValidationMethods::DEFAULT_VALIDATION_METHODS
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
1
3
|
module NOBSPW
|
2
4
|
class PasswordChecker
|
3
5
|
include NOBSPW::ValidationMethods
|
@@ -26,7 +28,6 @@ module NOBSPW
|
|
26
28
|
|
27
29
|
def check_password
|
28
30
|
@weak_password_reasons = []
|
29
|
-
|
30
31
|
NOBSPW.configuration.validation_methods.each do |method|
|
31
32
|
if send("#{method}")
|
32
33
|
@weak_password_reasons << method.to_s.sub(/\?$/, '').to_sym
|
@@ -36,9 +37,5 @@ module NOBSPW
|
|
36
37
|
|
37
38
|
@strong = @weak_password_reasons.empty?
|
38
39
|
end
|
39
|
-
|
40
|
-
def grep_command(path)
|
41
|
-
"#{NOBSPW.configuration.grep_path} '^#{@password}$' #{path}"
|
42
|
-
end
|
43
40
|
end
|
44
41
|
end
|
@@ -1,7 +1,11 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
require 'open3'
|
3
|
+
|
1
4
|
module NOBSPW
|
2
5
|
module ValidationMethods
|
3
6
|
DEFAULT_VALIDATION_METHODS = %i(password_empty?
|
4
7
|
name_included_in_password?
|
8
|
+
username_included_in_password?
|
5
9
|
email_included_in_password?
|
6
10
|
domain_included_in_password?
|
7
11
|
password_too_short?
|
@@ -11,6 +15,8 @@ module NOBSPW
|
|
11
15
|
password_too_common?)
|
12
16
|
|
13
17
|
INTERRUPT_VALIDATION_FOR = %i(password_empty?)
|
18
|
+
STDIN_GREP_COMMAND = ['/usr/bin/grep', '-m 1', '-f', '/dev/stdin',
|
19
|
+
NOBSPW.configuration.dictionary_path]
|
14
20
|
|
15
21
|
private
|
16
22
|
|
@@ -24,6 +30,12 @@ module NOBSPW
|
|
24
30
|
words_included_in_password?(words)
|
25
31
|
end
|
26
32
|
|
33
|
+
def username_included_in_password?
|
34
|
+
return nil unless @username
|
35
|
+
words = remove_word_separators(@username).split(' ')
|
36
|
+
words_included_in_password?(words)
|
37
|
+
end
|
38
|
+
|
27
39
|
def email_included_in_password?
|
28
40
|
return nil unless @email
|
29
41
|
words = remove_word_separators(email_without_extension(@email)).split(' ')
|
@@ -40,7 +52,16 @@ module NOBSPW
|
|
40
52
|
|
41
53
|
def password_not_allowed?
|
42
54
|
return nil unless NOBSPW.configuration.blacklist
|
43
|
-
|
55
|
+
|
56
|
+
NOBSPW.configuration.blacklist.each do |expression|
|
57
|
+
if expression.is_a?(Regexp)
|
58
|
+
return true if @password.match?(expression)
|
59
|
+
else
|
60
|
+
return true if expression.to_s == @password
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
false
|
44
65
|
end
|
45
66
|
|
46
67
|
def password_too_short?
|
@@ -65,22 +86,27 @@ module NOBSPW
|
|
65
86
|
end
|
66
87
|
|
67
88
|
def password_too_common?
|
68
|
-
|
69
|
-
|
70
|
-
case $?.exitstatus
|
71
|
-
when 0
|
72
|
-
true
|
73
|
-
when 1
|
74
|
-
false
|
75
|
-
when 127
|
76
|
-
raise StandardError.new("Grep not found at: #{NOBSPW.configuration.grep_path}")
|
77
|
-
else
|
78
|
-
false
|
79
|
-
end
|
89
|
+
NOBSPW.configuration.use_ruby_grep ? ruby_grep : shell_grep
|
80
90
|
end
|
81
91
|
|
82
92
|
# Helper methods
|
83
93
|
|
94
|
+
def shell_grep
|
95
|
+
raise StandardError.new("Grep not found at: #{NOBSPW.configuration.grep_path}") \
|
96
|
+
if !File.exist?(NOBSPW.configuration.grep_path)
|
97
|
+
|
98
|
+
output = Open3.popen3(STDIN_GREP_COMMAND.join(" "), out: '/dev/null') { |stdin, stdout, stderr, wait_thr|
|
99
|
+
stdin.puts "^#{escaped_password}$"
|
100
|
+
stdin.close
|
101
|
+
wait_thr.value
|
102
|
+
}
|
103
|
+
output.success?
|
104
|
+
end
|
105
|
+
|
106
|
+
def ruby_grep
|
107
|
+
File.open(NOBSPW.configuration.dictionary_path).grep(/^#{escaped_password}$/).any?
|
108
|
+
end
|
109
|
+
|
84
110
|
def email_without_extension(email)
|
85
111
|
name, domain, whatev = email&.split("@", 3)
|
86
112
|
"#{name}@#{strip_extension_from_domain(domain)}"
|
@@ -94,5 +120,8 @@ module NOBSPW
|
|
94
120
|
str&.gsub(/-|_|\.|\'|\"|\@/, ' ')
|
95
121
|
end
|
96
122
|
|
123
|
+
def escaped_password(password = @password)
|
124
|
+
Shellwords.escape(password)
|
125
|
+
end
|
97
126
|
end
|
98
127
|
end
|
data/lib/nobspw/version.rb
CHANGED
@@ -0,0 +1,73 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'benchmark'
|
4
|
+
require 'shellwords'
|
5
|
+
require 'open3'
|
6
|
+
require 'subprocess'
|
7
|
+
|
8
|
+
ITERATIONS = 100
|
9
|
+
DICTIONARY_PATH = File.join(File.dirname(__FILE__), '..', 'lib/db/dictionary.txt')
|
10
|
+
STDIN_GREP_COMMAND = ['/usr/bin/grep', '-m 1', '-f', '/dev/stdin', DICTIONARY_PATH]
|
11
|
+
|
12
|
+
password = 'swordfish'
|
13
|
+
|
14
|
+
def shell_grep(password)
|
15
|
+
password = Shellwords.escape(password)
|
16
|
+
`/usr/bin/grep -m 1 '^#{password}$' #{DICTIONARY_PATH}`
|
17
|
+
$?.exitstatus == 0
|
18
|
+
end
|
19
|
+
|
20
|
+
def shell_grep_open3(password)
|
21
|
+
password = Shellwords.escape(password)
|
22
|
+
|
23
|
+
output = Open3.popen3(STDIN_GREP_COMMAND.join(" "), out: '/dev/null') { |stdin, stdout, stderr, wait_thr|
|
24
|
+
stdin.puts "^#{password}$"
|
25
|
+
stdin.close
|
26
|
+
wait_thr.value
|
27
|
+
}
|
28
|
+
output.success?
|
29
|
+
end
|
30
|
+
|
31
|
+
def shell_grep_subprocess(password)
|
32
|
+
password = Shellwords.escape(password)
|
33
|
+
|
34
|
+
Subprocess.check_call(STDIN_GREP_COMMAND, stdin: Subprocess::PIPE, stdout: '/dev/null') do |p|
|
35
|
+
p.communicate("^#{password}$")
|
36
|
+
end
|
37
|
+
true
|
38
|
+
rescue Subprocess::NonZeroExit
|
39
|
+
false
|
40
|
+
end
|
41
|
+
|
42
|
+
def ruby_grep(password)
|
43
|
+
File.open(DICTIONARY_PATH).grep(/^#{password}$/)
|
44
|
+
end
|
45
|
+
|
46
|
+
def ruby_loop(password)
|
47
|
+
File.open(DICTIONARY_PATH).read_line do |l|
|
48
|
+
return true if l.match?(/^#{password}$/)
|
49
|
+
end
|
50
|
+
false
|
51
|
+
end
|
52
|
+
|
53
|
+
Benchmark.bm(17) do |benchmark|
|
54
|
+
benchmark.report("Shell") do
|
55
|
+
ITERATIONS.times { shell_grep(password) }
|
56
|
+
end
|
57
|
+
|
58
|
+
benchmark.report("Ruby Grep") do
|
59
|
+
ITERATIONS.times { ruby_grep(password) }
|
60
|
+
end
|
61
|
+
|
62
|
+
benchmark.report("Ruby Loop") do
|
63
|
+
ITERATIONS.times { ruby_grep(password) }
|
64
|
+
end
|
65
|
+
|
66
|
+
benchmark.report("Open3 stdin") do
|
67
|
+
ITERATIONS.times { shell_grep_open3(password) }
|
68
|
+
end
|
69
|
+
|
70
|
+
benchmark.report("Subprocess stdin") do
|
71
|
+
ITERATIONS.times { shell_grep_subprocess(password) }
|
72
|
+
end
|
73
|
+
end
|
data/nobspw.gemspec
CHANGED
@@ -21,14 +21,16 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
|
24
|
-
spec.add_development_dependency "bundler"
|
25
|
-
spec.add_development_dependency "rake", "~>
|
24
|
+
spec.add_development_dependency "bundler"
|
25
|
+
spec.add_development_dependency "rake", "~> 12.3.3"
|
26
26
|
spec.add_development_dependency "rspec", "~> 3.0"
|
27
27
|
spec.add_development_dependency "simplecov", "~> 0.13"
|
28
28
|
spec.add_development_dependency "guard", "~> 2.14"
|
29
29
|
spec.add_development_dependency "guard-rspec", "~> 4.7.3"
|
30
30
|
spec.add_development_dependency "activemodel", "~> 5.0"
|
31
31
|
spec.add_development_dependency "i18n", "~> 0.8.1"
|
32
|
+
spec.add_development_dependency "subprocess"
|
33
|
+
spec.add_development_dependency "byebug"
|
32
34
|
|
33
35
|
if RUBY_PLATFORM =~ /darwin/
|
34
36
|
spec.add_development_dependency 'ruby_gntp', "~> 0.3.4"
|
metadata
CHANGED
@@ -1,43 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nobspw
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Carl Mercier
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-06-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 12.3.3
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 12.3.3
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -122,6 +122,34 @@ dependencies:
|
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: 0.8.1
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: subprocess
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: byebug
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
125
153
|
- !ruby/object:Gem::Dependency
|
126
154
|
name: ruby_gntp
|
127
155
|
requirement: !ruby/object:Gem::Requirement
|
@@ -175,6 +203,7 @@ files:
|
|
175
203
|
- lib/nobspw/password_checker.rb
|
176
204
|
- lib/nobspw/validation_methods.rb
|
177
205
|
- lib/nobspw/version.rb
|
206
|
+
- misc/grep_benchmark.rb
|
178
207
|
- nobspw.gemspec
|
179
208
|
homepage: https://github.com/cmer/nobspw
|
180
209
|
licenses:
|
@@ -195,8 +224,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
195
224
|
- !ruby/object:Gem::Version
|
196
225
|
version: '0'
|
197
226
|
requirements: []
|
198
|
-
|
199
|
-
rubygems_version: 2.7.3
|
227
|
+
rubygems_version: 3.1.2
|
200
228
|
signing_key:
|
201
229
|
specification_version: 4
|
202
230
|
summary: No Bullshit Password strength checker
|