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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 50c1081ffc7e72b8a1e361ad624adf96198a4c6e514591b3594882a2a9def135
4
- data.tar.gz: 65d5f0f41746f64b9cc91861e664c85fd7d9d0f15242459b4a928e9e47bdab18
3
+ metadata.gz: 7b1e7ea86fc19c81387ce58eb83ab484557ac181fdbff154ba374475a83e30f0
4
+ data.tar.gz: cbedab06fecd36fea8084a9d7a3beb8ede35b16b5d8fc2ada55466473854fcec
5
5
  SHA512:
6
- metadata.gz: 55b0fead2d885c1ea121c6f188225dfd6ba75a8ddb921c43d188f74d33b2bcc466d2dda1a9fffd7905cd5353ba4b1b5f17a702810a63a04b83f9d819aa37efc1
7
- data.tar.gz: e1479fe39d6fd86d6a450ceb35681c4311d7125ebeb2c9da382301a0d7d13b4bcf8256f5205fcbf969295ff5459c57ac04c741c3b5160f87242249e0dc2d8891
6
+ metadata.gz: bdc5fb645fb17fb43b7b760274b056e0e1ddea9df67121e5be045a49c6e66c1208e0194213d5aefbce55348f1b0640c4ed48d4f73ee971f230cc787c2b957e40
7
+ data.tar.gz: f2d71325a90aae8ffbe3b0d7f91a9d03e92de23300a39dfcaae67c4ed241e002b69a9785c17c04211fd9e30709512d2d5095a4c5aed15445b9b381d556f197af
data/.travis.yml CHANGED
@@ -4,4 +4,5 @@ language: ruby
4
4
  cache: bundler
5
5
  rvm:
6
6
  - 2.6.0
7
+ - 2.7.1
7
8
  before_install: "gem install 'bundler:~>2.0'"
data/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # 1.0.1
2
+
3
+ - Fixed exception in `UnpwnedValidator` when the value was `nil`.
4
+
5
+ # 1.0.0
6
+
7
+ Initial release.
data/Gemfile.lock CHANGED
@@ -1,61 +1,65 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- unpwn (0.3.0)
4
+ unpwn (1.0.1)
5
5
  bloomer (~> 1.0)
6
- pwned (~> 1.2)
6
+ pwned (~> 2.0)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- addressable (2.5.2)
12
- public_suffix (>= 2.0.2, < 4.0)
13
- bitarray (1.2.0)
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.3)
18
- domain_name (0.5.20180417)
19
- unf (>= 0.0.5, < 1.0.0)
20
- http (4.0.0)
21
- addressable (~> 2.3)
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.0)
24
- http_parser.rb (~> 0.6.0)
25
- http-cookie (1.0.3)
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.1.1)
28
- http_parser.rb (0.6.0)
29
- msgpack (1.3.1)
30
- public_suffix (3.0.3)
31
- pwned (1.2.1)
32
- rake (10.5.0)
33
- rspec (3.8.0)
34
- rspec-core (~> 3.8.0)
35
- rspec-expectations (~> 3.8.0)
36
- rspec-mocks (~> 3.8.0)
37
- rspec-core (3.8.0)
38
- rspec-support (~> 3.8.0)
39
- rspec-expectations (3.8.2)
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.8.0)
42
- rspec-mocks (3.8.0)
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.8.0)
45
- rspec-support (3.8.0)
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 (~> 4.0)
56
- rake (~> 10.0)
59
+ http (~> 5.0)
60
+ rake (~> 13.0)
57
61
  rspec (~> 3.0)
58
62
  unpwn!
59
63
 
60
64
  BUNDLED WITH
61
- 2.0.1
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
- Add `unpwn` to your `Gemfile`:
11
+ Run `bundle add unpwn` to add the gem to your `Gemfile`.
12
+
13
+ ## Usage (Rails)
12
14
 
13
15
  ```ruby
14
- gem "unpwn", "~> 1.0"
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
@@ -1,3 +1,3 @@
1
1
  class Unpwn
2
- VERSION = "0.3.0"
2
+ VERSION = "1.0.1"
3
3
  end
data/lib/unpwn.rb CHANGED
@@ -1,13 +1,26 @@
1
1
  require "unpwn/version"
2
2
 
3
- require "bloomer"
4
- require "bloomer/msgpackable"
5
- require "pwned"
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) || Pwned.pwned?(password, request_options)
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", "~> 1.2"
24
+ spec.add_dependency "pwned", "~> 2.0"
25
25
 
26
26
  spec.add_development_dependency "bundler", ">= 1"
27
- spec.add_development_dependency "http", "~> 4.0"
28
- spec.add_development_dependency "rake", "~> 10.0"
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.3.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: 2019-09-16 00:00:00.000000000 Z
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: '1.2'
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: '1.2'
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: '4.0'
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: '4.0'
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: '10.0'
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: '10.0'
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.0.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: []