unpwn 0.3.0 → 1.0.1
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/.travis.yml +1 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile.lock +40 -36
- data/README.md +7 -3
- data/lib/unpwn/version.rb +1 -1
- data/lib/unpwn.rb +39 -6
- data/lib/unpwned_validator.rb +37 -0
- data/unpwn.gemspec +3 -3
- metadata +11 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7b1e7ea86fc19c81387ce58eb83ab484557ac181fdbff154ba374475a83e30f0
|
4
|
+
data.tar.gz: cbedab06fecd36fea8084a9d7a3beb8ede35b16b5d8fc2ada55466473854fcec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bdc5fb645fb17fb43b7b760274b056e0e1ddea9df67121e5be045a49c6e66c1208e0194213d5aefbce55348f1b0640c4ed48d4f73ee971f230cc787c2b957e40
|
7
|
+
data.tar.gz: f2d71325a90aae8ffbe3b0d7f91a9d03e92de23300a39dfcaae67c4ed241e002b69a9785c17c04211fd9e30709512d2d5095a4c5aed15445b9b381d556f197af
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
ADDED
data/Gemfile.lock
CHANGED
@@ -1,61 +1,65 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
unpwn (0.
|
4
|
+
unpwn (1.0.1)
|
5
5
|
bloomer (~> 1.0)
|
6
|
-
pwned (~>
|
6
|
+
pwned (~> 2.0)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
-
addressable (2.
|
12
|
-
public_suffix (>= 2.0.2, <
|
13
|
-
|
11
|
+
addressable (2.8.7)
|
12
|
+
public_suffix (>= 2.0.2, < 7.0)
|
13
|
+
base64 (0.2.0)
|
14
|
+
bitarray (1.3.1)
|
14
15
|
bloomer (1.0.0)
|
15
16
|
bitarray
|
16
17
|
msgpack
|
17
|
-
diff-lcs (1.
|
18
|
-
domain_name (0.
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
diff-lcs (1.6.2)
|
19
|
+
domain_name (0.6.20240107)
|
20
|
+
ffi (1.17.2)
|
21
|
+
ffi-compiler (1.3.2)
|
22
|
+
ffi (>= 1.15.5)
|
23
|
+
rake
|
24
|
+
http (5.2.0)
|
25
|
+
addressable (~> 2.8)
|
26
|
+
base64 (~> 0.1)
|
22
27
|
http-cookie (~> 1.0)
|
23
|
-
http-form_data (~> 2.
|
24
|
-
|
25
|
-
http-cookie (1.0.
|
28
|
+
http-form_data (~> 2.2)
|
29
|
+
llhttp-ffi (~> 0.5.0)
|
30
|
+
http-cookie (1.0.8)
|
26
31
|
domain_name (~> 0.5)
|
27
|
-
http-form_data (2.
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
rspec-
|
37
|
-
|
38
|
-
rspec-
|
39
|
-
rspec-
|
32
|
+
http-form_data (2.3.0)
|
33
|
+
llhttp-ffi (0.5.1)
|
34
|
+
ffi-compiler (~> 1.0)
|
35
|
+
rake (~> 13.0)
|
36
|
+
msgpack (1.8.0)
|
37
|
+
public_suffix (6.0.2)
|
38
|
+
pwned (2.4.1)
|
39
|
+
rake (13.2.1)
|
40
|
+
rspec (3.13.0)
|
41
|
+
rspec-core (~> 3.13.0)
|
42
|
+
rspec-expectations (~> 3.13.0)
|
43
|
+
rspec-mocks (~> 3.13.0)
|
44
|
+
rspec-core (3.13.3)
|
45
|
+
rspec-support (~> 3.13.0)
|
46
|
+
rspec-expectations (3.13.4)
|
40
47
|
diff-lcs (>= 1.2.0, < 2.0)
|
41
|
-
rspec-support (~> 3.
|
42
|
-
rspec-mocks (3.
|
48
|
+
rspec-support (~> 3.13.0)
|
49
|
+
rspec-mocks (3.13.4)
|
43
50
|
diff-lcs (>= 1.2.0, < 2.0)
|
44
|
-
rspec-support (~> 3.
|
45
|
-
rspec-support (3.
|
46
|
-
unf (0.1.4)
|
47
|
-
unf_ext
|
48
|
-
unf_ext (0.0.7.5)
|
51
|
+
rspec-support (~> 3.13.0)
|
52
|
+
rspec-support (3.13.3)
|
49
53
|
|
50
54
|
PLATFORMS
|
51
55
|
ruby
|
52
56
|
|
53
57
|
DEPENDENCIES
|
54
58
|
bundler (>= 1)
|
55
|
-
http (~>
|
56
|
-
rake (~>
|
59
|
+
http (~> 5.0)
|
60
|
+
rake (~> 13.0)
|
57
61
|
rspec (~> 3.0)
|
58
62
|
unpwn!
|
59
63
|
|
60
64
|
BUNDLED WITH
|
61
|
-
2.
|
65
|
+
2.6.9
|
data/README.md
CHANGED
@@ -8,13 +8,17 @@ Inspired by @codahale's [passpol](https://github.com/codahale/passpol), and uses
|
|
8
8
|
|
9
9
|
## Installation
|
10
10
|
|
11
|
-
|
11
|
+
Run `bundle add unpwn` to add the gem to your `Gemfile`.
|
12
|
+
|
13
|
+
## Usage (Rails)
|
12
14
|
|
13
15
|
```ruby
|
14
|
-
|
16
|
+
class User
|
17
|
+
validates :password, unpwned: true, if: -> { Rails.env.production? }
|
18
|
+
end
|
15
19
|
```
|
16
20
|
|
17
|
-
## Usage
|
21
|
+
## Usage (Ruby)
|
18
22
|
|
19
23
|
```ruby
|
20
24
|
require "unpwn"
|
data/lib/unpwn/version.rb
CHANGED
data/lib/unpwn.rb
CHANGED
@@ -1,13 +1,26 @@
|
|
1
1
|
require "unpwn/version"
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
# Unpwn.pwned? tells you if a password should be rejected.
|
3
|
+
# Unpwn checks passwords locally against the top one million passwords, as
|
4
|
+
# provided by the nbp project. Then, it uses the haveibeenpwned API to check
|
5
|
+
# proposed passwords against the largest corpus of publicly dumped passwords in
|
6
|
+
# the world.
|
8
7
|
class Unpwn
|
8
|
+
class << self
|
9
|
+
# Set `offline` to true to disable requests to the haveibeenpwned.com API
|
10
|
+
attr_accessor :offline
|
11
|
+
|
12
|
+
# Check if a password is _not_ already published. To set options like
|
13
|
+
# `min`, `max`, or on the Pwned API check, create a new instance of your
|
14
|
+
# own.
|
15
|
+
def acceptable?(password)
|
16
|
+
new.acceptable?(password)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
9
20
|
attr_reader :min, :max, :request_options
|
10
21
|
|
22
|
+
# Set the options for an Unpwn instance. `request_options` will be passed
|
23
|
+
# verbatim to the `Pwned` library.
|
11
24
|
def initialize(min: 8, max: nil, request_options: nil)
|
12
25
|
raise ArgumentError if min && min < 8
|
13
26
|
raise ArgumentError if max && max < 64
|
@@ -17,6 +30,7 @@ class Unpwn
|
|
17
30
|
@request_options = request_options || {}
|
18
31
|
end
|
19
32
|
|
33
|
+
# Check if a password meets the requirements and is not pwned.
|
20
34
|
def acceptable?(password)
|
21
35
|
return false if min && password.size < min
|
22
36
|
return false if max && password.size > max
|
@@ -24,15 +38,34 @@ class Unpwn
|
|
24
38
|
!pwned?(password)
|
25
39
|
end
|
26
40
|
|
41
|
+
# Checks if a password is pwned, via bloom filter then `Pwned`.
|
27
42
|
def pwned?(password)
|
28
|
-
bloom.include?(password)
|
43
|
+
pwned = bloom.include?(password)
|
44
|
+
|
45
|
+
unless self.class.offline
|
46
|
+
require "pwned"
|
47
|
+
pwned ||= Pwned.pwned?(password, request_options)
|
48
|
+
end
|
49
|
+
|
50
|
+
pwned
|
29
51
|
end
|
30
52
|
|
31
53
|
def bloom
|
32
54
|
@bloom ||= begin
|
55
|
+
require "bloomer"
|
56
|
+
require "bloomer/msgpackable"
|
33
57
|
top = File.read File.expand_path("top1000000.msgpack", __dir__)
|
34
58
|
Bloomer.from_msgpack(top)
|
35
59
|
end
|
36
60
|
end
|
37
61
|
|
62
|
+
def inspect
|
63
|
+
"<UnPwn bloomed=#{@bloom ? 'yes' : 'no'}>"
|
64
|
+
end
|
65
|
+
|
66
|
+
alias :to_s :inspect
|
67
|
+
end
|
68
|
+
|
69
|
+
if defined?(ActiveModel) || defined?(Rails)
|
70
|
+
autoload :UnpwnedValidator, "unpwned_validator"
|
38
71
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "unpwn"
|
2
|
+
|
3
|
+
# Validator class for passwords
|
4
|
+
#
|
5
|
+
# ==== Examples
|
6
|
+
#
|
7
|
+
# Validates that attribute is not pwned, but only in production.
|
8
|
+
#
|
9
|
+
# class User < ActiveRecord::Base
|
10
|
+
# validates :password, unpwned: true, if: -> { Rails.env.production? }
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# Validates that attribute meets min/max and is not pwned.
|
14
|
+
#
|
15
|
+
# class User < ActiveRecord::Base
|
16
|
+
# validates :password, unpwned: { min: 12, max: 128 }
|
17
|
+
# end
|
18
|
+
class UnpwnedValidator < ActiveModel::EachValidator
|
19
|
+
def validate_each(record, attribute, value)
|
20
|
+
return if value.nil?
|
21
|
+
|
22
|
+
unpwn = Unpwn.new(**options.slice(:min, :max, :request_options))
|
23
|
+
|
24
|
+
if unpwn.min && value.length < unpwn.min
|
25
|
+
record.errors.add attribute, "is too short"
|
26
|
+
end
|
27
|
+
|
28
|
+
if unpwn.max && value.length > unpwn.max
|
29
|
+
record.errors.add attribute, "is too long"
|
30
|
+
end
|
31
|
+
|
32
|
+
if unpwn.pwned?(value)
|
33
|
+
record.errors.add attribute, options.fetch(:message,
|
34
|
+
"is in common password lists, please choose something more unique")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/unpwn.gemspec
CHANGED
@@ -21,10 +21,10 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.require_paths = ["lib"]
|
22
22
|
|
23
23
|
spec.add_dependency "bloomer", "~> 1.0"
|
24
|
-
spec.add_dependency "pwned", "~>
|
24
|
+
spec.add_dependency "pwned", "~> 2.0"
|
25
25
|
|
26
26
|
spec.add_development_dependency "bundler", ">= 1"
|
27
|
-
spec.add_development_dependency "http", "~>
|
28
|
-
spec.add_development_dependency "rake", "~>
|
27
|
+
spec.add_development_dependency "http", "~> 5.0"
|
28
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
29
29
|
spec.add_development_dependency "rspec", "~> 3.0"
|
30
30
|
end
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unpwn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andre Arko
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: bloomer
|
@@ -30,14 +29,14 @@ dependencies:
|
|
30
29
|
requirements:
|
31
30
|
- - "~>"
|
32
31
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
32
|
+
version: '2.0'
|
34
33
|
type: :runtime
|
35
34
|
prerelease: false
|
36
35
|
version_requirements: !ruby/object:Gem::Requirement
|
37
36
|
requirements:
|
38
37
|
- - "~>"
|
39
38
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
39
|
+
version: '2.0'
|
41
40
|
- !ruby/object:Gem::Dependency
|
42
41
|
name: bundler
|
43
42
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,28 +57,28 @@ dependencies:
|
|
58
57
|
requirements:
|
59
58
|
- - "~>"
|
60
59
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
60
|
+
version: '5.0'
|
62
61
|
type: :development
|
63
62
|
prerelease: false
|
64
63
|
version_requirements: !ruby/object:Gem::Requirement
|
65
64
|
requirements:
|
66
65
|
- - "~>"
|
67
66
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
67
|
+
version: '5.0'
|
69
68
|
- !ruby/object:Gem::Dependency
|
70
69
|
name: rake
|
71
70
|
requirement: !ruby/object:Gem::Requirement
|
72
71
|
requirements:
|
73
72
|
- - "~>"
|
74
73
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
74
|
+
version: '13.0'
|
76
75
|
type: :development
|
77
76
|
prerelease: false
|
78
77
|
version_requirements: !ruby/object:Gem::Requirement
|
79
78
|
requirements:
|
80
79
|
- - "~>"
|
81
80
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
81
|
+
version: '13.0'
|
83
82
|
- !ruby/object:Gem::Dependency
|
84
83
|
name: rspec
|
85
84
|
requirement: !ruby/object:Gem::Requirement
|
@@ -104,6 +103,7 @@ files:
|
|
104
103
|
- ".gitignore"
|
105
104
|
- ".rspec"
|
106
105
|
- ".travis.yml"
|
106
|
+
- CHANGELOG.md
|
107
107
|
- CODE_OF_CONDUCT.md
|
108
108
|
- Gemfile
|
109
109
|
- Gemfile.lock
|
@@ -115,12 +115,12 @@ files:
|
|
115
115
|
- lib/top1000000.msgpack
|
116
116
|
- lib/unpwn.rb
|
117
117
|
- lib/unpwn/version.rb
|
118
|
+
- lib/unpwned_validator.rb
|
118
119
|
- unpwn.gemspec
|
119
120
|
homepage: https://github.com/indirect/unpwn
|
120
121
|
licenses:
|
121
122
|
- MIT
|
122
123
|
metadata: {}
|
123
|
-
post_install_message:
|
124
124
|
rdoc_options: []
|
125
125
|
require_paths:
|
126
126
|
- lib
|
@@ -135,8 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
135
135
|
- !ruby/object:Gem::Version
|
136
136
|
version: '0'
|
137
137
|
requirements: []
|
138
|
-
rubygems_version: 3.
|
139
|
-
signing_key:
|
138
|
+
rubygems_version: 3.6.8
|
140
139
|
specification_version: 4
|
141
140
|
summary: Keeps passwords from being easily hackable.
|
142
141
|
test_files: []
|