chelsea 0.0.23 → 0.0.28

Sign up to get free protection for your applications and to get access to all the features.
@@ -40,7 +40,7 @@ module Chelsea
40
40
  end
41
41
 
42
42
  def random_urn_uuid
43
- 'urn:uuid:' + SecureRandom.uuid
43
+ "urn:uuid:#{SecureRandom.uuid}"
44
44
  end
45
45
 
46
46
  private
@@ -87,8 +87,8 @@ module Chelsea
87
87
  component
88
88
  end
89
89
 
90
- def _show_logo
91
- logo = %Q(
90
+ def _show_logo # rubocop:disable Metrics/MethodLength
91
+ logo = %(
92
92
  -o/
93
93
  -+hNmNN-
94
94
  .:+osyhddddyso/-``ody+- .NN.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # Copyright 2019-Present Sonatype Inc.
3
5
  #
@@ -26,7 +28,7 @@ require_relative 'config'
26
28
  module Chelsea
27
29
  ##
28
30
  # This class provides an interface to the oss index, gems and deps
29
- class CLI
31
+ class CLI # rubocop:disable Metrics/ClassLength
30
32
  def initialize(opts)
31
33
  @opts = opts
32
34
  @pastel = Pastel.new
@@ -34,22 +36,28 @@ module Chelsea
34
36
  _show_logo # Move to formatter
35
37
  end
36
38
 
37
- def process!
39
+ # rubocop:disable Metrics/CyclomaticComplexity
40
+ def process! # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
38
41
  if @opts.config?
39
42
  _set_config # move to init
40
43
  elsif @opts.clear?
41
44
  require_relative 'db'
42
- Chelsea::DB.new().clear_cache
43
- puts "OSS Index cache cleared"
45
+ Chelsea::DB.new.clear_cache
46
+ puts 'OSS Index cache cleared'
44
47
  elsif @opts.file? && @opts.iq?
45
48
  dependencies = _process_file_iq
46
49
  _submit_sbom(dependencies)
50
+ elsif !@opts.file? && @opts.iq?
51
+ abort 'Missing the --file argument. It is required with the --iq argument.'
47
52
  elsif @opts.file?
48
53
  _process_file
49
54
  elsif @opts.help? # quit on opts.help earlier
50
55
  puts _cli_flags # this doesn't exist
56
+ else
57
+ abort 'Missing arguments! Chelsea did nothing. Try providing the --file <Gemfile.lock> argument.'
51
58
  end
52
59
  end
60
+ # rubocop:enable Metrics/CyclomaticComplexity
53
61
 
54
62
  def self.version
55
63
  Chelsea::VERSION
@@ -57,7 +65,7 @@ module Chelsea
57
65
 
58
66
  private
59
67
 
60
- def _submit_sbom(gems)
68
+ def _submit_sbom(gems) # rubocop:disable Metrics/MethodLength
61
69
  iq = Chelsea::IQClient.new(
62
70
  options: {
63
71
  public_application_id: @opts[:application],
@@ -70,10 +78,29 @@ module Chelsea
70
78
  bom = Chelsea::Bom.new(gems.deps.dependencies).collect
71
79
 
72
80
  status_url = iq.post_sbom(bom)
73
-
81
+
74
82
  return unless status_url
75
83
 
76
- iq.poll_status(status_url)
84
+ msg, color, exit_code = iq.poll_status(status_url)
85
+ show_status(msg, color)
86
+ # this may not be very ruby-esque, but `return exit_code` and `exit_code` didn't result in the desired exit status
87
+ exit exit_code
88
+ end
89
+
90
+ def show_status(msg, color)
91
+ case color
92
+ when Chelsea::IQClient::COLOR_FAILURE
93
+ puts @pastel.red.bold(msg)
94
+ when Chelsea::IQClient::COLOR_WARNING
95
+ # want yellow, but that doesn't print
96
+ # puts @pastel.color.bold(msg, color)
97
+ puts @pastel.blue.blue(msg)
98
+ when Chelsea::IQClient::COLOR_NONE
99
+ # want yellow, but that doesn't print
100
+ puts @pastel.green.bold(msg)
101
+ else
102
+ puts @pastel.bold(msg)
103
+ end
77
104
  end
78
105
 
79
106
  def _process_file
@@ -108,7 +135,7 @@ module Chelsea
108
135
 
109
136
  def _flags_set?
110
137
  # I'm still unsure what this is trying to express
111
- valid_flags = _flags.collect {|arg| @opts[arg] }.compact
138
+ valid_flags = _flags.collect { |arg| @opts[arg] }.compact
112
139
  valid_flags.count > 1
113
140
  end
114
141
 
@@ -120,7 +147,7 @@ module Chelsea
120
147
  def _show_logo
121
148
  font = TTY::Font.new(:doom)
122
149
  puts @pastel.green(font.write('Chelsea'))
123
- puts @pastel.green('Version: ' + CLI.version)
150
+ puts @pastel.green("Version: #{CLI.version}")
124
151
  end
125
152
 
126
153
  def _load_config
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # Copyright 2019-Present Sonatype Inc.
3
5
  #
@@ -17,6 +19,7 @@
17
19
  require 'yaml'
18
20
  require_relative 'oss_index'
19
21
 
22
+ # Saved credentials
20
23
  module Chelsea
21
24
  @oss_index_config_location = File.join(Dir.home.to_s, '.ossindex')
22
25
  @oss_index_config_filename = '.oss-index-config'
@@ -43,10 +46,8 @@ module Chelsea
43
46
  @client
44
47
  end
45
48
 
46
- def self.oss_index_config
47
- if !File.exist? File.join(@oss_index_config_location, @oss_index_config_filename)
48
- { oss_index_user_name: '', oss_index_user_token: '' }
49
- else
49
+ def self.oss_index_config # rubocop:disable Metrics/MethodLength
50
+ if File.exist? File.join(@oss_index_config_location, @oss_index_config_filename)
50
51
  conf_hash = YAML.safe_load(
51
52
  File.read(
52
53
  File.join(@oss_index_config_location, @oss_index_config_filename)
@@ -56,6 +57,8 @@ module Chelsea
56
57
  oss_index_user_name: conf_hash['Username'],
57
58
  oss_index_user_token: conf_hash['Token']
58
59
  }
60
+ else
61
+ { oss_index_user_name: '', oss_index_user_token: '' }
59
62
  end
60
63
  end
61
64
 
@@ -71,20 +74,18 @@ module Chelsea
71
74
  config = {}
72
75
 
73
76
  puts 'What username do you want to authenticate as (ex: your email address)? '
74
- config['Username'] = STDIN.gets.chomp
77
+ config['Username'] = $stdin.gets.chomp
75
78
 
76
79
  puts 'What token do you want to use? '
77
- config['Token'] = STDIN.gets.chomp
80
+ config['Token'] = $stdin.gets.chomp
78
81
 
79
82
  _write_oss_index_config_file(config)
80
83
  end
81
84
 
82
85
  def self._write_oss_index_config_file(config)
83
- unless File.exist? @oss_index_config_location
84
- Dir.mkdir(@oss_index_config_location)
85
- end
86
- File.open(File.join(@oss_index_config_location, @oss_index_config_filename), "w") do |file|
86
+ Dir.mkdir(@oss_index_config_location) unless File.exist? @oss_index_config_location
87
+ File.open(File.join(@oss_index_config_location, @oss_index_config_filename), 'w') do |file|
87
88
  file.write config.to_yaml
88
89
  end
89
90
  end
90
- end
91
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # Copyright 2019-Present Sonatype Inc.
3
5
  #
@@ -17,6 +19,7 @@
17
19
  require 'pstore'
18
20
 
19
21
  module Chelsea
22
+ # OSS Index data cache
20
23
  class DB
21
24
  def initialize
22
25
  @store = PStore.new(_get_db_store_location)
@@ -45,7 +48,7 @@ module Chelsea
45
48
  end
46
49
  end
47
50
 
48
- def _get_db_store_location()
51
+ def _get_db_store_location
49
52
  initial_path = File.join(Dir.home.to_s, '.ossindex')
50
53
  Dir.mkdir(initial_path) unless File.exist? initial_path
51
54
  File.join(initial_path, 'chelsea.pstore')
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # Copyright 2019-Present Sonatype Inc.
3
5
  #
@@ -15,8 +17,9 @@
15
17
  #
16
18
 
17
19
  module Chelsea
20
+ # Project dependency exception
18
21
  class DependencyException < StandardError
19
- def initialize(msg="This is a custom exception", exception_type="custom")
22
+ def initialize(msg = 'This is a custom exception', exception_type = 'custom')
20
23
  @exception_type = exception_type
21
24
  super(msg)
22
25
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # Copyright 2019-Present Sonatype Inc.
3
5
  #
@@ -22,6 +24,7 @@ require 'json'
22
24
  require 'rest-client'
23
25
 
24
26
  module Chelsea
27
+ # Project dependencies
25
28
  class Deps
26
29
  def initialize(path:, verbose: false)
27
30
  @verbose = verbose
@@ -47,7 +50,7 @@ module Chelsea
47
50
 
48
51
  # Collects all reverse dependencies in reverse_dependencies instance var
49
52
  # this rescue block honks
50
- def reverse_dependencies
53
+ def reverse_dependencies # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
51
54
  reverse = Gem::Commands::DependencyCommand.new
52
55
  reverse.options[:reverse_dependencies] = true
53
56
  # We want to filter the reverses dependencies by specs in lockfile
@@ -68,7 +71,7 @@ module Chelsea
68
71
  # in dependencies_versions and coordinates instance vars
69
72
  def coordinates
70
73
  dependencies.each_with_object({ 'coordinates' => [] }) do |(name, v), coords|
71
- coords['coordinates'] << self.class.to_purl(name, v[1]);
74
+ coords['coordinates'] << self.class.to_purl(name, v[1])
72
75
  end
73
76
  end
74
77
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # Copyright 2019-Present Sonatype Inc.
3
5
  #
@@ -20,7 +22,7 @@ require_relative 'text'
20
22
 
21
23
  # Factory for formatting dependencies
22
24
  class FormatterFactory
23
- def get_formatter(format: 'text', verbose:)
25
+ def get_formatter(verbose:, format: 'text')
24
26
  case format
25
27
  when 'text'
26
28
  Chelsea::TextFormatter.new verbose: verbose
@@ -28,7 +30,7 @@ class FormatterFactory
28
30
  Chelsea::JsonFormatter.new verbose: verbose
29
31
  when 'xml'
30
32
  Chelsea::XMLFormatter.new verbose: verbose
31
- else
33
+ else # rubocop:disable Lint/DuplicateBranch
32
34
  Chelsea::TextFormatter.new verbose: verbose
33
35
  end
34
36
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # Copyright 2019-Present Sonatype Inc.
3
5
  #
@@ -14,15 +16,17 @@
14
16
  # limitations under the License.
15
17
  #
16
18
 
19
+ # Base formatter class
17
20
  class Formatter
18
- def initialize
19
- @pastel = Pastel.new
20
- end
21
- def get_results
22
- raise 'must implement get_results method in subclass'
23
- end
24
-
25
- def do_print
26
- raise 'must implement do_print method in subclass'
27
- end
28
- end
21
+ def initialize
22
+ @pastel = Pastel.new
23
+ end
24
+
25
+ def fetch_results
26
+ raise 'must implement get_results method in subclass'
27
+ end
28
+
29
+ def do_print
30
+ raise 'must implement do_print method in subclass'
31
+ end
32
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # Copyright 2019-Present Sonatype Inc.
3
5
  #
@@ -18,12 +20,14 @@ require 'json'
18
20
  require_relative 'formatter'
19
21
 
20
22
  module Chelsea
23
+ # Produce output in json format
21
24
  class JsonFormatter < Formatter
22
25
  def initialize(options)
26
+ super()
23
27
  @options = options
24
28
  end
25
29
 
26
- def get_results(server_response, reverse_deps)
30
+ def fetch_results(server_response, _reverse_deps)
27
31
  server_response.to_json
28
32
  end
29
33
 
@@ -1,3 +1,6 @@
1
+ # rubocop:disable Style/FrozenStringLiteralComment
2
+ # rubocop:enable Style/FrozenStringLiteralComment
3
+
1
4
  #
2
5
  # Copyright 2019-Present Sonatype Inc.
3
6
  #
@@ -19,13 +22,16 @@ require 'tty-table'
19
22
  require_relative 'formatter'
20
23
 
21
24
  module Chelsea
25
+ # Produce output in text format
22
26
  class TextFormatter < Formatter
23
27
  def initialize(options)
28
+ super()
24
29
  @options = options
25
30
  @pastel = Pastel.new
26
31
  end
27
32
 
28
- def get_results(server_response, reverse_dependencies)
33
+ # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/AbcSize
34
+ def fetch_results(server_response, reverse_dependencies) # rubocop:disable Metrics/MethodLength
29
35
  response = ''
30
36
  if @options[:verbose]
31
37
  response += "\n"\
@@ -65,6 +71,7 @@ module Chelsea
65
71
  response += table.render(:unicode)
66
72
  response
67
73
  end
74
+ # rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/AbcSize
68
75
 
69
76
  def do_print(results)
70
77
  puts results
@@ -109,9 +116,7 @@ module Chelsea
109
116
  def _get_reverse_deps(coords, name)
110
117
  coords.each_with_object('') do |dep, s|
111
118
  dep.each do |gran|
112
- if gran.class == String && !gran.include?(name)
113
- s << "\tRequired by: #{gran}\n"
114
- end
119
+ s << "\tRequired by: #{gran}\n" if gran.instance_of?(String) && !gran.include?(name)
115
120
  end
116
121
  end
117
122
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # Copyright 2019-Present Sonatype Inc.
3
5
  #
@@ -17,12 +19,14 @@
17
19
  require 'ox'
18
20
  require_relative 'formatter'
19
21
  module Chelsea
22
+ # Produce output in xml format
20
23
  class XMLFormatter < Formatter
21
24
  def initialize(options)
25
+ super()
22
26
  @options = options
23
27
  end
24
28
 
25
- def get_results(server_response, reverse_deps)
29
+ def fetch_results(server_response, _reverse_deps) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
26
30
  doc = Ox::Document.new
27
31
  instruct = Ox::Instruct.new(:xml)
28
32
  instruct[:version] = '1.0'
@@ -37,13 +41,13 @@ module Chelsea
37
41
 
38
42
  server_response.each do |coord|
39
43
  testcase = Ox::Element.new('testcase')
40
- testcase[:classname] = coord["coordinates"]
41
- testcase[:name] = coord["coordinates"]
44
+ testcase[:classname] = coord['coordinates']
45
+ testcase[:name] = coord['coordinates']
42
46
 
43
47
  if coord['vulnerabilities'].length.positive?
44
48
  failure = Ox::Element.new('failure')
45
- failure[:type] = "Vulnerable Dependency"
46
- failure << get_vulnerability_block(coord["vulnerabilities"])
49
+ failure[:type] = 'Vulnerable Dependency'
50
+ failure << get_vulnerability_block(coord['vulnerabilities'])
47
51
  testcase << failure
48
52
  testsuite << testcase
49
53
  elsif @options[:verbose]
@@ -58,20 +62,20 @@ module Chelsea
58
62
  puts Ox.dump(results)
59
63
  end
60
64
 
61
- def get_vulnerability_block(vulnerabilities)
62
- vulnBlock = String.new
65
+ def get_vulnerability_block(vulnerabilities) # rubocop:disable Metrics/MethodLength
66
+ vuln_block = ''
63
67
  vulnerabilities.each do |vuln|
64
- vulnBlock += "Vulnerability Title: #{vuln["title"]}\n"\
65
- "ID: #{vuln["id"]}\n"\
66
- "Description: #{vuln["description"]}\n"\
67
- "CVSS Score: #{vuln["cvssScore"]}\n"\
68
- "CVSS Vector: #{vuln["cvssVector"]}\n"\
69
- "CVE: #{vuln["cve"]}\n"\
70
- "Reference: #{vuln["reference"]}"\
68
+ vuln_block += "Vulnerability Title: #{vuln['title']}\n"\
69
+ "ID: #{vuln['id']}\n"\
70
+ "Description: #{vuln['description']}\n"\
71
+ "CVSS Score: #{vuln['cvssScore']}\n"\
72
+ "CVSS Vector: #{vuln['cvssVector']}\n"\
73
+ "CVE: #{vuln['cve']}\n"\
74
+ "Reference: #{vuln['reference']}"\
71
75
  "\n"
72
76
  end
73
-
74
- vulnBlock
77
+
78
+ vuln_block
75
79
  end
76
80
  end
77
81
  end