pass-station 1.1.0 → 1.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +2 -1
- data/bin/pass-station +31 -23
- data/data/DefaultCreds-Cheat-Sheet.csv +17 -8
- data/data/many-passwords.csv +2561 -0
- data/lib/pass_station/output.rb +7 -8
- data/lib/pass_station/parse.rb +5 -4
- data/lib/pass_station/search.rb +9 -7
- data/lib/pass_station/source.rb +48 -11
- data/lib/pass_station/version.rb +1 -1
- data/lib/pass_station.rb +18 -5
- metadata +27 -12
data/lib/pass_station/output.rb
CHANGED
@@ -32,7 +32,7 @@ module PassStation
|
|
32
32
|
# @param formatter [String] Engine to use to format the data: +table+, +'pretty-table'+, +JSON+, +CSV+, +YAML+
|
33
33
|
# @return [Array<String>] formatted output
|
34
34
|
def output_search(formatter)
|
35
|
-
return
|
35
|
+
return [] if @search_result.empty?
|
36
36
|
|
37
37
|
output(formatter, @search_result)
|
38
38
|
end
|
@@ -52,7 +52,7 @@ module PassStation
|
|
52
52
|
end
|
53
53
|
|
54
54
|
# Normalize string to be class name compatible
|
55
|
-
# Join
|
55
|
+
# Join split words and capitalize
|
56
56
|
# @param formatter [String] formatter name
|
57
57
|
# @return [String] normalized name (class compatible)
|
58
58
|
def normalize(formatter)
|
@@ -105,11 +105,10 @@ module PassStation
|
|
105
105
|
# @return [Hash] fixed colsizes, keys are columns name, values are columns size
|
106
106
|
def correct_min_colsizes(colsizes)
|
107
107
|
min_colsizes = {
|
108
|
-
productvendor: 14,
|
109
|
-
|
110
|
-
password: 9
|
108
|
+
productvendor: 14, username: 9, password: 9, modelsoftware_name: 19,
|
109
|
+
version: 8, access_type: 12, privileges: 11, notes: 6, vendor: 7
|
111
110
|
}
|
112
|
-
|
111
|
+
colsizes.each_with_object({}) { |(k, v), h| h[k] = [v, min_colsizes[k]].max }
|
113
112
|
end
|
114
113
|
|
115
114
|
# Left justify an element of the column
|
@@ -220,7 +219,7 @@ module PassStation
|
|
220
219
|
# @param table [Array<CSV::Row>] an +Array<CSV::Row>+
|
221
220
|
# @return [Array<String>] the formatted JSON ready to be printed (only
|
222
221
|
# one element on the array, keep an array for compatibility with
|
223
|
-
# {highlight_found} and homogeneity with other formatters)
|
222
|
+
# {DB.highlight_found} and homogeneity with other formatters)
|
224
223
|
def format(table)
|
225
224
|
[table.map(&:to_h).to_json]
|
226
225
|
end
|
@@ -234,7 +233,7 @@ module PassStation
|
|
234
233
|
# @param table [Array<CSV::Row>] an +Array<CSV::Row>+
|
235
234
|
# @return [Array<String>] the formatted YAML ready to be printed (only
|
236
235
|
# one element on the array, keep an array for compatibility with
|
237
|
-
# {highlight_found} and homogeneity with other formatters)
|
236
|
+
# {DB.highlight_found} and homogeneity with other formatters)
|
238
237
|
def format(table)
|
239
238
|
[table.map(&:to_h).to_yaml]
|
240
239
|
end
|
data/lib/pass_station/parse.rb
CHANGED
@@ -22,10 +22,11 @@ module PassStation
|
|
22
22
|
end
|
23
23
|
|
24
24
|
# Parse, sort and sanitize the password database
|
25
|
-
# @param sort [Symbol] column name to sort by
|
26
|
-
# @return [Array<CSV::Row>] table of +CSV::Row+, each row contains
|
27
|
-
# attributes
|
28
|
-
def parse(sort =
|
25
|
+
# @param sort [Symbol] column name to sort by (columns depends on the database source, see {UPSTREAM_DATABASE})
|
26
|
+
# @return [Array<CSV::Row>] table of +CSV::Row+, each row contains multiple
|
27
|
+
# attributes (columns depends on the database source, see {UPSTREAM_DATABASE})
|
28
|
+
def parse(sort = nil)
|
29
|
+
sort ||= UPSTREAM_DATABASE[@database_type][:COLUMNS].first[0]
|
29
30
|
@data = CSV.table(@database_path, **@config).sort_by do |s|
|
30
31
|
s[sort].downcase
|
31
32
|
end
|
data/lib/pass_station/search.rb
CHANGED
@@ -6,11 +6,13 @@ module PassStation
|
|
6
6
|
class DB
|
7
7
|
# Search term in the data table
|
8
8
|
# @param term [String] the searched term
|
9
|
-
# @param col [Symbol] the column to search in:
|
9
|
+
# @param col [Symbol] the column to search in: column name (columns depends on the database source, see
|
10
|
+
# {UPSTREAM_DATABASE}) or :all (all columns)
|
10
11
|
# @see build_regexp for +opts+ param description
|
11
|
-
# @return [Array<CSV::Row>] table of +CSV::Row+, each row contains
|
12
|
-
# attributes
|
12
|
+
# @return [Array<CSV::Row>] table of +CSV::Row+, each row contains multiple
|
13
|
+
# attributes (columns depends on the database source, see {UPSTREAM_DATABASE})
|
13
14
|
def search(term, col, opts = {})
|
15
|
+
col ||= UPSTREAM_DATABASE[@database_type][:COLUMNS].first[0]
|
14
16
|
r1 = prepare_search(term, opts)
|
15
17
|
condition = column_selector(col, r1)
|
16
18
|
@data.each do |row|
|
@@ -21,14 +23,14 @@ module PassStation
|
|
21
23
|
|
22
24
|
# Choose in which column the search will be performed and build the
|
23
25
|
# condition to use
|
24
|
-
# @param col [Symbol] the column to search in:
|
26
|
+
# @param col [Symbol] the column to search in: column name (columns depends on the database source, see
|
27
|
+
# {UPSTREAM_DATABASE}) or :all (all columns)
|
25
28
|
# @param rgxp [Regexp] the search regexp (generated by {build_regexp})
|
26
29
|
# @return [Proc] the proc condition to use for searching
|
27
30
|
def column_selector(col, rgxp)
|
28
31
|
proc { |row|
|
29
32
|
if col == :all
|
30
|
-
|
31
|
-
row[:password].match?(rgxp)
|
33
|
+
UPSTREAM_DATABASE[@database_type][:COLUMNS].keys.map { |x| row[x].match?(rgxp) }.inject(:|)
|
32
34
|
else
|
33
35
|
row[col].match?(rgxp)
|
34
36
|
end
|
@@ -62,7 +64,7 @@ module PassStation
|
|
62
64
|
|
63
65
|
# Raise an error is data attribute is nil
|
64
66
|
def data_nil?
|
65
|
-
raise 'You must use the parse method to
|
67
|
+
raise 'You must use the parse method to populate the data attribute first' if @data.nil?
|
66
68
|
end
|
67
69
|
|
68
70
|
protected :build_regexp, :prepare_search, :column_selector, :data_nil?
|
data/lib/pass_station/source.rb
CHANGED
@@ -9,45 +9,82 @@ module PassStation
|
|
9
9
|
# Password database handling
|
10
10
|
class DB
|
11
11
|
UPSTREAM_DATABASE = {
|
12
|
-
|
13
|
-
|
12
|
+
MAPPING: {
|
13
|
+
1 => :DEFAULT_CREDENTIALS_CHEAT_SHEET,
|
14
|
+
2 => :MANY_PASSWORDS
|
15
|
+
},
|
16
|
+
DEFAULT_CREDENTIALS_CHEAT_SHEET: {
|
17
|
+
URL: 'https://raw.githubusercontent.com/ihebski/DefaultCreds-cheat-sheet/main/DefaultCreds-Cheat-Sheet.csv',
|
18
|
+
HASH: 'f03f3ed77a8a932b1b2891fbec705d42b1eec4911fb76ccf36cde9e79a385556',
|
19
|
+
FILENAME: 'DefaultCreds-Cheat-Sheet.csv',
|
20
|
+
COLUMNS: {
|
21
|
+
productvendor: 'Product/Vendor',
|
22
|
+
username: 'Username',
|
23
|
+
password: 'Password'
|
24
|
+
}
|
25
|
+
},
|
26
|
+
MANY_PASSWORDS: {
|
27
|
+
URL: 'https://raw.githubusercontent.com/many-passwords/many-passwords/main/passwords.csv',
|
28
|
+
HASH: '293ce4411446c702aeda977b9a446ff42d045d980be0b5287a848b5bd7d39402',
|
29
|
+
FILENAME: 'many-passwords.csv',
|
30
|
+
COLUMNS: {
|
31
|
+
vendor: 'Vendor',
|
32
|
+
modelsoftware_name: 'Model/Software name',
|
33
|
+
version: 'Version',
|
34
|
+
access_type: 'Access Type',
|
35
|
+
username: 'Username',
|
36
|
+
password: 'Password',
|
37
|
+
privileges: 'Privileges',
|
38
|
+
notes: 'Notes'
|
39
|
+
}
|
40
|
+
}
|
14
41
|
}.freeze
|
15
42
|
|
16
43
|
class << self
|
17
44
|
# Download upstream password database
|
18
45
|
# @param destination_path [String] the destination path (may
|
19
46
|
# overwrite existing file).
|
20
|
-
# @param opts [Hash] the optional
|
47
|
+
# @param opts [Hash] the optional download parameters.
|
21
48
|
# @option opts [String] :sha256 the SHA256 hash to check, if the file
|
22
49
|
# already exist and the hash matches then the download will be skipped.
|
50
|
+
# @option opts [String] :source_db id of the source database (see. MAPPING in {UPSTREAM_DATABASE}). Default is 1.
|
23
51
|
# @return [String|nil] the saved file path.
|
24
52
|
def download_upstream(destination_path, opts = {})
|
25
|
-
|
53
|
+
opts[:source_db] ||= 1
|
54
|
+
source_db = UPSTREAM_DATABASE[:MAPPING][opts[:source_db]]
|
55
|
+
opts[:filename] = UPSTREAM_DATABASE[source_db][:FILENAME]
|
56
|
+
source_url = UPSTREAM_DATABASE[source_db][:URL]
|
57
|
+
download_file(source_url, destination_path, opts)
|
26
58
|
end
|
27
59
|
|
28
|
-
#
|
60
|
+
# Check if an update is available
|
29
61
|
# @return [Boolean] +true+ if there is, +false+ else.
|
30
62
|
def check_for_update
|
31
|
-
|
32
|
-
|
33
|
-
|
63
|
+
ret_vals = []
|
64
|
+
UPSTREAM_DATABASE[:MAPPING].each do |_k, v|
|
65
|
+
file = download_file(UPSTREAM_DATABASE[v][:URL], Dir.mktmpdir)
|
66
|
+
# Same hash = no update
|
67
|
+
ret_vals << !check_hash(file, UPSTREAM_DATABASE[v][:HASH])
|
68
|
+
end
|
69
|
+
ret_vals.inject(:|) # logical OR, there is an update if at least one entry needs one
|
34
70
|
end
|
35
71
|
|
36
72
|
# Download a file.
|
37
73
|
# @param file_url [String] the URL of the file.
|
38
74
|
# @param destination_path [String] the destination path (may
|
39
75
|
# overwrite existing file).
|
40
|
-
# @param opts [Hash] the optional
|
76
|
+
# @param opts [Hash] the optional download parameters.
|
41
77
|
# @option opts [String] :sha256 the SHA256 hash to check, if the file
|
42
78
|
# already exist and the hash matches then the download will be skipped.
|
79
|
+
# @option opts [String] :filename override upstream filename
|
43
80
|
# @return [String|nil] the saved file path.
|
44
81
|
def download_file(file_url, destination_path, opts = {})
|
45
82
|
opts[:sha256] ||= nil
|
46
83
|
|
47
84
|
destination_path += '/' unless destination_path[-1] == '/'
|
48
85
|
uri = URI(file_url)
|
49
|
-
filename
|
50
|
-
destination_file = destination_path + filename
|
86
|
+
opts[:filename] ||= uri.path.split('/').last
|
87
|
+
destination_file = destination_path + opts[:filename]
|
51
88
|
# Verify hash to see if it is the latest
|
52
89
|
skip_download = check_hash(destination_file, opts[:sha256])
|
53
90
|
write_file(destination_file, fetch_file(uri)) unless skip_download
|
data/lib/pass_station/version.rb
CHANGED
data/lib/pass_station.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# Ruby internal
|
4
|
+
require 'pathname'
|
3
5
|
# Project internal
|
4
6
|
require 'pass_station/source'
|
5
7
|
require 'pass_station/parse'
|
@@ -23,14 +25,17 @@ module PassStation
|
|
23
25
|
attr_accessor :database_name
|
24
26
|
|
25
27
|
# Get the password database in +Array<CSV::Row>+ format
|
26
|
-
# @return [Array<CSV::Row>]
|
28
|
+
# @return [Array<CSV::Row>] password database
|
27
29
|
attr_reader :data
|
28
30
|
|
29
31
|
# A new instance of Pass Station
|
30
|
-
|
32
|
+
# @param db [Integer] the credentials source database id (see {UPSTREAM_DATABASE})
|
33
|
+
def initialize(db = nil)
|
34
|
+
db ||= 1
|
31
35
|
@storage_location = 'data/'
|
32
|
-
@
|
33
|
-
@
|
36
|
+
@database_type = UPSTREAM_DATABASE[:MAPPING][db]
|
37
|
+
@database_name = UPSTREAM_DATABASE[@database_type][:FILENAME]
|
38
|
+
@database_path = absolute_db_path
|
34
39
|
database_exists?
|
35
40
|
@config = {}
|
36
41
|
csv_config
|
@@ -38,6 +43,14 @@ module PassStation
|
|
38
43
|
@search_result = []
|
39
44
|
end
|
40
45
|
|
46
|
+
# Find the absolute path of the DB from its relative location
|
47
|
+
# @return [String] absolute filename of the DB
|
48
|
+
def absolute_db_path
|
49
|
+
pn = Pathname.new(__FILE__)
|
50
|
+
install_dir = pn.dirname.parent.to_s + Pathname::SEPARATOR_LIST
|
51
|
+
install_dir + @storage_location + @database_name
|
52
|
+
end
|
53
|
+
|
41
54
|
# Check if the password database exists
|
42
55
|
# @return [Boolean] +true+ if the file exists
|
43
56
|
def database_exists?
|
@@ -47,6 +60,6 @@ module PassStation
|
|
47
60
|
exists
|
48
61
|
end
|
49
62
|
|
50
|
-
protected :database_exists
|
63
|
+
protected :database_exists?, :absolute_db_path
|
51
64
|
end
|
52
65
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pass-station
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexandre ZANNI
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-11-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: docopt
|
@@ -78,14 +78,14 @@ dependencies:
|
|
78
78
|
requirements:
|
79
79
|
- - "~>"
|
80
80
|
- !ruby/object:Gem::Version
|
81
|
-
version: '
|
81
|
+
version: '4.0'
|
82
82
|
type: :development
|
83
83
|
prerelease: false
|
84
84
|
version_requirements: !ruby/object:Gem::Requirement
|
85
85
|
requirements:
|
86
86
|
- - "~>"
|
87
87
|
- !ruby/object:Gem::Version
|
88
|
-
version: '
|
88
|
+
version: '4.0'
|
89
89
|
- !ruby/object:Gem::Dependency
|
90
90
|
name: minitest
|
91
91
|
requirement: !ruby/object:Gem::Requirement
|
@@ -142,6 +142,20 @@ dependencies:
|
|
142
142
|
- - "~>"
|
143
143
|
- !ruby/object:Gem::Version
|
144
144
|
version: '1.10'
|
145
|
+
- !ruby/object:Gem::Dependency
|
146
|
+
name: webrick
|
147
|
+
requirement: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - "~>"
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '1.7'
|
152
|
+
type: :development
|
153
|
+
prerelease: false
|
154
|
+
version_requirements: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - "~>"
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '1.7'
|
145
159
|
- !ruby/object:Gem::Dependency
|
146
160
|
name: yard
|
147
161
|
requirement: !ruby/object:Gem::Requirement
|
@@ -169,22 +183,23 @@ files:
|
|
169
183
|
- bin/pass-station
|
170
184
|
- bin/pass-station_console
|
171
185
|
- data/DefaultCreds-Cheat-Sheet.csv
|
186
|
+
- data/many-passwords.csv
|
172
187
|
- lib/pass_station.rb
|
173
188
|
- lib/pass_station/output.rb
|
174
189
|
- lib/pass_station/parse.rb
|
175
190
|
- lib/pass_station/search.rb
|
176
191
|
- lib/pass_station/source.rb
|
177
192
|
- lib/pass_station/version.rb
|
178
|
-
homepage: https://
|
193
|
+
homepage: https://noraj.github.io/pass-station/
|
179
194
|
licenses:
|
180
195
|
- MIT
|
181
196
|
metadata:
|
182
197
|
yard.run: yard
|
183
|
-
bug_tracker_uri: https://github.com/
|
184
|
-
changelog_uri: https://github.com/
|
185
|
-
documentation_uri: https://
|
186
|
-
homepage_uri: https://
|
187
|
-
source_code_uri: https://github.com/
|
198
|
+
bug_tracker_uri: https://github.com/noraj/pass-station/issues
|
199
|
+
changelog_uri: https://github.com/noraj/pass-station/blob/master/docs/CHANGELOG.md
|
200
|
+
documentation_uri: https://noraj.github.io/pass-station/yard/
|
201
|
+
homepage_uri: https://noraj.github.io/pass-station/
|
202
|
+
source_code_uri: https://github.com/noraj/pass-station/
|
188
203
|
post_install_message:
|
189
204
|
rdoc_options: []
|
190
205
|
require_paths:
|
@@ -196,14 +211,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
196
211
|
version: 2.6.0
|
197
212
|
- - "<"
|
198
213
|
- !ruby/object:Gem::Version
|
199
|
-
version: '3.
|
214
|
+
version: '3.1'
|
200
215
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
201
216
|
requirements:
|
202
217
|
- - ">="
|
203
218
|
- !ruby/object:Gem::Version
|
204
219
|
version: '0'
|
205
220
|
requirements: []
|
206
|
-
rubygems_version: 3.
|
221
|
+
rubygems_version: 3.2.22
|
207
222
|
signing_key:
|
208
223
|
specification_version: 4
|
209
224
|
summary: CLI & library to search for default credentials among thousands of Products
|