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.
@@ -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 '[-] No result' if @search_result.empty?
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 splitted words and capitalize
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
- username: 9,
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
- min_colsizes.each_with_object({}) { |(k, v), h| h[k] = [v, colsizes[k]].max }
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
@@ -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: +:productvendor+, +:username+, +:password+
26
- # @return [Array<CSV::Row>] table of +CSV::Row+, each row contains three
27
- # attributes: :productvendor, :username, :password
28
- def parse(sort = :productvendor)
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
@@ -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: :productvendor | :username | :password | :all (all columns)
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 three
12
- # attributes: :productvendor, :username, :password
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: :productvendor | :username | :password | :all (all columns)
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
- row[:productvendor].match?(rgxp) || row[:username].match?(rgxp) ||
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 polutate the data attribute first' if @data.nil?
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?
@@ -9,45 +9,82 @@ module PassStation
9
9
  # Password database handling
10
10
  class DB
11
11
  UPSTREAM_DATABASE = {
12
- URL: 'https://raw.githubusercontent.com/ihebski/DefaultCreds-cheat-sheet/main/DefaultCreds-Cheat-Sheet.csv',
13
- HASH: 'de6a9f7e7ac94fbcd142ec5817efb71d3a0027076266c87ead5158a4960ec708'
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 downlaod parameters.
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
- download_file(UPSTREAM_DATABASE[:URL], destination_path, opts)
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
- # Chek if an update is available
60
+ # Check if an update is available
29
61
  # @return [Boolean] +true+ if there is, +false+ else.
30
62
  def check_for_update
31
- file = download_file(UPSTREAM_DATABASE[:URL], Dir.mktmpdir)
32
- # Same hash = no update
33
- !check_hash(file, UPSTREAM_DATABASE[:HASH])
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 downlaod parameters.
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 = uri.path.split('/').last
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Version
4
- VERSION = '1.1.0'
4
+ VERSION = '1.2.3'
5
5
  end
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>] pasword database
28
+ # @return [Array<CSV::Row>] password database
27
29
  attr_reader :data
28
30
 
29
31
  # A new instance of Pass Station
30
- def initialize
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
- @database_name = 'DefaultCreds-Cheat-Sheet.csv'
33
- @database_path = @storage_location + @database_name
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.1.0
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-03-15 00:00:00.000000000 Z
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: '3.0'
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: '3.0'
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://sec-it.github.io/pass-station/
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/sec-it/pass-station/issues
184
- changelog_uri: https://github.com/sec-it/pass-station/blob/master/docs/CHANGELOG.md
185
- documentation_uri: https://sec-it.github.io/pass-station/yard/
186
- homepage_uri: https://sec-it.github.io/pass-station/
187
- source_code_uri: https://github.com/sec-it/pass-station/
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.0'
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.1.4
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