Class: PassStation::DB

Inherits:
Object
  • Object
show all
Defined in:
lib/pass_station.rb,
lib/pass_station/parse.rb,
lib/pass_station/output.rb,
lib/pass_station/search.rb,
lib/pass_station/source.rb

Overview

Password database handling

Constant Summary collapse

UPSTREAM_DATABASE =
{
  MAPPING: {
    1 => :DEFAULT_CREDENTIALS_CHEAT_SHEET,
    2 => :MANY_PASSWORDS
  },
  DEFAULT_CREDENTIALS_CHEAT_SHEET: {
    URL: 'https://raw.githubusercontent.com/ihebski/DefaultCreds-cheat-sheet/main/DefaultCreds-Cheat-Sheet.csv',
    HASH: 'f03f3ed77a8a932b1b2891fbec705d42b1eec4911fb76ccf36cde9e79a385556',
    FILENAME: 'DefaultCreds-Cheat-Sheet.csv',
    COLUMNS: {
      productvendor: 'Product/Vendor',
      username: 'Username',
      password: 'Password'
    }
  },
  MANY_PASSWORDS: {
    URL: 'https://raw.githubusercontent.com/many-passwords/many-passwords/main/passwords.csv',
    HASH: '293ce4411446c702aeda977b9a446ff42d045d980be0b5287a848b5bd7d39402',
    FILENAME: 'many-passwords.csv',
    COLUMNS: {
      vendor: 'Vendor',
      modelsoftware_name: 'Model/Software name',
      version: 'Version',
      access_type: 'Access Type',
      username: 'Username',
      password: 'Password',
      privileges: 'Privileges',
      notes: 'Notes'
    }
  }
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(db = nil) ⇒ DB

A new instance of Pass Station

Parameters:

  • db (Integer) (defaults to: nil)

    the credentials source database id (see UPSTREAM_DATABASE)



33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/pass_station.rb', line 33

def initialize(db = nil)
  db ||= 1
  @storage_location = 'data/'
  @database_type = UPSTREAM_DATABASE[:MAPPING][db]
  @database_name = UPSTREAM_DATABASE[@database_type][:FILENAME]
  @database_path = absolute_db_path
  database_exists?
  @config = {}
  csv_config
  @data = nil
  @search_result = []
end

Instance Attribute Details

#dataArray<CSV::Row> (readonly)

Get the password database in Array<CSV::Row> format

Returns:

  • (Array<CSV::Row>)

    password database



29
30
31
# File 'lib/pass_station.rb', line 29

def data
  @data
end

#database_nameString

Get / set the password database name

Returns:

  • (String)

    password database filename. Default to DefaultCreds-Cheat-Sheet.csv.



25
26
27
# File 'lib/pass_station.rb', line 25

def database_name
  @database_name
end

#storage_locationString

Get / set storage location, where will be stored the password database.

Returns:

  • (String)

    database storage location. Default to data/.



20
21
22
# File 'lib/pass_station.rb', line 20

def storage_location
  @storage_location
end

Class Method Details

.check_for_updateBoolean

Check if an update is available

Returns:

  • (Boolean)

    true if there is, false else.



62
63
64
65
66
67
68
69
70
# File 'lib/pass_station/source.rb', line 62

def check_for_update
  ret_vals = []
  UPSTREAM_DATABASE[:MAPPING].each do |_k, v|
    file = download_file(UPSTREAM_DATABASE[v][:URL], Dir.mktmpdir)
    # Same hash = no update
    ret_vals << !check_hash(file, UPSTREAM_DATABASE[v][:HASH])
  end
  ret_vals.inject(:|) # logical OR, there is an update if at least one entry needs one
end

.check_hash(file, hash) ⇒ Boolean

Check if a file match a SHA256 hash

Parameters:

  • file (String)

    the path of the file.

  • hash (String)

    tha SHA256 hash to check against.

Returns:

  • (Boolean)

    if the hash of the file matched the one provided (true) or not (false).



98
99
100
101
102
103
104
105
# File 'lib/pass_station/source.rb', line 98

def check_hash(file, hash)
  if !hash.nil? && File.file?(file)
    computed_h = Digest::SHA256.file(file)
    true if hash.casecmp(computed_h.hexdigest).zero?
  else
    false
  end
end

.download_file(file_url, destination_path, opts = {}) ⇒ String|nil

Download a file.

Parameters:

  • file_url (String)

    the URL of the file.

  • destination_path (String)

    the destination path (may overwrite existing file).

  • opts (Hash) (defaults to: {})

    the optional download parameters.

Options Hash (opts):

  • :sha256 (String)

    the SHA256 hash to check, if the file already exist and the hash matches then the download will be skipped.

  • :filename (String)

    override upstream filename

Returns:

  • (String|nil)

    the saved file path.



81
82
83
84
85
86
87
88
89
90
91
# File 'lib/pass_station/source.rb', line 81

def download_file(file_url, destination_path, opts = {})
  opts[:sha256] ||= nil

  destination_path += '/' unless destination_path[-1] == '/'
  uri = URI(file_url)
  opts[:filename] ||= uri.path.split('/').last
  destination_file = destination_path + opts[:filename]
  # Verify hash to see if it is the latest
  skip_download = check_hash(destination_file, opts[:sha256])
  write_file(destination_file, fetch_file(uri)) unless skip_download
end

.download_upstream(destination_path, opts = {}) ⇒ String|nil

Download upstream password database

Parameters:

  • destination_path (String)

    the destination path (may overwrite existing file).

  • opts (Hash) (defaults to: {})

    the optional download parameters.

Options Hash (opts):

  • :sha256 (String)

    the SHA256 hash to check, if the file already exist and the hash matches then the download will be skipped.

  • :source_db (String)

    id of the source database (see. MAPPING in UPSTREAM_DATABASE). Default is 1.

Returns:

  • (String|nil)

    the saved file path.



52
53
54
55
56
57
58
# File 'lib/pass_station/source.rb', line 52

def download_upstream(destination_path, opts = {})
  opts[:source_db] ||= 1
  source_db = UPSTREAM_DATABASE[:MAPPING][opts[:source_db]]
  opts[:filename] = UPSTREAM_DATABASE[source_db][:FILENAME]
  source_url = UPSTREAM_DATABASE[source_db][:URL]
  download_file(source_url, destination_path, opts)
end

.fetch_file(uri) ⇒ Bytes

Just fetch a file

Parameters:

  • uri (URI)

    the URI to of the file

Returns:

  • (Bytes)

    the content of the file



110
111
112
113
114
115
# File 'lib/pass_station/source.rb', line 110

def fetch_file(uri)
  res = Net::HTTP.get_response(uri)
  raise "#{file_url} ended with #{res.code} #{res.message}" unless res.is_a?(Net::HTTPSuccess)

  res.body
end

.write_file(destination_file, file_content) ⇒ String

Write a file to disk

Parameters:

  • destination_file (String)

    the file path where the fiel will be written to disk

  • file_content (String)

    the content to write in the file

Returns:

  • (String)

    destination file path



122
123
124
125
# File 'lib/pass_station/source.rb', line 122

def write_file(destination_file, file_content)
  File.binwrite(destination_file, file_content)
  destination_file
end

Instance Method Details

#highlight_found(term, text, sensitive) ⇒ Array<String>

Highlight (colorize) a searched term in the input When used with the search command, it will ignore in which column the search was made, and will instead colorize in every columns.

Parameters:

  • term (String)

    the searched term

  • text (String)

    the output in which the colorization must be made

  • sensitive (Boolean)

    case sensitive or not

Returns:

  • (Array<String>)

    colorized output



47
48
49
50
51
52
# File 'lib/pass_station/output.rb', line 47

def highlight_found(term, text, sensitive)
  text.map do |x|
    rgxp = build_regexp(term, sensitive: sensitive)
    x.gsub(rgxp) { |s| Paint[s, :red] }
  end
end

#output(formatter, data) ⇒ Array<String>

Output the data in the chosen format

Parameters:

  • formatter (String)

    Engine to use to format the data: table, 'pretty-table', JSON, CSV, YAML

  • data (Array<CSV::Row>)

Returns:

  • (Array<String>)

    formatted output



18
19
20
21
# File 'lib/pass_station/output.rb', line 18

def output(formatter, data)
  # Convert string to class
  Object.const_get("PassStation::Output::#{normalize(formatter)}").format(data)
end

#output_list(formatter) ⇒ Array<String>

Output the data in the chosen format (list command)

Parameters:

  • formatter (String)

    Engine to use to format the data: table, 'pretty-table', JSON, CSV, YAML

Returns:

  • (Array<String>)

    formatted output



26
27
28
29
# File 'lib/pass_station/output.rb', line 26

def output_list(formatter)
  data_nil?
  output(formatter, @data)
end

#output_search(formatter) ⇒ Array<String>

Output the data in the chosen format (search command)

Parameters:

  • formatter (String)

    Engine to use to format the data: table, 'pretty-table', JSON, CSV, YAML

Returns:

  • (Array<String>)

    formatted output



34
35
36
37
38
# File 'lib/pass_station/output.rb', line 34

def output_search(formatter)
  return [] if @search_result.empty?

  output(formatter, @search_result)
end

#parse(sort = nil) ⇒ Array<CSV::Row>

Parse, sort and sanitize the password database

Parameters:

  • sort (Symbol) (defaults to: nil)

    column name to sort by (columns depends on the database source, see UPSTREAM_DATABASE)

Returns:

  • (Array<CSV::Row>)

    table of CSV::Row, each row contains multiple attributes (columns depends on the database source, see UPSTREAM_DATABASE)



28
29
30
31
32
33
# File 'lib/pass_station/parse.rb', line 28

def parse(sort = nil)
  sort ||= UPSTREAM_DATABASE[@database_type][:COLUMNS].first[0]
  @data = CSV.table(@database_path, **@config).sort_by do |s|
    s[sort].downcase
  end
end

#search(term, col, opts = {}) ⇒ Array<CSV::Row>

Search term in the data table

Parameters:

  • term (String)

    the searched term

  • col (Symbol)

    the column to search in: column name (columns depends on the database source, see UPSTREAM_DATABASE) or :all (all columns)

Returns:

  • (Array<CSV::Row>)

    table of CSV::Row, each row contains multiple attributes (columns depends on the database source, see UPSTREAM_DATABASE)

See Also:

  • for `opts` param description


14
15
16
17
18
19
20
21
22
# File 'lib/pass_station/search.rb', line 14

def search(term, col, opts = {})
  col ||= UPSTREAM_DATABASE[@database_type][:COLUMNS].first[0]
  r1 = prepare_search(term, opts)
  condition = column_selector(col, r1)
  @data.each do |row|
    @search_result.push(row) if condition.call(row)
  end
  @search_result
end