chelsea 0.0.2 → 0.0.3

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: 12e5cefdcebf95177fb2a4dc4d3dd21803549b66a16570ecd7244191f70665c2
4
- data.tar.gz: 6d42d99b6d1fbf033bcf1746857e92c819e0ad887dc7c680387a653b8d0d76ba
3
+ metadata.gz: 1633315a7f5da46a2c43414058160959b0be5202a3c7a6aa333e99f91f48a755
4
+ data.tar.gz: ac7a5f1f3c77061ccf54edf458758b621038f74ca9e272261cb0dc417cb94298
5
5
  SHA512:
6
- metadata.gz: '09e122e3b9957a32ef4b270404a7388a13009564282cde771444852a5092e5b669d627c89260e7efda00d61af747184b159b48d9ad69603918d096bcff702ff3'
7
- data.tar.gz: 0a4cd07a064cd5c40346ef6bd17fce588e97bfd242063c3e4e22b3db5c864378349d6ba6ab6ac9d9f13afffba1a1d37939d207696ce398840ae20c53799ff7c7
6
+ metadata.gz: 1b7eea08843bed093b27054b29da075edfc63730836421e8151e0dfd661d06f030bf7be242c8a08e11d5c4aae49434d3976f4b8a628962f44fa8e8b787979eea
7
+ data.tar.gz: c946fd43b812750e8d3ebe7b4c13914ca54e257c6b86a63d3abb1224bec4d94af027a1e4dcc7cd65940af23091b5e82d10d99e5397c1406e6d4199f2fb2f49b8
@@ -30,6 +30,15 @@ jobs:
30
30
  bundle exec rspec --format progress \
31
31
  --format RspecJunitFormatter \
32
32
  --out test_results/rspec.xml
33
+ - run:
34
+ name: Build gem
35
+ command: gem build chelsea.gemspec
36
+ - run:
37
+ name: Install gem
38
+ command: gem install ./chelsea-*.gem
39
+ - run:
40
+ name: Dogfood
41
+ command: chelsea --file Gemfile.lock
33
42
  - store_test_results:
34
43
  path: test_results
35
44
  release:
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- chelsea (0.0.1)
5
- bundler (~> 2.0.0)
4
+ chelsea (0.0.2)
5
+ bundler (>= 1.2.0, < 3)
6
6
  pastel (~> 0.7.2)
7
7
  rest-client (~> 2.0.2)
8
8
  slop (~> 4.8.0)
@@ -43,6 +43,8 @@ GEM
43
43
  diff-lcs (>= 1.2.0, < 2.0)
44
44
  rspec-support (~> 3.9.0)
45
45
  rspec-support (3.9.2)
46
+ rspec_junit_formatter (0.4.1)
47
+ rspec-core (>= 2, < 4, != 2.12.0)
46
48
  slop (4.8.0)
47
49
  tty-color (0.5.1)
48
50
  tty-cursor (0.7.1)
@@ -60,6 +62,7 @@ DEPENDENCIES
60
62
  chelsea!
61
63
  rake (~> 10.0)
62
64
  rspec (~> 3.0)
65
+ rspec_junit_formatter (~> 0.4.1)
63
66
 
64
67
  BUNDLED WITH
65
68
  2.0.2
data/README.md CHANGED
@@ -41,7 +41,7 @@ Options:
41
41
 
42
42
  Most basic usage is:
43
43
 
44
- `chelsea --file name.gemspec`
44
+ `chelsea --file Gemfile.lock`
45
45
 
46
46
  ## Development
47
47
 
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
- require "chelsea"
3
+ require_relative "../lib/chelsea"
4
4
 
5
5
  Chelsea::CLI.new.main
@@ -35,7 +35,6 @@ Gem::Specification.new do |spec|
35
35
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
36
36
  spec.require_paths = ["lib"]
37
37
 
38
-
39
38
  spec.add_dependency "tty-font", "~> 0.5.0"
40
39
  spec.add_dependency "tty-spinner", "~> 0.9.3"
41
40
  spec.add_dependency "slop", "~> 4.8.0"
@@ -5,6 +5,9 @@ require 'tty-spinner'
5
5
  require 'bundler'
6
6
  require 'bundler/lockfile_parser'
7
7
  require_relative 'version'
8
+ require 'rubygems'
9
+ require 'rubygems/commands/dependency_command'
10
+ require 'pstore'
8
11
 
9
12
  module Chelsea
10
13
  class Gems
@@ -17,6 +20,8 @@ module Chelsea
17
20
  @coordinates = Hash.new()
18
21
  @coordinates["coordinates"] = Array.new()
19
22
  @server_response = Array.new()
23
+ @reverse_deps = Hash.new()
24
+ @store = PStore.new(get_db_store_location())
20
25
 
21
26
  if not gemfile_lock_file_exists()
22
27
  return
@@ -28,6 +33,12 @@ module Chelsea
28
33
  )
29
34
  end
30
35
 
36
+ def get_db_store_location()
37
+ initial_path = File.join("#{Dir.home}", ".ossindex")
38
+ Dir.mkdir(initial_path) unless File.exists? initial_path
39
+ path = File.join(initial_path, "chelsea.pstore")
40
+ end
41
+
31
42
  def execute(input: $stdin, output: $stdout)
32
43
  n = get_dependencies()
33
44
  if n == 0
@@ -58,6 +69,10 @@ module Chelsea
58
69
  spinner = TTY::Spinner.new(format, success_mark: @pastel.green('+'), hide_cursor: true)
59
70
  spinner.auto_spin()
60
71
 
72
+ reverse = Gem::Commands::DependencyCommand.new
73
+ reverse.options[:reverse_dependencies] = true
74
+ @reverse_deps = reverse.reverse_dependencies(@lockfile.specs)
75
+
61
76
  @lockfile.specs.each do |gem|
62
77
  @dependencies[gem.name] = [gem.name, gem.version]
63
78
  rescue StandardError => e
@@ -95,28 +110,36 @@ module Chelsea
95
110
  end
96
111
  end
97
112
 
98
- def get_user_agent()
99
- user_agent = "chelsea/#{Chelsea::VERSION}"
100
-
101
- user_agent
102
- end
103
-
104
113
  def get_vulns()
105
114
  require 'json'
106
115
  require 'rest-client'
107
116
  format = "[#{@pastel.green(':spinner')}] " + @pastel.white("Making request to OSS Index server")
108
117
  spinner = TTY::Spinner.new(format, success_mark: @pastel.green('+'), hide_cursor: true)
109
118
  spinner.auto_spin()
110
- r = RestClient.post "https://ossindex.sonatype.org/api/v3/component-report", @coordinates.to_json,
111
- {content_type: :json, accept: :json, 'User-Agent': get_user_agent()}
112
- if r.code == 200
113
- @server_response = JSON.parse(r.body)
119
+
120
+ check_db_for_cached_values()
121
+
122
+ if @coordinates["coordinates"].count() > 0
123
+ chunked = Hash.new()
124
+ @coordinates["coordinates"].each_slice(128).to_a.each do |coords|
125
+ chunked["coordinates"] = coords
126
+ r = RestClient.post "https://ossindex.sonatype.org/api/v3/component-report", chunked.to_json,
127
+ {content_type: :json, accept: :json, 'User-Agent': get_user_agent()}
128
+
129
+ if r.code == 200
130
+ @server_response = @server_response.concat(JSON.parse(r.body))
131
+ save_values_to_db(JSON.parse(r.body))
132
+ spinner.success("...done.")
133
+ @server_response.count()
134
+ else
135
+ spinner.stop("...request failed.")
136
+ print_err "Error getting data from OSS Index server. Server returned non-success code #{r.code}."
137
+ 0
138
+ end
139
+ end
140
+ else
114
141
  spinner.success("...done.")
115
142
  @server_response.count()
116
- else
117
- spinner.stop("...request failed.")
118
- print_err "Error getting data from OSS Index server. Server returned non-success code #{r.code}."
119
- 0
120
143
  end
121
144
  rescue SocketError => e
122
145
  spinner.stop("...request failed.")
@@ -150,17 +173,42 @@ module Chelsea
150
173
  i += 1
151
174
  package = r["coordinates"]
152
175
  vulnerable = r["vulnerabilities"].length() > 0
176
+ coord = r["coordinates"].sub("pkg:gem/", "")
177
+ name = coord.split('@')[0]
178
+ version = coord.split('@')[1]
179
+ reverse_dep_coord = "#{name}-#{version}"
153
180
  if vulnerable
154
181
  puts @pastel.red("[#{i}/#{count}] - #{package} ") + @pastel.red.bold("Vulnerable.")
182
+ print_reverse_deps(@reverse_deps[reverse_dep_coord], name, version)
155
183
  r["vulnerabilities"].each do |k, v|
156
184
  puts @pastel.red.bold(" #{k}:#{v}")
157
185
  end
158
186
  else
159
187
  puts(@pastel.white("[#{i}/#{count}] - #{package} ") + @pastel.green.bold("No vulnerabilities found!"))
188
+ print_reverse_deps(@reverse_deps[reverse_dep_coord], name, version)
160
189
  end
161
190
  end
162
191
  end
163
192
 
193
+ def print_reverse_deps(reverse_deps, name, version)
194
+ reverse_deps.each do |dep|
195
+ dep.each do |gran|
196
+ if gran.class == String && !gran.include?(name)
197
+ # There is likely a fun and clever way to check @server-results, etc... and see if a dep is in there
198
+ # Right now this looks at all Ruby deps, so it might find some in your Library, but that don't belong to your project
199
+ puts "\tRequired by: " + gran
200
+ else
201
+ end
202
+ end
203
+ end
204
+ end
205
+
206
+ def to_purl(name, version)
207
+ purl = "pkg:gem/#{name}@#{version}"
208
+
209
+ purl
210
+ end
211
+
164
212
  def print_err(s)
165
213
  puts @pastel.red.bold(s)
166
214
  end
@@ -168,5 +216,61 @@ module Chelsea
168
216
  def print_success(s)
169
217
  puts @pastel.green.bold(s)
170
218
  end
219
+
220
+ private
221
+
222
+ def get_user_agent()
223
+ user_agent = "chelsea/#{Chelsea::VERSION}"
224
+
225
+ user_agent
226
+ end
227
+
228
+ # This method will take an array of values, and save them to a pstore database
229
+ # and as well set a TTL of Time.now to be checked later
230
+ def save_values_to_db(values)
231
+ values.each do |val|
232
+ if get_cached_value_from_db(val["coordinates"]).nil?
233
+ new_val = val.dup
234
+ new_val["ttl"] = Time.now
235
+ @store.transaction do
236
+ @store[new_val["coordinates"]] = new_val
237
+ end
238
+ end
239
+ end
240
+ end
241
+
242
+ # Checks pstore to see if a coordinate exists, and if it does also
243
+ # checks to see if it's ttl has expired. Returns nil unless a record
244
+ # is valid in the cache (ttl has not expired) and found
245
+ def get_cached_value_from_db(coordinate)
246
+ record = @store.transaction { @store[coordinate] }
247
+ if !record.nil?
248
+ diff = (Time.now - record['ttl']) / 3600
249
+ if diff > 12
250
+ return nil
251
+ else
252
+ return record
253
+ end
254
+ else
255
+ return nil
256
+ end
257
+ end
258
+
259
+ # Goes through the list of @coordinates and checks pstore for them, if it finds a valid coord
260
+ # it will add it to the server response. If it does not, it will append the coord to a new hash
261
+ # and eventually set @coordinates to the new hash, so we query OSS Index on only coords not in cache
262
+ def check_db_for_cached_values()
263
+ new_coords = Hash.new
264
+ new_coords["coordinates"] = Array.new
265
+ @coordinates["coordinates"].each do |coord|
266
+ record = get_cached_value_from_db(coord)
267
+ if !record.nil?
268
+ @server_response << record
269
+ else
270
+ new_coords["coordinates"].push(coord)
271
+ end
272
+ end
273
+ @coordinates = new_coords
274
+ end
171
275
  end
172
276
  end
@@ -1,3 +1,3 @@
1
1
  module Chelsea
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chelsea
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Allister Beharry
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-22 00:00:00.000000000 Z
11
+ date: 2020-03-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: tty-font