chelsea 0.0.26 → 0.0.31
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/.circleci/config.yml +10 -5
- data/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
- data/.rubocop.yml +6 -0
- data/Gemfile +7 -2
- data/Gemfile.lock +28 -8
- data/README.md +268 -1
- data/Rakefile +5 -3
- data/bin/chelsea +14 -7
- data/bin/console +5 -4
- data/chelsea.gemspec +30 -29
- data/lib/chelsea/bom.rb +3 -3
- data/lib/chelsea/cli.rb +36 -9
- data/lib/chelsea/config.rb +12 -11
- data/lib/chelsea/db.rb +4 -1
- data/lib/chelsea/dependency_exception.rb +4 -1
- data/lib/chelsea/deps.rb +5 -2
- data/lib/chelsea/formatters/factory.rb +4 -2
- data/lib/chelsea/formatters/formatter.rb +15 -11
- data/lib/chelsea/formatters/json.rb +5 -1
- data/lib/chelsea/formatters/text.rb +9 -4
- data/lib/chelsea/formatters/xml.rb +20 -16
- data/lib/chelsea/gems.rb +16 -17
- data/lib/chelsea/iq_client.rb +66 -38
- data/lib/chelsea/oss_index.rb +8 -9
- data/lib/chelsea/spinner.rb +4 -1
- data/lib/chelsea/version.rb +3 -1
- metadata +49 -62
data/Rakefile
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
#
|
2
4
|
# Copyright 2019-Present Sonatype Inc.
|
3
5
|
#
|
@@ -14,9 +16,9 @@
|
|
14
16
|
# limitations under the License.
|
15
17
|
#
|
16
18
|
|
17
|
-
require
|
18
|
-
require
|
19
|
+
require 'bundler/gem_tasks'
|
20
|
+
require 'rspec/core/rake_task'
|
19
21
|
|
20
22
|
RSpec::Core::RakeTask.new(:spec)
|
21
23
|
|
22
|
-
task :
|
24
|
+
task default: :spec
|
data/bin/chelsea
CHANGED
@@ -16,7 +16,8 @@
|
|
16
16
|
#
|
17
17
|
|
18
18
|
# frozen_string_literal: true
|
19
|
-
|
19
|
+
|
20
|
+
require_relative '../lib/chelsea'
|
20
21
|
require 'slop'
|
21
22
|
opts =
|
22
23
|
begin
|
@@ -31,13 +32,19 @@ opts =
|
|
31
32
|
o.string '-iu', '--iquser', 'Specify the IQ username', default: 'admin'
|
32
33
|
o.string '-it', '--iqpass', 'Specify the IQ auth token', default: 'admin123'
|
33
34
|
o.string '-w', '--whitelist', 'Set path to vulnerability whitelist file'
|
34
|
-
o.bool '-v', '--verbose',
|
35
|
-
|
35
|
+
o.bool '-v', '--verbose',
|
36
|
+
'For text format, list dependencies, their reverse dependencies (what brought them in to your project), and
|
37
|
+
if they are vulnerable. (default: false)', default: false
|
38
|
+
o.string '-t', '--format',
|
39
|
+
'Choose what type of format you want your report in (default: text) (options: text, json, xml)',
|
40
|
+
default: 'text'
|
36
41
|
o.bool '-b', '--iq', 'Use Nexus IQ Server to audit your project'
|
37
|
-
o.string '-s', '--stage',
|
42
|
+
o.string '-s', '--stage',
|
43
|
+
'Specify Nexus IQ Stage (default: build) (options: develop, build, stage-release, release, operate)',
|
44
|
+
default: 'build'
|
38
45
|
o.on '--version', 'Print the version' do
|
39
|
-
|
40
|
-
|
46
|
+
puts Chelsea::VERSION
|
47
|
+
exit
|
41
48
|
end
|
42
49
|
o.on '-h', '--help', 'Show usage' do
|
43
50
|
puts(o)
|
@@ -45,7 +52,7 @@ opts =
|
|
45
52
|
end
|
46
53
|
end
|
47
54
|
rescue Slop::Error => e
|
48
|
-
puts(e.message
|
55
|
+
puts("#{e.message} (try --help)")
|
49
56
|
exit 1
|
50
57
|
end
|
51
58
|
if opts.arguments.count.positive?
|
data/bin/console
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
#
|
3
5
|
# Copyright 2019-Present Sonatype Inc.
|
4
6
|
#
|
@@ -15,9 +17,8 @@
|
|
15
17
|
# limitations under the License.
|
16
18
|
#
|
17
19
|
|
20
|
+
require 'bundler/setup'
|
21
|
+
require 'chelsea'
|
18
22
|
|
19
|
-
require
|
20
|
-
require "chelsea"
|
21
|
-
|
22
|
-
require "irb"
|
23
|
+
require 'irb'
|
23
24
|
IRB.start(__FILE__)
|
data/chelsea.gemspec
CHANGED
@@ -1,42 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
|
-
lib = File.expand_path(
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
5
|
+
require 'chelsea/version'
|
5
6
|
|
6
7
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
8
|
-
spec.license =
|
8
|
+
spec.name = 'chelsea'
|
9
|
+
spec.license = 'Apache-2.0'
|
9
10
|
spec.version = Chelsea::VERSION
|
10
|
-
spec.authors = [
|
11
|
-
spec.email = [
|
11
|
+
spec.authors = ['Allister Beharry']
|
12
|
+
spec.email = ['allister.beharry@gmail.com']
|
13
|
+
spec.required_ruby_version = '>= 2.6.6'
|
12
14
|
|
13
|
-
spec.summary =
|
14
|
-
spec.homepage =
|
15
|
-
|
16
|
-
spec.metadata["homepage_uri"] = spec.homepage
|
17
|
-
spec.metadata["source_code_uri"] = "https://github.com/sonatype-nexus-community/chelsea"
|
18
|
-
spec.metadata["changelog_uri"] = "https://github.com/sonatype-nexus-community/chelsea/CHANGELOG"
|
15
|
+
spec.summary = 'Audit Ruby package dependencies for security vulnerabilities.'
|
16
|
+
spec.homepage = 'https://github.com/sonatype-nexus-community/chelsea'
|
19
17
|
|
20
|
-
spec.
|
18
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
19
|
+
spec.metadata['source_code_uri'] = 'https://github.com/sonatype-nexus-community/chelsea'
|
20
|
+
spec.metadata['changelog_uri'] = 'https://github.com/sonatype-nexus-community/chelsea/CHANGELOG'
|
21
|
+
|
22
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
21
23
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
22
24
|
end
|
23
|
-
spec.bindir =
|
25
|
+
spec.bindir = 'bin'
|
24
26
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
25
|
-
spec.require_paths = [
|
27
|
+
spec.require_paths = ['lib']
|
26
28
|
|
27
|
-
spec.add_dependency
|
28
|
-
spec.add_dependency
|
29
|
-
spec.add_dependency
|
30
|
-
spec.add_dependency
|
31
|
-
spec.add_dependency
|
32
|
-
spec.add_dependency
|
33
|
-
spec.add_dependency
|
34
|
-
spec.add_dependency
|
29
|
+
spec.add_dependency 'bundler', '>= 1.2.0', '< 3'
|
30
|
+
spec.add_dependency 'ox', '~> 2.13.2'
|
31
|
+
spec.add_dependency 'pastel', '~> 0.7.2'
|
32
|
+
spec.add_dependency 'rest-client', '~> 2.0.2'
|
33
|
+
spec.add_dependency 'slop', '~> 4.8.1'
|
34
|
+
spec.add_dependency 'tty-font', '~> 0.5.0'
|
35
|
+
spec.add_dependency 'tty-spinner', '~> 0.9.3'
|
36
|
+
spec.add_dependency 'tty-table', '~> 0.11.0'
|
35
37
|
|
36
|
-
spec.add_development_dependency
|
37
|
-
spec.add_development_dependency
|
38
|
-
spec.add_development_dependency
|
39
|
-
spec.add_development_dependency
|
40
|
-
spec.add_development_dependency
|
41
|
-
spec.add_development_dependency 'pry'
|
38
|
+
spec.add_development_dependency 'byebug', '~> 11.1.2'
|
39
|
+
spec.add_development_dependency 'rake', '~> 12.3'
|
40
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
41
|
+
spec.add_development_dependency 'rspec_junit_formatter', '~> 0.4.1'
|
42
|
+
spec.add_development_dependency 'webmock', '~> 3.8.3'
|
42
43
|
end
|
data/lib/chelsea/bom.rb
CHANGED
@@ -40,7 +40,7 @@ module Chelsea
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def random_urn_uuid
|
43
|
-
|
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 = %
|
90
|
+
def _show_logo # rubocop:disable Metrics/MethodLength
|
91
|
+
logo = %(
|
92
92
|
-o/
|
93
93
|
-+hNmNN-
|
94
94
|
.:+osyhddddyso/-``ody+- .NN.
|
data/lib/chelsea/cli.rb
CHANGED
@@ -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
|
-
|
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
|
43
|
-
puts
|
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(
|
150
|
+
puts @pastel.green("Version: #{CLI.version}")
|
124
151
|
end
|
125
152
|
|
126
153
|
def _load_config
|
data/lib/chelsea/config.rb
CHANGED
@@ -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
|
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'] =
|
77
|
+
config['Username'] = $stdin.gets.chomp
|
75
78
|
|
76
79
|
puts 'What token do you want to use? '
|
77
|
-
config['Token'] =
|
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
|
-
|
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
|
data/lib/chelsea/db.rb
CHANGED
@@ -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=
|
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
|
data/lib/chelsea/deps.rb
CHANGED
@@ -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'
|
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
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|