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.
@@ -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