chelsea 0.0.27 → 0.0.28

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0f179d743f4810498f2e0edbe787038e2caed8e43af7279c39bf309860a568ae
4
- data.tar.gz: b20643fbaa0bbc56073a53575b4d3d5ba2d1b4f5b8b1e996dcdc373e2a2bd997
3
+ metadata.gz: 1d6bcb375015c64ae7e452f93e38b10cfabb4ecbc9425fad0121e926a0148efa
4
+ data.tar.gz: f1a171d3a72a0bf910ac4e22258cee6a0d6c3e8181307856a34a5b22ca0435c7
5
5
  SHA512:
6
- metadata.gz: b44c830f8e38ba6693babade3cb1da853baf545a6839aa79bea41a5821712cf055737e8af349c1c9aa9653bb09c5faa1cad0660ec3806f0add68418dadd36f54
7
- data.tar.gz: ec6ebcd7e0ac9fb7ebb30349c824187cfc06390fabd40a76a39c4c622a6801c513c5446f89dc3422f703e15cab214a3b1fb72ff7acd4f7ef6e249a27c8331b6e
6
+ metadata.gz: decb1e4e8a54798161b2e888e1694d9558ba0bbacd6708c6cf32d4b71b32cb05520f017733a18e90dab99c0e2e2be500d3c8b8701b1ff464041ad63ab89ffa74
7
+ data.tar.gz: 94f91e9b3f248d7a3c494fdba567f832edf38b2e91e75f791ac1d9dace17b285e89d09c43bae25d6faacd0b2a76e5a4bb211acb3153dfc57ea4b581243f5d8d1
@@ -18,7 +18,7 @@ version: 2.1
18
18
  jobs:
19
19
  build:
20
20
  docker:
21
- - image: circleci/ruby:2.6.5-stretch
21
+ - image: circleci/ruby:2.6.6-stretch
22
22
  environment:
23
23
  BUNDLE_PATH: vendor/bundle
24
24
  steps:
@@ -48,6 +48,9 @@ jobs:
48
48
  bundle exec rspec --format progress \
49
49
  --format RspecJunitFormatter \
50
50
  --out test_results/rspec.xml
51
+ - run:
52
+ name: Run linter
53
+ command: bundle exec rubocop
51
54
  - run:
52
55
  name: Build gem
53
56
  command: gem build chelsea.gemspec
@@ -64,7 +67,7 @@ jobs:
64
67
  path: test_results
65
68
  release:
66
69
  docker:
67
- - image: circleci/ruby:2.6.5-stretch
70
+ - image: circleci/ruby:2.6.6-stretch
68
71
  steps:
69
72
  - add_ssh_keys:
70
73
  fingerprints:
@@ -23,7 +23,7 @@ If applicable, add screenshots to help explain your problem.
23
23
 
24
24
  **Desktop (please complete the following information):**
25
25
  - OS: [e.g. OS X 1.13.6]
26
- - Ruby Version: [e.g. 2.6.5]
26
+ - Ruby Version: [e.g. 2.6.6]
27
27
  - Bundler Version: [e.g. 2.1.4]
28
28
  - chelsea Version [e.g. 0.0.11]
29
29
 
@@ -0,0 +1,6 @@
1
+ AllCops:
2
+ NewCops: enable
3
+ TargetRubyVersion: 2.6.6
4
+
5
+ Metrics/BlockLength:
6
+ IgnoredMethods: ['Slop.parse']
data/Gemfile CHANGED
@@ -1,6 +1,11 @@
1
- source "https://rubygems.org"
1
+ # frozen_string_literal: true
2
2
 
3
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
6
 
5
7
  # Specify your gem's dependencies in chelsea.gemspec
6
8
  gemspec
9
+
10
+ # linter
11
+ gem 'rubocop', require: false
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- chelsea (0.0.26)
4
+ chelsea (0.0.27)
5
5
  bundler (>= 1.2.0, < 3)
6
6
  ox (~> 2.13.2)
7
7
  pastel (~> 0.7.2)
@@ -16,6 +16,7 @@ GEM
16
16
  specs:
17
17
  addressable (2.7.0)
18
18
  public_suffix (>= 2.0.2, < 5.0)
19
+ ast (2.4.2)
19
20
  byebug (11.1.2)
20
21
  crack (0.4.3)
21
22
  safe_yaml (~> 1.0.0)
@@ -32,15 +33,21 @@ GEM
32
33
  necromancer (0.6.0)
33
34
  netrc (0.11.0)
34
35
  ox (2.13.4)
36
+ parallel (1.20.1)
37
+ parser (3.0.0.0)
38
+ ast (~> 2.4.1)
35
39
  pastel (0.7.4)
36
40
  equatable (~> 0.6)
37
41
  tty-color (~> 0.5)
38
42
  public_suffix (4.0.3)
43
+ rainbow (3.0.0)
39
44
  rake (12.3.3)
45
+ regexp_parser (2.0.3)
40
46
  rest-client (2.0.2)
41
47
  http-cookie (>= 1.0.2, < 2.0)
42
48
  mime-types (>= 1.16, < 4.0)
43
49
  netrc (~> 0.8)
50
+ rexml (3.2.4)
44
51
  rspec (3.9.0)
45
52
  rspec-core (~> 3.9.0)
46
53
  rspec-expectations (~> 3.9.0)
@@ -56,6 +63,18 @@ GEM
56
63
  rspec-support (3.9.2)
57
64
  rspec_junit_formatter (0.4.1)
58
65
  rspec-core (>= 2, < 4, != 2.12.0)
66
+ rubocop (1.9.0)
67
+ parallel (~> 1.10)
68
+ parser (>= 3.0.0.0)
69
+ rainbow (>= 2.2.2, < 4.0)
70
+ regexp_parser (>= 1.8, < 3.0)
71
+ rexml
72
+ rubocop-ast (>= 1.2.0, < 2.0)
73
+ ruby-progressbar (~> 1.7)
74
+ unicode-display_width (>= 1.4.0, < 3.0)
75
+ rubocop-ast (1.4.1)
76
+ parser (>= 2.7.1.5)
77
+ ruby-progressbar (1.11.0)
59
78
  safe_yaml (1.0.5)
60
79
  slop (4.8.2)
61
80
  strings (0.1.8)
@@ -94,6 +113,7 @@ DEPENDENCIES
94
113
  rake (~> 12.3)
95
114
  rspec (~> 3.0)
96
115
  rspec_junit_formatter (~> 0.4.1)
116
+ rubocop
97
117
  webmock (~> 3.8.3)
98
118
 
99
119
  BUNDLED WITH
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 "bundler/gem_tasks"
18
- require "rspec/core/rake_task"
19
+ require 'bundler/gem_tasks'
20
+ require 'rspec/core/rake_task'
19
21
 
20
22
  RSpec::Core::RakeTask.new(:spec)
21
23
 
22
- task :default => :spec
24
+ task default: :spec
@@ -16,7 +16,8 @@
16
16
  #
17
17
 
18
18
  # frozen_string_literal: true
19
- require_relative "../lib/chelsea"
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', 'For text format, list dependencies, their reverse dependencies (what brought them in to your project), and if they are vulnerable. (default: false)', default: false
35
- o.string '-t', '--format', 'Choose what type of format you want your report in (default: text) (options: text, json, xml)', default: 'text'
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', 'Specify Nexus IQ Stage (default: build) (options: develop, build, stage-release, release, operate)', default: 'build'
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
- puts Chelsea::VERSION
40
- exit
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 + ' (try --help)')
55
+ puts("#{e.message} (try --help)")
49
56
  exit 1
50
57
  end
51
58
  if opts.arguments.count.positive?
@@ -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 "bundler/setup"
20
- require "chelsea"
21
-
22
- require "irb"
23
+ require 'irb'
23
24
  IRB.start(__FILE__)
@@ -1,41 +1,43 @@
1
+ # frozen_string_literal: true
1
2
 
2
- lib = File.expand_path("../lib", __FILE__)
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "chelsea/version"
5
+ require 'chelsea/version'
5
6
 
6
7
  Gem::Specification.new do |spec|
7
- spec.name = "chelsea"
8
- spec.license = "Apache-2.0"
8
+ spec.name = 'chelsea'
9
+ spec.license = 'Apache-2.0'
9
10
  spec.version = Chelsea::VERSION
10
- spec.authors = ["Allister Beharry"]
11
- spec.email = ["allister.beharry@gmail.com"]
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 = "Audit Ruby package dependencies for security vulnerabilities."
14
- spec.homepage = "https://github.com/sonatype-nexus-community/chelsea"
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.files = Dir.chdir(File.expand_path('..', __FILE__)) do
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 = "bin"
25
+ spec.bindir = 'bin'
24
26
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
25
- spec.require_paths = ["lib"]
27
+ spec.require_paths = ['lib']
26
28
 
27
- spec.add_dependency "tty-font", "~> 0.5.0"
28
- spec.add_dependency "tty-spinner", "~> 0.9.3"
29
- spec.add_dependency "slop", "~> 4.8.1"
30
- spec.add_dependency "pastel", "~> 0.7.2"
31
- spec.add_dependency "rest-client", "~> 2.0.2"
32
- spec.add_dependency "bundler", ">= 1.2.0", "< 3"
33
- spec.add_dependency "ox", "~> 2.13.2"
34
- spec.add_dependency "tty-table", "~> 0.11.0"
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 "rake", "~> 12.3"
37
- spec.add_development_dependency "rspec", "~> 3.0"
38
- spec.add_development_dependency "rspec_junit_formatter", "~> 0.4.1"
39
- spec.add_development_dependency "webmock", "~> 3.8.3"
40
- spec.add_development_dependency "byebug", "~> 11.1.2"
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'
41
43
  end
@@ -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,26 +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
45
  Chelsea::DB.new.clear_cache
43
- puts "OSS Index cache cleared"
46
+ puts 'OSS Index cache cleared'
44
47
  elsif @opts.file? && @opts.iq?
45
48
  dependencies = _process_file_iq
46
49
  _submit_sbom(dependencies)
47
50
  elsif !@opts.file? && @opts.iq?
48
- abort "Missing the --file argument. It is required with the --iq argument."
51
+ abort 'Missing the --file argument. It is required with the --iq argument.'
49
52
  elsif @opts.file?
50
53
  _process_file
51
54
  elsif @opts.help? # quit on opts.help earlier
52
55
  puts _cli_flags # this doesn't exist
53
56
  else
54
- abort "Missing arguments! Chelsea did nothing. Try providing the --file <Gemfile.lock> argument."
57
+ abort 'Missing arguments! Chelsea did nothing. Try providing the --file <Gemfile.lock> argument.'
55
58
  end
56
59
  end
60
+ # rubocop:enable Metrics/CyclomaticComplexity
57
61
 
58
62
  def self.version
59
63
  Chelsea::VERSION
@@ -61,7 +65,7 @@ module Chelsea
61
65
 
62
66
  private
63
67
 
64
- def _submit_sbom(gems)
68
+ def _submit_sbom(gems) # rubocop:disable Metrics/MethodLength
65
69
  iq = Chelsea::IQClient.new(
66
70
  options: {
67
71
  public_application_id: @opts[:application],
@@ -74,7 +78,7 @@ module Chelsea
74
78
  bom = Chelsea::Bom.new(gems.deps.dependencies).collect
75
79
 
76
80
  status_url = iq.post_sbom(bom)
77
-
81
+
78
82
  return unless status_url
79
83
 
80
84
  msg, color, exit_code = iq.poll_status(status_url)
@@ -131,7 +135,7 @@ module Chelsea
131
135
 
132
136
  def _flags_set?
133
137
  # I'm still unsure what this is trying to express
134
- valid_flags = _flags.collect {|arg| @opts[arg] }.compact
138
+ valid_flags = _flags.collect { |arg| @opts[arg] }.compact
135
139
  valid_flags.count > 1
136
140
  end
137
141
 
@@ -143,7 +147,7 @@ module Chelsea
143
147
  def _show_logo
144
148
  font = TTY::Font.new(:doom)
145
149
  puts @pastel.green(font.write('Chelsea'))
146
- puts @pastel.green('Version: ' + CLI.version)
150
+ puts @pastel.green("Version: #{CLI.version}")
147
151
  end
148
152
 
149
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
@@ -15,6 +15,7 @@
15
15
  #
16
16
 
17
17
  # frozen_string_literal: true
18
+
18
19
  require 'pastel'
19
20
  require 'bundler'
20
21
  require 'bundler/lockfile_parser'
@@ -31,11 +32,10 @@ module Chelsea
31
32
  # Class to collect and audit packages from a Gemfile.lock
32
33
  class Gems
33
34
  attr_accessor :deps
34
- def initialize(file:, verbose:, options: { 'format': 'text' })
35
+
36
+ def initialize(file:, verbose:, options: { format: 'text' }) # rubocop:disable Metrics/MethodLength
35
37
  @verbose = verbose
36
- unless File.file?(file) || file.nil?
37
- raise 'Gemfile.lock not found, check --file path'
38
- end
38
+ raise 'Gemfile.lock not found, check --file path' unless File.file?(file) || file.nil?
39
39
 
40
40
  _silence_stderr unless @verbose
41
41
 
@@ -52,7 +52,7 @@ module Chelsea
52
52
  # Audits depenencies using deps library and prints results
53
53
  # using formatter library
54
54
 
55
- def execute
55
+ def execute # rubocop:disable Metrics/MethodLength
56
56
  server_response, dependencies, reverse_dependencies = audit
57
57
  if dependencies.nil?
58
58
  _print_err 'No dependencies retrieved. Exiting.'
@@ -62,20 +62,19 @@ module Chelsea
62
62
  _print_success 'No vulnerability data retrieved from server. Exiting.'
63
63
  return
64
64
  end
65
- results = @formatter.get_results(server_response, reverse_dependencies)
65
+ results = @formatter.fetch_results(server_response, reverse_dependencies)
66
66
  @formatter.do_print(results)
67
67
 
68
68
  server_response.map { |r| r['vulnerabilities'].length.positive? }.any?
69
69
  end
70
70
 
71
71
  def collect_iq
72
- dependencies = @deps.dependencies
73
- dependencies
72
+ @deps.dependencies
74
73
  end
75
74
 
76
75
  # Runs through auditing algorithm, raising exceptions
77
76
  # on REST calls made by @deps.get_vulns
78
- def audit
77
+ def audit # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
79
78
  # This spinner management is out of control
80
79
  # we should wrap a block with start and stop messages,
81
80
  # or use a stack to ensure all spinners stop.
@@ -84,7 +83,7 @@ module Chelsea
84
83
  begin
85
84
  dependencies = @deps.dependencies
86
85
  spin.success('...done.')
87
- rescue StandardError => e
86
+ rescue StandardError
88
87
  spin.stop
89
88
  _print_err "Parsing dependency line #{gem} failed."
90
89
  end
@@ -100,16 +99,16 @@ module Chelsea
100
99
  begin
101
100
  server_response = @client.get_vulns(coordinates)
102
101
  spin.success('...done.')
103
- rescue SocketError => e
102
+ rescue SocketError
104
103
  spin.stop('...request failed.')
105
104
  _print_err 'Socket error getting data from OSS Index server.'
106
105
  rescue RestClient::RequestFailed => e
107
106
  spin.stop('...request failed.')
108
107
  _print_err "Error getting data from OSS Index server:#{e.response}."
109
- rescue RestClient::ResourceNotFound => e
108
+ rescue RestClient::ResourceNotFound
110
109
  spin.stop('...request failed.')
111
110
  _print_err 'Error getting data from OSS Index server. Resource not found.'
112
- rescue Errno::ECONNREFUSED => e
111
+ rescue Errno::ECONNREFUSED
113
112
  spin.stop('...request failed.')
114
113
  _print_err 'Error getting data from OSS Index server. Connection refused.'
115
114
  end
@@ -122,12 +121,12 @@ module Chelsea
122
121
  $stderr.reopen('/dev/null', 'w')
123
122
  end
124
123
 
125
- def _print_err(s)
126
- puts @pastel.red.bold(s)
124
+ def _print_err(msg)
125
+ puts @pastel.red.bold(msg)
127
126
  end
128
127
 
129
- def _print_success(s)
130
- puts @pastel.green.bold(s)
128
+ def _print_success(msg)
129
+ puts @pastel.green.bold(msg)
131
130
  end
132
131
  end
133
132
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # Copyright 2019-Present Sonatype Inc.
3
5
  #
@@ -22,8 +24,8 @@ require 'uri'
22
24
  require_relative 'spinner'
23
25
 
24
26
  module Chelsea
25
- class IQClient
26
-
27
+ # IQ audit operations
28
+ class IQClient # rubocop:disable Metrics/ClassLength
27
29
  DEFAULT_OPTIONS = {
28
30
  public_application_id: 'testapp',
29
31
  server_url: 'http://localhost:8070',
@@ -31,7 +33,7 @@ module Chelsea
31
33
  auth_token: 'admin123',
32
34
  internal_application_id: '',
33
35
  stage: 'build'
34
- }
36
+ }.freeze
35
37
 
36
38
  def initialize(options: DEFAULT_OPTIONS)
37
39
  @options = options
@@ -39,8 +41,8 @@ module Chelsea
39
41
  @spinner = Chelsea::Spinner.new
40
42
  end
41
43
 
42
- def post_sbom(sbom)
43
- spin = @spinner.spin_msg "Submitting sbom to Nexus IQ Server"
44
+ def post_sbom(sbom) # rubocop:disable Metrics/MethodLength
45
+ spin = @spinner.spin_msg 'Submitting sbom to Nexus IQ Server'
44
46
  @internal_application_id = _get_internal_application_id
45
47
  resource = RestClient::Resource.new(
46
48
  _api_url,
@@ -48,12 +50,12 @@ module Chelsea
48
50
  password: @options[:auth_token]
49
51
  )
50
52
  res = resource.post sbom.to_s, _headers.merge(content_type: 'application/xml')
51
- if res.code != 202
53
+ if res.code == 202
54
+ spin.success('...done.')
55
+ status_url(res)
56
+ else
52
57
  spin.stop('...request failed.')
53
58
  nil
54
- else
55
- spin.success("...done.")
56
- status_url(res)
57
59
  end
58
60
  end
59
61
 
@@ -63,17 +65,15 @@ module Chelsea
63
65
  end
64
66
 
65
67
  def poll_status(url)
66
- spin = @spinner.spin_msg "Polling Nexus IQ Server for results"
68
+ spin = @spinner.spin_msg 'Polling Nexus IQ Server for results'
67
69
  loop do
68
- begin
69
- res = _poll_iq_server(url)
70
- if res.code == 200
71
- spin.success("...done.")
72
- return _handle_response(res)
73
- end
74
- rescue
75
- sleep(1)
70
+ res = _poll_iq_server(url)
71
+ if res.code == 200
72
+ spin.success('...done.')
73
+ return _handle_response(res)
76
74
  end
75
+ rescue StandardError
76
+ sleep(1)
77
77
  end
78
78
  end
79
79
 
@@ -88,28 +88,28 @@ module Chelsea
88
88
 
89
89
  private
90
90
 
91
- def _handle_response(res)
91
+ def _handle_response(res) # rubocop:disable Metrics/MethodLength
92
92
  res = JSON.parse(res.body)
93
93
  # get absolute report url
94
94
  absolute_report_html_url = URI.join(@options[:server_url], res['reportHtmlUrl'])
95
95
 
96
96
  case res['policyAction']
97
97
  when POLICY_ACTION_FAILURE
98
- return "Hi! Chelsea here, you have some policy violations to clean up!"\
98
+ ['Hi! Chelsea here, you have some policy violations to clean up!'\
99
99
  "\nReport URL: #{absolute_report_html_url}",
100
- COLOR_FAILURE, 1
100
+ COLOR_FAILURE, 1]
101
101
  when POLICY_ACTION_WARNING
102
- return "Hi! Chelsea here, you have some policy warnings to peck at!"\
102
+ ['Hi! Chelsea here, you have some policy warnings to peck at!'\
103
103
  "\nReport URL: #{absolute_report_html_url}",
104
- COLOR_WARNING, 0
104
+ COLOR_WARNING, 0]
105
105
  when POLICY_ACTION_NONE
106
- return "Hi! Chelsea here, no policy violations for this audit!"\
106
+ ['Hi! Chelsea here, no policy violations for this audit!'\
107
107
  "\nReport URL: #{absolute_report_html_url}",
108
- COLOR_NONE, 0
108
+ COLOR_NONE, 0]
109
109
  else
110
- return "Hi! Chelsea here, no policy violations for this audit, but unknown policy action!"\
110
+ ['Hi! Chelsea here, no policy violations for this audit, but unknown policy action!'\
111
111
  "\nReport URL: #{absolute_report_html_url}",
112
- COLOR_FAILURE, 1
112
+ COLOR_FAILURE, 1]
113
113
  end
114
114
  end
115
115
 
@@ -137,26 +137,22 @@ module Chelsea
137
137
  res['statusUrl']
138
138
  end
139
139
 
140
- private
141
-
142
- def _poll_status
140
+ def _poll_status # rubocop:disable Metrics/MethodLength
143
141
  return unless @status_url
144
142
 
145
143
  loop do
146
- begin
147
- res = check_status(@status_url)
148
- if res.code == 200
149
- puts JSON.parse(res.body)
150
- break
151
- end
152
- rescue RestClient::ResourceNotFound => _e
153
- print '.'
154
- sleep(1)
144
+ res = check_status(@status_url)
145
+ if res.code == 200
146
+ puts JSON.parse(res.body)
147
+ break
155
148
  end
149
+ rescue RestClient::ResourceNotFound => _e
150
+ print '.'
151
+ sleep(1)
156
152
  end
157
153
  end
158
154
 
159
- def _get_internal_application_id
155
+ def _get_internal_application_id # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
160
156
  resource = RestClient::Resource.new(
161
157
  _internal_application_id_api_url,
162
158
  user: @options[:username],
@@ -164,7 +160,7 @@ module Chelsea
164
160
  )
165
161
  res = resource.get _headers
166
162
  if res.code != 200
167
- puts "failed to get internal application id for IQ application id: #{@options[:public_application_id]}. response status: #{res.code}"
163
+ puts "failure reading application id: #{@options[:public_application_id]}. response status: #{res.code}"
168
164
  return
169
165
  end
170
166
  body = JSON.parse(res)
@@ -180,7 +176,9 @@ module Chelsea
180
176
  end
181
177
 
182
178
  def _api_url
179
+ # rubocop:disable Layout/LineLength
183
180
  "#{@options[:server_url]}/api/v2/scan/applications/#{@internal_application_id}/sources/chelsea?stageId=#{@options[:stage]}"
181
+ # rubocop:enable Layout/LineLength
184
182
  end
185
183
 
186
184
  def _internal_application_id_api_url
@@ -21,11 +21,12 @@ require 'rest-client'
21
21
  require_relative 'db'
22
22
 
23
23
  module Chelsea
24
+ # OSS Index audit operations
24
25
  class OSSIndex
25
26
  DEFAULT_OPTIONS = {
26
27
  oss_index_username: '',
27
28
  oss_index_user_token: ''
28
- }
29
+ }.freeze
29
30
  def initialize(options: DEFAULT_OPTIONS)
30
31
  @oss_index_user_name = options[:oss_index_user_name]
31
32
  @oss_index_user_token = options[:oss_index_user_token]
@@ -37,13 +38,11 @@ module Chelsea
37
38
 
38
39
  def get_vulns(coordinates)
39
40
  remaining_coordinates, cached_server_response = _cache(coordinates)
40
- unless remaining_coordinates['coordinates'].count.positive?
41
- return cached_server_response
42
- end
41
+ return cached_server_response unless remaining_coordinates['coordinates'].count.positive?
43
42
 
44
43
  remaining_coordinates['coordinates'].each_slice(128).to_a.each do |coords|
45
44
  res_json = JSON.parse(call_oss_index({ 'coordinates' => coords }))
46
- cached_server_response = cached_server_response.concat(res_json)
45
+ cached_server_response.concat(res_json)
47
46
  @db.save_values_to_db(res_json)
48
47
  end
49
48
 
@@ -57,15 +56,15 @@ module Chelsea
57
56
 
58
57
  private
59
58
 
60
- def _cache(coordinates)
59
+ def _cache(coordinates) # rubocop:disable Metrics/MethodLength
61
60
  new_coords = { 'coordinates' => [] }
62
61
  cached_server_response = []
63
62
  coordinates['coordinates'].each do |coord|
64
63
  record = @db.get_cached_value_from_db(coord)
65
- if !record.nil?
66
- cached_server_response << record
67
- else
64
+ if record.nil?
68
65
  new_coords['coordinates'].push(coord)
66
+ else
67
+ cached_server_response << record
69
68
  end
70
69
  end
71
70
  [new_coords, cached_server_response]
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # Copyright 2019-Present Sonatype Inc.
3
5
  #
@@ -18,6 +20,7 @@ require 'tty-spinner'
18
20
  require 'pastel'
19
21
 
20
22
  module Chelsea
23
+ # the spinner we use in Chelsea, needs work
21
24
  class Spinner
22
25
  def initialize
23
26
  @pastel = Pastel.new
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # Copyright 2019-Present Sonatype Inc.
3
5
  #
@@ -15,5 +17,5 @@
15
17
  #
16
18
 
17
19
  module Chelsea
18
- VERSION = '0.0.27'.freeze
20
+ VERSION = '0.0.28'
19
21
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chelsea
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.27
4
+ version: 0.0.28
5
5
  platform: ruby
6
6
  authors:
7
7
  - Allister Beharry
@@ -11,109 +11,109 @@ cert_chain: []
11
11
  date: 2021-01-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: tty-font
14
+ name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.5.0
19
+ version: 1.2.0
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '3'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
- - - "~>"
27
+ - - ">="
25
28
  - !ruby/object:Gem::Version
26
- version: 0.5.0
29
+ version: 1.2.0
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '3'
27
33
  - !ruby/object:Gem::Dependency
28
- name: tty-spinner
34
+ name: ox
29
35
  requirement: !ruby/object:Gem::Requirement
30
36
  requirements:
31
37
  - - "~>"
32
38
  - !ruby/object:Gem::Version
33
- version: 0.9.3
39
+ version: 2.13.2
34
40
  type: :runtime
35
41
  prerelease: false
36
42
  version_requirements: !ruby/object:Gem::Requirement
37
43
  requirements:
38
44
  - - "~>"
39
45
  - !ruby/object:Gem::Version
40
- version: 0.9.3
46
+ version: 2.13.2
41
47
  - !ruby/object:Gem::Dependency
42
- name: slop
48
+ name: pastel
43
49
  requirement: !ruby/object:Gem::Requirement
44
50
  requirements:
45
51
  - - "~>"
46
52
  - !ruby/object:Gem::Version
47
- version: 4.8.1
53
+ version: 0.7.2
48
54
  type: :runtime
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
51
57
  requirements:
52
58
  - - "~>"
53
59
  - !ruby/object:Gem::Version
54
- version: 4.8.1
60
+ version: 0.7.2
55
61
  - !ruby/object:Gem::Dependency
56
- name: pastel
62
+ name: rest-client
57
63
  requirement: !ruby/object:Gem::Requirement
58
64
  requirements:
59
65
  - - "~>"
60
66
  - !ruby/object:Gem::Version
61
- version: 0.7.2
67
+ version: 2.0.2
62
68
  type: :runtime
63
69
  prerelease: false
64
70
  version_requirements: !ruby/object:Gem::Requirement
65
71
  requirements:
66
72
  - - "~>"
67
73
  - !ruby/object:Gem::Version
68
- version: 0.7.2
74
+ version: 2.0.2
69
75
  - !ruby/object:Gem::Dependency
70
- name: rest-client
76
+ name: slop
71
77
  requirement: !ruby/object:Gem::Requirement
72
78
  requirements:
73
79
  - - "~>"
74
80
  - !ruby/object:Gem::Version
75
- version: 2.0.2
81
+ version: 4.8.1
76
82
  type: :runtime
77
83
  prerelease: false
78
84
  version_requirements: !ruby/object:Gem::Requirement
79
85
  requirements:
80
86
  - - "~>"
81
87
  - !ruby/object:Gem::Version
82
- version: 2.0.2
88
+ version: 4.8.1
83
89
  - !ruby/object:Gem::Dependency
84
- name: bundler
90
+ name: tty-font
85
91
  requirement: !ruby/object:Gem::Requirement
86
92
  requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: 1.2.0
90
- - - "<"
93
+ - - "~>"
91
94
  - !ruby/object:Gem::Version
92
- version: '3'
95
+ version: 0.5.0
93
96
  type: :runtime
94
97
  prerelease: false
95
98
  version_requirements: !ruby/object:Gem::Requirement
96
99
  requirements:
97
- - - ">="
98
- - !ruby/object:Gem::Version
99
- version: 1.2.0
100
- - - "<"
100
+ - - "~>"
101
101
  - !ruby/object:Gem::Version
102
- version: '3'
102
+ version: 0.5.0
103
103
  - !ruby/object:Gem::Dependency
104
- name: ox
104
+ name: tty-spinner
105
105
  requirement: !ruby/object:Gem::Requirement
106
106
  requirements:
107
107
  - - "~>"
108
108
  - !ruby/object:Gem::Version
109
- version: 2.13.2
109
+ version: 0.9.3
110
110
  type: :runtime
111
111
  prerelease: false
112
112
  version_requirements: !ruby/object:Gem::Requirement
113
113
  requirements:
114
114
  - - "~>"
115
115
  - !ruby/object:Gem::Version
116
- version: 2.13.2
116
+ version: 0.9.3
117
117
  - !ruby/object:Gem::Dependency
118
118
  name: tty-table
119
119
  requirement: !ruby/object:Gem::Requirement
@@ -128,6 +128,20 @@ dependencies:
128
128
  - - "~>"
129
129
  - !ruby/object:Gem::Version
130
130
  version: 0.11.0
131
+ - !ruby/object:Gem::Dependency
132
+ name: byebug
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: 11.1.2
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: 11.1.2
131
145
  - !ruby/object:Gem::Dependency
132
146
  name: rake
133
147
  requirement: !ruby/object:Gem::Requirement
@@ -184,20 +198,6 @@ dependencies:
184
198
  - - "~>"
185
199
  - !ruby/object:Gem::Version
186
200
  version: 3.8.3
187
- - !ruby/object:Gem::Dependency
188
- name: byebug
189
- requirement: !ruby/object:Gem::Requirement
190
- requirements:
191
- - - "~>"
192
- - !ruby/object:Gem::Version
193
- version: 11.1.2
194
- type: :development
195
- prerelease: false
196
- version_requirements: !ruby/object:Gem::Requirement
197
- requirements:
198
- - - "~>"
199
- - !ruby/object:Gem::Version
200
- version: 11.1.2
201
201
  description:
202
202
  email:
203
203
  - allister.beharry@gmail.com
@@ -217,6 +217,7 @@ files:
217
217
  - ".github/pull_request_template.md"
218
218
  - ".gitignore"
219
219
  - ".rspec"
220
+ - ".rubocop.yml"
220
221
  - ".vscode/launch.json"
221
222
  - CODE_OF_CONDUCT.md
222
223
  - CONTRIBUTORS.md
@@ -267,7 +268,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
267
268
  requirements:
268
269
  - - ">="
269
270
  - !ruby/object:Gem::Version
270
- version: '0'
271
+ version: 2.6.6
271
272
  required_rubygems_version: !ruby/object:Gem::Requirement
272
273
  requirements:
273
274
  - - ">="