pass-station 1.1.0 → 1.2.3
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/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
|