pwned 1.2.1 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +1 -0
- data/.github/workflows/tests.yml +39 -0
- data/CHANGELOG.md +75 -18
- data/README.md +153 -10
- data/bin/pwned +52 -0
- data/lib/pwned.rb +22 -6
- data/lib/pwned/hashed_password.rb +36 -0
- data/lib/pwned/not_pwned_validator.rb +3 -3
- data/lib/pwned/password.rb +12 -96
- data/lib/pwned/password_base.rb +141 -0
- data/lib/pwned/version.rb +1 -1
- data/pwned.gemspec +11 -2
- metadata +29 -34
- data/.travis.yml +0 -23
- data/docs/NotPwnedValidator.html +0 -488
- data/docs/Pwned.html +0 -513
- data/docs/Pwned/Error.html +0 -149
- data/docs/Pwned/Password.html +0 -925
- data/docs/Pwned/TimeoutError.html +0 -152
- data/docs/PwnedValidator.html +0 -192
- data/docs/_index.html +0 -162
- data/docs/class_list.html +0 -51
- data/docs/css/common.css +0 -1
- data/docs/css/full_list.css +0 -58
- data/docs/css/style.css +0 -499
- data/docs/file.README.html +0 -294
- data/docs/file_list.html +0 -56
- data/docs/frames.html +0 -17
- data/docs/index.html +0 -294
- data/docs/js/app.js +0 -248
- data/docs/js/full_list.js +0 -216
- data/docs/js/jquery.js +0 -4
- data/docs/method_list.html +0 -115
- data/docs/top-level-namespace.html +0 -112
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1790330e2068217b48ba0929c4b2409dfecd939e76f7d231acc872ef9802320a
|
4
|
+
data.tar.gz: afa13cc83a53df1be859a74876e00992b15eed757e571bec652168d85b3ab73e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23482aeeab95bb130ba04f1fdc57f769282d78c7c4c56c594f15652c472ffce9573967d8a31d539548a325c85f54aa038d65bb023aded41352147ad9a906534e
|
7
|
+
data.tar.gz: d31fb7ad5171adc4cc8ee28ed97d125fbd19c93bc68e65e7921076d1fb586748a1a4f12007e70cbfe6378e3c868effa756efaa08e627bfa82b2c73166e0b7dd1
|
data/.github/FUNDING.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
github: philnash
|
@@ -0,0 +1,39 @@
|
|
1
|
+
name: tests
|
2
|
+
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
test:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
strategy:
|
9
|
+
fail-fast: false
|
10
|
+
matrix:
|
11
|
+
ruby: [2.5, 2.6, 2.7, 3.0, head]
|
12
|
+
rails: [4.2.11.3, 5.0.7.2, 5.1.7, 5.2.4.4, 6.0.3.4, 6.1.0]
|
13
|
+
exclude:
|
14
|
+
# Ruby 3.0 and Rails 5 do not get along together.
|
15
|
+
- ruby: 3.0
|
16
|
+
rails: 5.0.7.2
|
17
|
+
- ruby: 3.0
|
18
|
+
rails: 5.1.7
|
19
|
+
- ruby: 3.0
|
20
|
+
rails: 5.2.4.4
|
21
|
+
- ruby: head
|
22
|
+
rails: 5.0.7.2
|
23
|
+
- ruby: head
|
24
|
+
rails: 5.1.7
|
25
|
+
- ruby: head
|
26
|
+
rails: 5.2.4.4
|
27
|
+
continue-on-error: ${{ endsWith(matrix.ruby, 'head') }}
|
28
|
+
env:
|
29
|
+
RAILS_VERSION: ${{ matrix.rails }}
|
30
|
+
steps:
|
31
|
+
- uses: actions/checkout@v2
|
32
|
+
- name: Set up Ruby ${{ matrix.ruby }}
|
33
|
+
uses: ruby/setup-ruby@v1
|
34
|
+
with:
|
35
|
+
ruby-version: ${{ matrix.ruby }}
|
36
|
+
- name: "Install dependencies (rails: ${{matrix.rails}})"
|
37
|
+
run: bundle install
|
38
|
+
- name: Run tests
|
39
|
+
run: bundle exec rspec
|
data/CHANGELOG.md
CHANGED
@@ -1,31 +1,88 @@
|
|
1
1
|
# Changelog for `Pwned`
|
2
2
|
|
3
|
-
## Ongoing [☰](https://github.com/philnash/pwned/compare/
|
3
|
+
## Ongoing [☰](https://github.com/philnash/pwned/compare/v2.0.2...master)
|
4
4
|
|
5
|
-
...
|
5
|
+
## 2.2.0 (March 27, 2021) [☰](https://github.com/philnash/pwned/compare/v2.1.0...v2.2.0)
|
6
6
|
|
7
|
-
|
7
|
+
- Minor updates
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
- Adds `:proxy` option to `request_options` to directly set a proxy on the
|
10
|
+
request. Fixes #21, thanks [dparpyani](https://github.com/dparpyani).
|
11
11
|
|
12
|
-
## 1.
|
12
|
+
## 2.1.0 (July 8, 2020) [☰](https://github.com/philnash/pwned/compare/v2.0.2...v2.1.0)
|
13
13
|
|
14
|
-
|
15
|
-
* Changes `PwnedValidator` to `NotPwnedValidator`, so that the validation looks like `validates :password, not_pwned: true`. `PwnedValidator` now subclasses `NotPwnedValidator` for backwards compatibility with version 1.1.0 but is deprecated.
|
14
|
+
- Minor updates
|
16
15
|
|
17
|
-
|
16
|
+
- Adds `Pwned::HashedPassword` class which is initializd with a SHA1 hash to
|
17
|
+
query the API with so that the lookup can be done in the background without
|
18
|
+
storing passwords. Fixes #19, thanks [@paprikati](https://github.com/paprikati).
|
18
19
|
|
19
|
-
|
20
|
-
* Refactors exception handling with built in Ruby method ([PR #1](https://github.com/philnash/pwned/pull/1) thanks [@kpumuk](https://github.com/kpumuk))
|
21
|
-
* Passwords must be strings, the initializer will raise a `TypeError` unless `password.is_a? String`. ([dbf7697](https://github.com/philnash/pwned/commit/dbf7697e878d87ac74aed1e715cee19b73473369))
|
22
|
-
* Added Ruby on Rails validator ([PR #3](https://github.com/philnash/pwned/pull/3) & [PR #6](https://github.com/philnash/pwned/pull/6))
|
23
|
-
* Added simplified accessors `Pwned.pwned?` and `Pwned.pwned_count` ([PR #4](https://github.com/philnash/pwned/pull/4))
|
20
|
+
## 2.0.2 (May 20, 2020) [☰](https://github.com/philnash/pwned/compare/v2.0.1...v2.0.2)
|
24
21
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
22
|
+
- Minor fix
|
23
|
+
|
24
|
+
- It was found to be possible for reading the lines body of a response to
|
25
|
+
result in a `nil` which caused trouble with string concatenation. This
|
26
|
+
avoids that scenario. Fixes #18, thanks [@flori](https://github.com/flori).
|
27
|
+
|
28
|
+
## 2.0.1 (January 14, 2020) [☰](https://github.com/philnash/pwned/compare/v2.0.0...v2.0.1)
|
29
|
+
|
30
|
+
- Minor updates
|
31
|
+
|
32
|
+
- Adds double-splat to ActiveModel::Errors#add calls with options to make Ruby 2.7 happy.
|
33
|
+
- Detects presence of Net::HTTPClientException in tests to remove deprecation warning.
|
34
|
+
|
35
|
+
## 2.0.0 (October 1, 2019) [☰](https://github.com/philnash/pwned/compare/v1.2.1...v2.0.0)
|
36
|
+
|
37
|
+
- Major updates
|
38
|
+
|
39
|
+
- Switches from `open-uri` to `Net::HTTP`. This is a potentially breaking change.
|
40
|
+
- `request_options` are now used to configure `Net::HTTP.start`.
|
41
|
+
- Rather than using all string keys from `request_options`, HTTP headers are now
|
42
|
+
specified in their own `headers` hash. To upgrade, any options intended as
|
43
|
+
headers need to be extracted into a `headers` hash, e.g.
|
44
|
+
|
45
|
+
```diff
|
46
|
+
validates :password, not_pwned: {
|
47
|
+
- request_options: { read_timeout: 5, open_timeout: 1, "User-Agent" => "Super fun user agent" }
|
48
|
+
+ request_options: { read_timeout: 5, open_timeout: 1, headers: { "User-Agent" => "Super fun user agent" } }
|
49
|
+
}
|
50
|
+
|
51
|
+
- password = Pwned::Password.new("password", 'User-Agent' => 'Super fun new user agent')
|
52
|
+
+ password = Pwned::Password.new("password", headers: { 'User-Agent' => 'Super fun new user agent' }, read_timeout: 10)
|
53
|
+
```
|
54
|
+
|
55
|
+
- Adds a CLI to let you check passwords on the command line
|
56
|
+
|
57
|
+
```bash
|
58
|
+
$ pwned password
|
59
|
+
Pwned!
|
60
|
+
The password has been found in public breaches 3730471 times.
|
61
|
+
```
|
62
|
+
|
63
|
+
## 1.2.1 (March 17, 2018) [☰](https://github.com/philnash/pwned/compare/v1.2.0...v1.2.1)
|
64
|
+
|
65
|
+
- Minor updates
|
66
|
+
- Validator no longer raises `TypeError` when password is `nil`
|
67
|
+
|
68
|
+
## 1.2.0 (March 15, 2018) [☰](https://github.com/philnash/pwned/compare/v1.1.0...v1.2.0)
|
69
|
+
|
70
|
+
- Major updates
|
71
|
+
- Changes `PwnedValidator` to `NotPwnedValidator`, so that the validation looks like `validates :password, not_pwned: true`. `PwnedValidator` now subclasses `NotPwnedValidator` for backwards compatibility with version 1.1.0 but is deprecated.
|
72
|
+
|
73
|
+
## 1.1.0 (March 12, 2018) [☰](https://github.com/philnash/pwned/compare/v1.0.0...v1.1.0)
|
74
|
+
|
75
|
+
- Major updates
|
76
|
+
|
77
|
+
- Refactors exception handling with built in Ruby method ([PR #1](https://github.com/philnash/pwned/pull/1) thanks [@kpumuk](https://github.com/kpumuk))
|
78
|
+
- Passwords must be strings, the initializer will raise a `TypeError` unless `password.is_a? String`. ([dbf7697](https://github.com/philnash/pwned/commit/dbf7697e878d87ac74aed1e715cee19b73473369))
|
79
|
+
- Added Ruby on Rails validator ([PR #3](https://github.com/philnash/pwned/pull/3) & [PR #6](https://github.com/philnash/pwned/pull/6))
|
80
|
+
- Added simplified accessors `Pwned.pwned?` and `Pwned.pwned_count` ([PR #4](https://github.com/philnash/pwned/pull/4))
|
81
|
+
|
82
|
+
- Minor updates
|
83
|
+
- SHA1 is only calculated once
|
84
|
+
- Frozen string literal to make sure Ruby does not copy strings over and over again
|
85
|
+
- Removal of `@match_data`, since we only use it to retrieve the counter. Caching the counter instead (all [PR #2](https://github.com/philnash/pwned/pull/2) thanks [@kpumuk](https://github.com/kpumuk))
|
29
86
|
|
30
87
|
## 1.0.0 (March 6, 2018) [☰](https://github.com/philnash/pwned/commits/v1.0.0)
|
31
88
|
|
data/README.md
CHANGED
@@ -2,9 +2,34 @@
|
|
2
2
|
|
3
3
|
An easy, Ruby way to use the Pwned Passwords API.
|
4
4
|
|
5
|
-
[![Gem Version](https://badge.fury.io/rb/pwned.svg)](https://rubygems.org/gems/pwned)
|
6
|
-
|
7
|
-
[API docs](https://
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/pwned.svg)](https://rubygems.org/gems/pwned) ![Build Status](https://github.com/philnash/pwned/workflows/tests/badge.svg) [![Maintainability](https://codeclimate.com/github/philnash/pwned/badges/gpa.svg)](https://codeclimate.com/github/philnash/pwned/maintainability) [![Inline docs](https://inch-ci.org/github/philnash/pwned.svg?branch=master)](https://inch-ci.org/github/philnash/pwned)
|
6
|
+
|
7
|
+
[API docs](https://www.rubydoc.info/gems/pwned) | [GitHub repo](https://github.com/philnash/pwned)
|
8
|
+
|
9
|
+
## Table of Contents
|
10
|
+
|
11
|
+
- [Pwned](#pwned)
|
12
|
+
- [Table of Contents](#table-of-contents)
|
13
|
+
- [About](#about)
|
14
|
+
- [Installation](#installation)
|
15
|
+
- [Usage](#usage)
|
16
|
+
- [Plain Ruby](#plain-ruby)
|
17
|
+
- [Advanced](#advanced)
|
18
|
+
- [ActiveRecord Validator](#activerecord-validator)
|
19
|
+
- [I18n](#i18n)
|
20
|
+
- [Threshold](#threshold)
|
21
|
+
- [Network Error Handling](#network-error-handling)
|
22
|
+
- [Custom Request Options](#custom-request-options)
|
23
|
+
- [Using Asynchronously](#using-asynchronously)
|
24
|
+
- [Devise](#devise)
|
25
|
+
- [Rodauth](#rodauth)
|
26
|
+
- [Command line](#command-line)
|
27
|
+
- [Unpwn](#unpwn)
|
28
|
+
- [How Pwned is Pi?](#how-pwned-is-pi)
|
29
|
+
- [Development](#development)
|
30
|
+
- [Contributing](#contributing)
|
31
|
+
- [License](#license)
|
32
|
+
- [Code of Conduct](#code-of-conduct)
|
8
33
|
|
9
34
|
## About
|
10
35
|
|
@@ -14,6 +39,8 @@ Troy Hunt's [Pwned Passwords API V2](https://haveibeenpwned.com/API/v2#PwnedPass
|
|
14
39
|
|
15
40
|
The data from this API is provided by [Have I been pwned?](https://haveibeenpwned.com/). Before using the API, please check [the acceptable uses and license of the API](https://haveibeenpwned.com/API/v2#AcceptableUse).
|
16
41
|
|
42
|
+
Here is a blog post I wrote on [how to use this gem in your Ruby applications to make your users' passwords better](https://www.twilio.com/blog/2018/03/better-passwords-in-ruby-applications-pwned-passwords-api.html).
|
43
|
+
|
17
44
|
## Installation
|
18
45
|
|
19
46
|
Add this line to your application's Gemfile:
|
@@ -32,6 +59,14 @@ Or install it yourself as:
|
|
32
59
|
|
33
60
|
## Usage
|
34
61
|
|
62
|
+
There are a few ways you can use this gem:
|
63
|
+
|
64
|
+
1. [Plain Ruby](#plain-ruby)
|
65
|
+
2. [Rails](#activerecord-validator)
|
66
|
+
3. [Rails and Devise](#devise)
|
67
|
+
|
68
|
+
### Plain Ruby
|
69
|
+
|
35
70
|
To test a password against the API, instantiate a `Pwned::Password` object and then ask if it is `pwned?`.
|
36
71
|
|
37
72
|
```ruby
|
@@ -72,10 +107,11 @@ Pwned.pwned_count("password")
|
|
72
107
|
|
73
108
|
#### Advanced
|
74
109
|
|
75
|
-
You can set
|
110
|
+
You can set http request options to be used with `Net::HTTP.start` when making the request to the API. These options are
|
111
|
+
documented in the [`Net::HTTP.start` documentation](http://ruby-doc.org/stdlib-2.6.3/libdoc/net/http/rdoc/Net/HTTP.html#method-c-start). The `:headers` option defines defines HTTP headers. These headers must be string keys.
|
76
112
|
|
77
113
|
```ruby
|
78
|
-
password = Pwned::Password.new("password", { 'User-Agent' => 'Super fun new user agent' })
|
114
|
+
password = Pwned::Password.new("password", headers: { 'User-Agent' => 'Super fun new user agent' }, read_timeout: 10)
|
79
115
|
```
|
80
116
|
|
81
117
|
### ActiveRecord Validator
|
@@ -113,7 +149,7 @@ class User < ApplicationRecord
|
|
113
149
|
end
|
114
150
|
```
|
115
151
|
|
116
|
-
#### Network
|
152
|
+
#### Network Error Handling
|
117
153
|
|
118
154
|
By default the record will be treated as valid when we cannot reach the [haveibeenpwned.com](https://haveibeenpwned.com/) servers. This can be changed with the `:on_error` validator parameter:
|
119
155
|
|
@@ -145,17 +181,124 @@ end
|
|
145
181
|
|
146
182
|
#### Custom Request Options
|
147
183
|
|
148
|
-
You can configure network requests made from the validator using `:request_options` (see [
|
184
|
+
You can configure network requests made from the validator using `:request_options` (see [Net::HTTP.start](http://ruby-doc.org/stdlib-2.6.3/libdoc/net/http/rdoc/Net/HTTP.html#method-c-start) for the list of available options).
|
185
|
+
In addition to these options, HTTP headers can be specified with the `:headers` key (e.g. `"User-Agent"`) and proxy can be specified with the `:proxy` key:
|
149
186
|
|
150
187
|
```ruby
|
151
188
|
validates :password, not_pwned: {
|
152
|
-
request_options: {
|
189
|
+
request_options: {
|
190
|
+
read_timeout: 5,
|
191
|
+
open_timeout: 1,
|
192
|
+
headers: { "User-Agent" => "Super fun user agent" },
|
193
|
+
proxy: "https://username:password@example.com:12345"
|
194
|
+
}
|
153
195
|
}
|
154
196
|
```
|
155
197
|
|
156
|
-
|
198
|
+
### Using Asynchronously
|
199
|
+
|
200
|
+
You may have a use case for hashing the password in advance, and then making the call to the Pwned Passwords API later (for example if you want to enqueue a job without storing the plaintext password). To do this, you can hash the password with the `Pwned.hash_password` method and then initialize the `Pwned::HashPassword` class with the hash, like this:
|
201
|
+
|
202
|
+
```ruby
|
203
|
+
hashed_password = Pwned.hash_password(password)
|
204
|
+
# some time later
|
205
|
+
Pwned::HashPassword.new(hashed_password, request_options).pwned?
|
206
|
+
```
|
207
|
+
|
208
|
+
### Devise
|
209
|
+
|
210
|
+
If you are using [Devise](https://github.com/heartcombo/devise) I recommend you use the [devise-pwned_password extension](https://github.com/michaelbanfield/devise-pwned_password) which is now powered by this gem.
|
211
|
+
|
212
|
+
### Rodauth
|
213
|
+
|
214
|
+
If you are using [Rodauth](https://github.com/jeremyevans/rodauth) then you can use the [rodauth-pwned](https://github.com/janko/rodauth-pwned) feature which is powered by this gem.
|
215
|
+
|
216
|
+
### Command line
|
217
|
+
|
218
|
+
The gem provides a command line utility for checking passwords. You can call it from your terminal application like this:
|
219
|
+
|
220
|
+
```bash
|
221
|
+
$ pwned password
|
222
|
+
Pwned!
|
223
|
+
The password has been found in public breaches 3645804 times.
|
224
|
+
```
|
225
|
+
|
226
|
+
If you don't want the password you are checking to be visible, call:
|
157
227
|
|
158
|
-
|
228
|
+
```bash
|
229
|
+
$ pwned --secret
|
230
|
+
```
|
231
|
+
|
232
|
+
You will be prompted for the password, but it won't be displayed.
|
233
|
+
|
234
|
+
### Unpwn
|
235
|
+
|
236
|
+
To cut down on unnecessary network requests, [the unpwn project](https://github.com/indirect/unpwn) uses a list of the top one million passwords to check passwords against. Only if a password is not included in the top million is it then checked against the Pwned Passwords API.
|
237
|
+
|
238
|
+
## How Pwned is Pi?
|
239
|
+
|
240
|
+
[@daz](https://github.com/daz) [shared](https://twitter.com/dazonic/status/1074647842046660609) a fantastic example of using this gem to show how many times the digits of Pi have been used as passwords and leaked.
|
241
|
+
|
242
|
+
```ruby
|
243
|
+
require 'pwned'
|
244
|
+
|
245
|
+
PI = '3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111'
|
246
|
+
|
247
|
+
for n in 1..40
|
248
|
+
password = Pwned::Password.new PI[0..(n + 1)]
|
249
|
+
str = [ n.to_s.rjust(2) ]
|
250
|
+
str << (password.pwned? ? '😡' : '😃')
|
251
|
+
str << password.pwned_count.to_s.rjust(4)
|
252
|
+
str << password.password
|
253
|
+
|
254
|
+
puts str.join ' '
|
255
|
+
end
|
256
|
+
```
|
257
|
+
|
258
|
+
The results may, or may not, surprise you.
|
259
|
+
|
260
|
+
```
|
261
|
+
1 😡 16 3.1
|
262
|
+
2 😡 238 3.14
|
263
|
+
3 😡 34 3.141
|
264
|
+
4 😡 1345 3.1415
|
265
|
+
5 😡 2552 3.14159
|
266
|
+
6 😡 791 3.141592
|
267
|
+
7 😡 9582 3.1415926
|
268
|
+
8 😡 1591 3.14159265
|
269
|
+
9 😡 637 3.141592653
|
270
|
+
10 😡 873 3.1415926535
|
271
|
+
11 😡 137 3.14159265358
|
272
|
+
12 😡 103 3.141592653589
|
273
|
+
13 😡 65 3.1415926535897
|
274
|
+
14 😡 201 3.14159265358979
|
275
|
+
15 😡 41 3.141592653589793
|
276
|
+
16 😡 57 3.1415926535897932
|
277
|
+
17 😡 28 3.14159265358979323
|
278
|
+
18 😡 29 3.141592653589793238
|
279
|
+
19 😡 1 3.1415926535897932384
|
280
|
+
20 😡 7 3.14159265358979323846
|
281
|
+
21 😡 5 3.141592653589793238462
|
282
|
+
22 😡 2 3.1415926535897932384626
|
283
|
+
23 😡 2 3.14159265358979323846264
|
284
|
+
24 😃 0 3.141592653589793238462643
|
285
|
+
25 😡 3 3.1415926535897932384626433
|
286
|
+
26 😃 0 3.14159265358979323846264338
|
287
|
+
27 😃 0 3.141592653589793238462643383
|
288
|
+
28 😃 0 3.1415926535897932384626433832
|
289
|
+
29 😃 0 3.14159265358979323846264338327
|
290
|
+
30 😃 0 3.141592653589793238462643383279
|
291
|
+
31 😃 0 3.1415926535897932384626433832795
|
292
|
+
32 😃 0 3.14159265358979323846264338327950
|
293
|
+
33 😃 0 3.141592653589793238462643383279502
|
294
|
+
34 😃 0 3.1415926535897932384626433832795028
|
295
|
+
35 😃 0 3.14159265358979323846264338327950288
|
296
|
+
36 😃 0 3.141592653589793238462643383279502884
|
297
|
+
37 😃 0 3.1415926535897932384626433832795028841
|
298
|
+
38 😃 0 3.14159265358979323846264338327950288419
|
299
|
+
39 😃 0 3.141592653589793238462643383279502884197
|
300
|
+
40 😃 0 3.1415926535897932384626433832795028841971
|
301
|
+
```
|
159
302
|
|
160
303
|
## Development
|
161
304
|
|
data/bin/pwned
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "pwned"
|
4
|
+
require "optparse"
|
5
|
+
require "io/console"
|
6
|
+
|
7
|
+
options = {}
|
8
|
+
parser = OptionParser.new do |opts|
|
9
|
+
opts.banner = <<-USAGE
|
10
|
+
Usage: pwned <password>
|
11
|
+
|
12
|
+
Tests a password against the Pwned Passwords API using the k-anonymity model,
|
13
|
+
which avoids sending the entire password to the service.opts
|
14
|
+
|
15
|
+
If the password has been found in a publicly available breach then this tool
|
16
|
+
will report how many times it has been seen. Otherwise the tool will report that
|
17
|
+
the password has not been found in a public breach yet.
|
18
|
+
|
19
|
+
USAGE
|
20
|
+
|
21
|
+
opts.version = Pwned::VERSION
|
22
|
+
|
23
|
+
opts.on("-s", "--secret", "Enter password without displaying characters.\n#{" "* 37}Overrides provided arguments.")
|
24
|
+
opts.on_tail("-h", "--help", "Show help.")
|
25
|
+
opts.on_tail("-v", "--version", "Show version number.\n\n")
|
26
|
+
end
|
27
|
+
|
28
|
+
parser.parse!(ARGV, into: options)
|
29
|
+
|
30
|
+
if options[:help]
|
31
|
+
puts parser.help
|
32
|
+
exit
|
33
|
+
end
|
34
|
+
if options[:version]
|
35
|
+
puts parser.ver
|
36
|
+
exit
|
37
|
+
end
|
38
|
+
password_to_test = ARGV.first
|
39
|
+
if options[:secret]
|
40
|
+
password_to_test = STDIN.getpass("Password: ")
|
41
|
+
end
|
42
|
+
if !password_to_test || password_to_test.strip == ""
|
43
|
+
puts parser.help
|
44
|
+
exit
|
45
|
+
end
|
46
|
+
password = Pwned::Password.new(password_to_test || ARGV.first)
|
47
|
+
if password.pwned?
|
48
|
+
puts "Pwned!\nThe password has been found in public breaches #{password.pwned_count} times."
|
49
|
+
else
|
50
|
+
puts "The password has not been found in a public breach."
|
51
|
+
end
|
52
|
+
|
data/lib/pwned.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "digest"
|
3
4
|
require "pwned/version"
|
4
5
|
require "pwned/error"
|
5
6
|
require "pwned/password"
|
7
|
+
require "pwned/hashed_password"
|
6
8
|
|
7
9
|
begin
|
8
10
|
# Load Rails and our custom validator
|
@@ -29,10 +31,10 @@ module Pwned
|
|
29
31
|
# Pwned.pwned?("pwned::password") #=> false
|
30
32
|
#
|
31
33
|
# @param password [String] The password you want to check against the API.
|
32
|
-
# @param [Hash] request_options Options that can be passed to +
|
34
|
+
# @param [Hash] request_options Options that can be passed to +Net::HTTP.start+ when
|
33
35
|
# calling the API
|
34
|
-
# @option request_options [
|
35
|
-
#
|
36
|
+
# @option request_options [Symbol] :headers ({ "User-Agent" => "Ruby Pwned::Password #{Pwned::VERSION}" })
|
37
|
+
# HTTP headers to include in the request
|
36
38
|
# @return [Boolean] Whether the password appears in the data breaches or not.
|
37
39
|
# @since 1.1.0
|
38
40
|
def self.pwned?(password, request_options={})
|
@@ -47,14 +49,28 @@ module Pwned
|
|
47
49
|
# Pwned.pwned_count("pwned::password") #=> 0
|
48
50
|
#
|
49
51
|
# @param password [String] The password you want to check against the API.
|
50
|
-
# @param [Hash] request_options Options that can be passed to +
|
52
|
+
# @param [Hash] request_options Options that can be passed to +Net::HTTP.start+ when
|
51
53
|
# calling the API
|
52
|
-
# @option request_options [
|
53
|
-
#
|
54
|
+
# @option request_options [Symbol] :headers ({ "User-Agent" => "Ruby Pwned::Password #{Pwned::VERSION}" })
|
55
|
+
# HTTP headers to include in the request
|
54
56
|
# @return [Integer] The number of times the password has appeared in the data
|
55
57
|
# breaches.
|
56
58
|
# @since 1.1.0
|
57
59
|
def self.pwned_count(password, request_options={})
|
58
60
|
Pwned::Password.new(password, request_options).pwned_count
|
59
61
|
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Returns the full SHA1 hash of the given password in uppercase. This can be safely passed around your code
|
65
|
+
# before making the pwned request (e.g. dropped into a queue table).
|
66
|
+
#
|
67
|
+
# @example
|
68
|
+
# Pwned.hash_password("password") #=> 5BAA61E4C9B93F3F0682250B6CF8331B7EE68FD8
|
69
|
+
#
|
70
|
+
# @param password [String] The password you want to check against the API
|
71
|
+
# @return [String] An uppercase SHA1 hash of the password
|
72
|
+
# @since 2.1.0
|
73
|
+
def self.hash_password(password)
|
74
|
+
Digest::SHA1.hexdigest(password).upcase
|
75
|
+
end
|
60
76
|
end
|