myprecious 0.1.2 → 0.2.0

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: be0d5a3297a77bea22a403968fa7c3a23f2fd7c2d4ce96b957c832788f29de91
4
- data.tar.gz: d82b540dbb7be2ffb7601fe571cc07482d2fbe87563b8d78d937bc791bd22d3f
3
+ metadata.gz: d1d5adc30e0ccc2fb1c7a183402c7ac0015a9fbdde70e18267ad61d21bf4d790
4
+ data.tar.gz: '07394d3a1ff9a238220b9ae4d2be7328a9878ee08c47ec7179e9564af53e9e60'
5
5
  SHA512:
6
- metadata.gz: 79e7317fa010abdc6ab9d7207d7be1229d85e2ba2a53cb2225e1a8bed7c95063d51294fab4917f1449e106210e1aa3ef5c52fa743bde9e6fb85f582a6942694a
7
- data.tar.gz: 0fb7d8ec2c159fe58867b2f5cbbfa95a9a27c7f0e6f020ea2f69fb2ecf55b49b2ed385857c3a6162670c07a39871b54724f2005c71dd6897e691438701d33316
6
+ metadata.gz: 6f152193febe7482d2e9aeb1f3435b56c84f127a897cd0e3c3f8e35c5027a93d955bcb04b09c1e16f6d3a691bb92e85ff15047e31ba0b956daa5664ff6430287
7
+ data.tar.gz: ba2ea748b6a1c5f566f28375776639cb774449c885b11db306d02f709b7d6c86d7727acad771fd9eedaab610fa5be93914aa30b30607ada1d66691a9362c1f20
@@ -4,5 +4,5 @@ require 'rubygems'
4
4
  require 'myprecious'
5
5
 
6
6
  MyPrecious::Program.run(
7
- on_error: (:exit_program! if ENV['TRACE_ERRORS'].to_s.empty?)
7
+ on_error: (:exit_program! unless MyPrecious.tracing_errors?)
8
8
  )
@@ -13,6 +13,7 @@ module MyPrecious
13
13
  ONE_DAY = 60 * 60 * 24
14
14
  end
15
15
  require 'myprecious/data_caches'
16
+ require 'myprecious/cves'
16
17
 
17
18
  module MyPrecious
18
19
  extend Rake::DSL
@@ -20,6 +21,10 @@ module MyPrecious
20
21
  Program = Rake::ToolkitProgram
21
22
  Program.title = "myprecious Dependecy Reporting Tool"
22
23
 
24
+ def self.tracing_errors?
25
+ !ENV['TRACE_ERRORS'].to_s.empty?
26
+ end
27
+
23
28
  def self.common_program_args(parser, args)
24
29
  parser.on(
25
30
  '-o', '--out FILE',
@@ -34,11 +39,12 @@ module MyPrecious
34
39
  fpath = Pathname(fpath)
35
40
  parser.invalid_args!("#{fpath} does not exist.") unless fpath.exist?
36
41
  args.target = fpath
42
+ CVEs.config_dir = fpath
37
43
  end
38
44
 
39
45
  parser.on(
40
46
  '--[no-]cache',
41
- "Control caching of gem information"
47
+ "Control caching of dependency information"
42
48
  ) {|v| MyPrecious.caching_disabled = v}
43
49
  end
44
50
 
@@ -273,13 +279,14 @@ module MyPrecious
273
279
  def common_col_title(attr)
274
280
  case attr
275
281
  when :current_version then 'Our Version'
276
- when :age then 'Age (in days)'
282
+ when :age then 'Age (days)'
277
283
  when :latest_version then 'Latest Version'
278
284
  when :latest_released then 'Date Available'
279
285
  when :recommended_version then 'Recommended Version'
280
286
  when :license then 'License Type'
281
287
  when :changelog then 'Change Log'
282
288
  when :obsolescence then 'How Bad'
289
+ when :cves then 'CVEs'
283
290
  else
284
291
  warn("'#{attr}' column does not have a mapped name")
285
292
  attr
@@ -331,7 +338,7 @@ module MyPrecious
331
338
  # to some extent like frozen Array instances.
332
339
  #
333
340
  class ColumnOrder
334
- DEFAULT = %i[name current_version age latest_version latest_released recommended_version license changelog].freeze
341
+ DEFAULT = %i[name current_version age latest_version latest_released recommended_version cves license changelog].freeze
335
342
  COLUMN_FROM_TEXT_NAME = {
336
343
  'gem' => :name,
337
344
  'package' => :name,
@@ -340,10 +347,11 @@ module MyPrecious
340
347
  'how bad' => :obsolescence,
341
348
  'latest version' => :latest_version,
342
349
  'date available' => :latest_released,
343
- 'age (in days)' => :age,
350
+ 'age (days)' => :age,
344
351
  'license type' => :license,
345
352
  /change ?log/ => :changelog,
346
353
  'recommended version' => :recommended_version,
354
+ 'cves' => :cves,
347
355
  }
348
356
 
349
357
  def initialize
@@ -422,80 +430,151 @@ module MyPrecious
422
430
  # and returns enhanced Markdown for selected columns (e.g. +name+).
423
431
  #
424
432
  class MarkdownAdapter
433
+ QS_VALUE_UNSAFE = /#{URI::UNSAFE}|[&=]/
434
+
425
435
  def initialize(dep)
426
436
  super()
427
437
  @dependency = dep
428
438
  end
429
439
  attr_reader :dependency
430
440
 
441
+ def self.embellish(attr_name, &blk)
442
+ define_method(attr_name) do
443
+ value = begin
444
+ dependency.send(attr_name)
445
+ rescue NoMethodError
446
+ raise
447
+ rescue StandardError
448
+ return "(error)"
449
+ end
450
+
451
+ begin
452
+ instance_exec(value, &blk)
453
+ rescue StandardError => ex
454
+ err_key = [ex.backtrace[0], ex.to_s]
455
+ unless (@errors ||= Set.new).include?(err_key)
456
+ @errors << err_key
457
+ if MyPrecious.tracing_errors?
458
+ $stderr.puts("Traceback (most recent call last):")
459
+ ex.backtrace[1..-1].reverse.each_with_index do |loc, i|
460
+ $stderr.puts("#{(i + 1).to_s.rjust(8)}: #{loc}")
461
+ end
462
+ $stderr.puts("#{ex.backtrace[0]}: #{ex} (#{ex.class} while computing #{attr_name})")
463
+ else
464
+ $stderr.puts("#{ex} (while computing #{attr_name})")
465
+ end
466
+ end
467
+ value
468
+ end
469
+ end
470
+ end
471
+
431
472
  ##
432
473
  # Generate Markdown linking the +name+ to the homepage for the dependency
433
474
  #
434
- def name
475
+ embellish(:name) do |base_val|
435
476
  cswatch = begin
436
477
  color_swatch + ' '
437
478
  rescue StandardError
438
479
  ''
439
480
  end
440
- "#{cswatch}[#{dependency.name}](#{dependency.homepage_uri})"
441
- rescue StandardError
442
- dependency.name
481
+ "#{cswatch}[#{base_val}](#{dependency.homepage_uri})"
482
+ rescue Gems::NotFound
483
+ base_val
484
+ end
485
+
486
+ ##
487
+ # Decorate the latest version as a link to the release history
488
+ #
489
+ embellish(:latest_version) do |base_val|
490
+ release_history_url = begin
491
+ dependency.release_history_url
492
+ rescue StandardError
493
+ nil
494
+ end
495
+
496
+ if release_history_url
497
+ "[#{base_val}](#{release_history_url})"
498
+ else
499
+ base_val
500
+ end
443
501
  end
444
502
 
445
503
  ##
446
504
  # Include information about temporal difference between current and
447
505
  # recommended versions
448
506
  #
449
- def recommended_version
450
- recommended_version = dependency.recommended_version
451
- if dependency.current_version < recommended_version
452
- span_comment = begin
453
- if days_newer = dependency.days_between_current_and_recommended
454
- " -- #{days_newer} days newer"
455
- else
456
- ""
457
- end
507
+ embellish(:recommended_version) do |recced_ver|
508
+ if recced_ver.kind_of?(String)
509
+ recced_ver = Gem::Version.new(recced_ver)
510
+ end
511
+ next recced_ver if dependency.current_version.nil? || dependency.current_version >= recced_ver
512
+
513
+ span_comment = begin
514
+ if days_newer = dependency.days_between_current_and_recommended
515
+ " -- #{days_newer} days newer"
516
+ else
517
+ ""
458
518
  end
459
- "**#{recommended_version}**#{span_comment}"
460
- else
461
- recommended_version
462
519
  end
463
- rescue StandardError
464
- recommended_version || "(error)"
520
+
521
+ cve_url = URI('https://nvd.nist.gov/products/cpe/search/results')
522
+ cve_url.query = URI.encode_www_form(
523
+ keyword: "cpe:2.3:a:*:#{dependency.name.downcase}:#{recced_ver}",
524
+ )
525
+
526
+ "**#{recced_ver}**#{span_comment} ([current CVEs](#{cve_url}))"
465
527
  end
466
528
 
467
529
  ##
468
530
  # Include update info in the license column
469
531
  #
470
- def license
471
- value = dependency.license
472
- if value.update_info
473
- "#{value}<br/>(#{value.update_info})"
532
+ embellish(:license) do |base_val|
533
+ begin
534
+ update_info = base_val.update_info
535
+ rescue NoMethodError
536
+ base_val
474
537
  else
475
- value
538
+ update_info.to_s.empty? ? base_val : "#{base_val}<br/>(#{update_info})"
476
539
  end
477
- rescue StandardError
478
- "(error)"
479
540
  end
480
541
 
481
542
  ##
482
543
  # Render short links for http: or https: changelog URLs
483
544
  #
484
- def changelog
485
- base_val = begin
486
- dependency.changelog
487
- rescue StandardError
488
- return "(error)"
489
- end
490
-
545
+ embellish(:changelog) do |base_val|
491
546
  begin
492
547
  uri = URI.parse(base_val)
493
548
  if ['http', 'https'].include?(uri.scheme)
494
- return "[on #{uri.hostname}](#{base_val})"
549
+ next "[on #{uri.hostname}](#{base_val})"
495
550
  end
496
551
  rescue StandardError
497
552
  end
498
- return base_val
553
+
554
+ base_val
555
+ end
556
+
557
+ ##
558
+ # Render links to NIST's NVD
559
+ #
560
+ NVD_CVE_URL_TEMPLATE = "https://nvd.nist.gov/vuln/detail/%s"
561
+ embellish(:cves) do |base_val|
562
+ base_val.map do |cve|
563
+ link_text_parts = [cve]
564
+ addnl_info_parts = []
565
+ begin
566
+ addnl_info_parts << cve.vendors.to_a.join(',') unless cve.vendors.empty?
567
+ rescue StandardError
568
+ end
569
+ begin
570
+ addnl_info_parts << cve.score.to_s if cve.score
571
+ rescue StandardError
572
+ end
573
+ unless addnl_info_parts.empty?
574
+ link_text_parts << "(#{addnl_info_parts.join(' - ')})"
575
+ end
576
+ "[#{link_text_parts.join(' ')}](#{NVD_CVE_URL_TEMPLATE % cve})"
577
+ end.join(', ')
499
578
  end
500
579
 
501
580
  def obsolescence
@@ -508,10 +587,16 @@ module MyPrecious
508
587
  # Get a CSS-style hex color code corresponding to the obsolescence of the dependency
509
588
  #
510
589
  def color
590
+ red = "fb0e0e"
591
+
592
+ if (dependency.cves.map(&:score).compact.max || 0) >= 7
593
+ return red
594
+ end
595
+
511
596
  case dependency.obsolescence
512
597
  when :mild then "dde418"
513
598
  when :moderate then "f9b733"
514
- when :severe then "fb0e0e"
599
+ when :severe then red
515
600
  else "4dda1b"
516
601
  end
517
602
  end
@@ -0,0 +1,239 @@
1
+ require 'date'
2
+ require 'digest'
3
+ require 'json'
4
+ require 'myprecious/data_caches'
5
+ require 'open3'
6
+ require 'pathname'
7
+ require 'rest-client'
8
+ require 'set'
9
+
10
+ module MyPrecious
11
+ module CVEs
12
+ extend DataCaching
13
+
14
+ MIN_GAP_SECONDS = 5
15
+ CONFIG_FILE = '.myprecious-cves.rb'
16
+
17
+ CVE_DATA_CACHE_DIR = MyPrecious.data_cache(DATA_DIR / "cve-data")
18
+
19
+ class <<self
20
+ attr_reader :config_dir
21
+
22
+ def config_dir=(val)
23
+ @config_dir = Pathname(val)
24
+ end
25
+ end
26
+
27
+ def self.last_query_time
28
+ @last_query_time ||= DateTime.now - 1
29
+ end
30
+
31
+ def self.queried!
32
+ @last_query_time = DateTime.now
33
+ end
34
+
35
+ ##
36
+ # If you don't specify version, you get to match against the applicable
37
+ # configurations on your own to determine which CVEs returned apply to
38
+ # the versions of the named package in which you are interested
39
+ #
40
+ def self.get_for(package_name, version='*')
41
+ nvd_url = URI("https://services.nvd.nist.gov/rest/json/cves/1.0")
42
+ nvd_url.query = URI.encode_www_form(
43
+ cpeMatchString: "cpe:2.3:a:*:#{package_name.downcase}:#{version}:*:*:*:*:*:*:*",
44
+ )
45
+
46
+ cache = CVE_DATA_CACHE_DIR / "#{Digest::SHA256.hexdigest(nvd_url.to_s)}.json"
47
+ cve_data = apply_cache(cache) do
48
+ # Use last_query_time to sleep if necessary
49
+ wait_time = MIN_GAP_SECONDS - (DateTime.now - last_query_time) * 24 * 3600
50
+ if wait_time > 0
51
+ sleep(wait_time)
52
+ end
53
+
54
+ response = RestClient.get(nvd_url.to_s)
55
+ queried!
56
+
57
+ JSON.parse(response.body)
58
+ end
59
+
60
+ begin
61
+ return cve_data['result']['CVE_Items'].map do |e|
62
+ applicability = objectify_configurations(package_name, e['configurations'])
63
+ score = (((e['impact'] || {})['baseMetricV3'] || {})['cvssV3'] || {})['baseScore']
64
+ cve = CVERecord.new(
65
+ e['cve']['CVE_data_meta']['ID'],
66
+ applicability.respond_to?(:vendors) ? applicability.vendors : nil,
67
+ score
68
+ )
69
+
70
+ [cve, applicability]
71
+ end.reject {|cve, a| a.respond_to?(:applies_to?) && !a.applies_to?(version)}
72
+ rescue StandardError => e
73
+ $stderr.puts "[WARN] #{e}\n\n#{JSON.dump(cve_data)}\n\n"
74
+ []
75
+ end
76
+ end
77
+
78
+ def self.config
79
+ if !@config && config_dir
80
+ if (config_path = config_dir / CONFIG_FILE).exist?
81
+ @config = begin
82
+ config_prog_output, status = Open3.capture2(RbConfig.ruby, config_path.to_s)
83
+ if status.success?
84
+ JSON.parse(config_prog_output)
85
+ else
86
+ $stderr.puts "#{config_path} did not exit cleanly (code ${status.exitstatus})"
87
+ {}
88
+ end
89
+ rescue StandardError
90
+ end
91
+
92
+ unless @config.kind_of?(Hash)
93
+ $stderr.puts "#{config_path} did not output a JSON configuration"
94
+ @config = {}
95
+ end
96
+ else
97
+ @config = {}
98
+ end
99
+ end
100
+ @config ||= {}
101
+ end
102
+
103
+ def self.objectify_configurations(package_name, configs)
104
+ if configs.kind_of?(Hash) && configs['CVE_data_version'] == "4.0"
105
+ Applicability_V4_0.new(package_name, configs)
106
+ else
107
+ configs
108
+ end
109
+ end
110
+
111
+ class CVERecord < String
112
+ def initialize(id, vendors, score)
113
+ super(id)
114
+
115
+ @vendors = vendors
116
+ @score = score
117
+ end
118
+
119
+ attr_accessor :vendors, :score
120
+ end
121
+
122
+ class Applicability_V4_0 < Hash
123
+ def initialize(package, configs)
124
+ super()
125
+ self.update(configs)
126
+ @package = package.downcase
127
+ end
128
+ attr_reader :package
129
+
130
+ def nodes
131
+ self['nodes']
132
+ end
133
+
134
+ def applies_to?(version)
135
+ package_nodes(nodes).any? do |node|
136
+ version_matches_node?(version, node)
137
+ end
138
+ end
139
+
140
+ def vendors
141
+ Set.new(each_vulnerable_cpe.map do |cpe|
142
+ cpe.split(':')[3]
143
+ end)
144
+ end
145
+
146
+ def package_nodes(node_list)
147
+ node_list.select do |node|
148
+ node['children'] || node['cpe_match'].any? do |pattern|
149
+ pattern['cpe23Uri'] =~ package_cpe_regexp
150
+ end
151
+ end
152
+ end
153
+
154
+ def each_vulnerable_cpe
155
+ return enum_for(:each_vulnerable_cpe) unless block_given?
156
+
157
+ remaining = nodes.to_a.dup
158
+ while (node = remaining.shift)
159
+ if node['children']
160
+ remaining.insert(0, *node['children'])
161
+ else
162
+ node['cpe_match'].each do |pattern|
163
+ next unless pattern['vulnerable']
164
+ cpe = pattern['cpe23Uri']
165
+ if package_cpe_regexp =~ cpe
166
+ yield cpe
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
172
+
173
+ def package_cpe_regexp
174
+ /^cpe:2.3:a:[^:]*:#{package}(:|$)/
175
+ end
176
+
177
+ def version_matches_node?(version, node)
178
+ test = (node['operator'] == 'AND') ? :all? : :any?
179
+ if node['children']
180
+ return node['children'].send(test) {|child| version_matches_node?(version, child)}
181
+ end
182
+
183
+ return node['cpe_match'].any? do |pattern|
184
+ cpe_entry_indicates_vulnerable_version?(version, pattern)
185
+ end
186
+ end
187
+
188
+ def cpe_entry_indicates_vulnerable_version?(version, pattern)
189
+ return false unless pattern['vulnerable']
190
+
191
+ cpe_vendor, cpe_product, cpe_version, cpe_update = pattern['cpe23Uri'].split(':')[3,4]
192
+ return false if (CVEs.config['blockedProducts'] ||= []).include?([cpe_vendor, cpe_product].join(':'))
193
+ return false if cpe_product != @package
194
+ if version == '*'
195
+ return true
196
+ end
197
+ return false unless [nil, '*', '-'].include?(cpe_update) # We'll ignore prerelease versions
198
+ if cpe_version != '*' && cpe_version == version
199
+ return true
200
+ end
201
+
202
+ if (range_start = pattern['versionStartIncluding'])
203
+ range_test = :<=
204
+ elsif (range_start = pattern['versionStartExcluding'])
205
+ range_test = :<
206
+ else
207
+ range_test = nil
208
+ end
209
+ if range_test && !version_compare(range_start, version).send(range_test, 0)
210
+ return false
211
+ end
212
+
213
+ if (range_end = pattern['versionEndIncluding'])
214
+ range_test = :<=
215
+ elsif (range_end = pattern['versionEndExcluding'])
216
+ range_test = :<
217
+ else
218
+ range_test = nil
219
+ end
220
+ if range_test && !version_compare(version, range_end).send(range_test, 0)
221
+ return false
222
+ end
223
+
224
+ return range_start || range_end
225
+ end
226
+
227
+ ##
228
+ # Return a <=> b for version strings a and b
229
+ #
230
+ def version_compare(a, b)
231
+ make_comparable(a) <=> make_comparable(b)
232
+ end
233
+
234
+ def make_comparable(ver_str)
235
+ ver_str.split('.').map {|p| p.to_i}
236
+ end
237
+ end
238
+ end
239
+ end
@@ -1,5 +1,7 @@
1
+ require 'date'
1
2
  require 'json'
2
3
  require 'myprecious'
4
+ require 'myprecious/cves'
3
5
  require 'myprecious/data_caches'
4
6
  require 'open-uri'
5
7
  require 'open3'
@@ -212,7 +214,7 @@ module MyPrecious
212
214
  end
213
215
 
214
216
  def latest_released
215
- versions_with_release[0][1]
217
+ Date.parse(versions_with_release[0][1].to_s).to_s
216
218
  end
217
219
 
218
220
  ##
@@ -260,12 +262,25 @@ module MyPrecious
260
262
  LicenseDescription.new(get_package_info['info']['license'])
261
263
  end
262
264
 
265
+ def cves
266
+ resolve_name!
267
+ resolve_version!
268
+
269
+ CVEs.get_for(name, current_version.to_s).map do |cve, applicability|
270
+ cve
271
+ end
272
+ end
273
+
263
274
  def changelog
264
275
  # This is wrong
265
276
  info = get_package_info['info']
266
277
  return info['project_url']
267
278
  end
268
279
 
280
+ def release_history_url
281
+ "https://pypi.org/project/#{name}/#history"
282
+ end
283
+
269
284
  def days_between_current_and_recommended
270
285
  v, cv_rel = versions_with_release.find do |v, r|
271
286
  case
@@ -1,4 +1,6 @@
1
+ require 'date'
1
2
  require 'gems'
3
+ require 'myprecious/cves'
2
4
  require 'myprecious/data_caches'
3
5
  require 'pathname'
4
6
 
@@ -33,11 +35,11 @@ module MyPrecious
33
35
  # used by the project. Each gem name will appear in only one +:current+
34
36
  # iteration, but may occur in multiple +:reqs+ iterations.
35
37
  #
36
- def self.each_gem_used(fpath)
38
+ def self.each_gem_used(fpath, gemfile: 'Gemfile')
37
39
  return enum_for(:each_gem_used, fpath) unless block_given?
38
40
 
39
- gemlock = Pathname(fpath).join('Gemfile.lock')
40
- raise "No Gemfile.lock in #{fpath}" unless gemlock.exist?
41
+ gemlock = Pathname(fpath).join(gemfile + '.lock')
42
+ raise "No #{gemfile}.lock in #{fpath}" unless gemlock.exist?
41
43
 
42
44
  section = nil
43
45
  gemlock.each_line do |l|
@@ -64,9 +66,9 @@ module MyPrecious
64
66
  # #current_version values and meaningful information in #version_reqs,
65
67
  # as indicated in the Gemfile.lock for +fpath+.
66
68
  #
67
- def self.accum_gem_lock_info(fpath)
69
+ def self.accum_gem_lock_info(fpath, **opts)
68
70
  {}.tap do |gems|
69
- each_gem_used(fpath) do |entry_type, name, verreq|
71
+ each_gem_used(fpath, **opts) do |entry_type, name, verreq|
70
72
  g = (gems[name] ||= RubyGemInfo.new(name))
71
73
 
72
74
  case entry_type
@@ -156,7 +158,7 @@ module MyPrecious
156
158
 
157
159
  def latest_released
158
160
  return nil if versions_with_release.empty?
159
- versions_with_release[0][1]
161
+ Date.parse(versions_with_release[0][1].to_s).to_s
160
162
  end
161
163
 
162
164
  ##
@@ -202,6 +204,12 @@ module MyPrecious
202
204
  end
203
205
  end
204
206
 
207
+ def cves
208
+ CVEs.get_for(name, current_version && current_version.to_s).map do |cve, appl|
209
+ cve
210
+ end
211
+ end
212
+
205
213
  def changelogs
206
214
  gv_data = get_gems_versions.sort_by {|v| Gem::Version.new(v['number'])}.reverse
207
215
  if current_version
@@ -214,6 +222,16 @@ module MyPrecious
214
222
  changelogs[0]
215
223
  end
216
224
 
225
+ def release_history_url
226
+ "https://rubygems.org/gems/#{name}/versions" if (
227
+ begin
228
+ get_gems_versions
229
+ rescue StandardError
230
+ nil
231
+ end
232
+ )
233
+ end
234
+
217
235
  def days_between_current_and_recommended
218
236
  v, cv_rel = versions_with_release.find {|v, r| v == current_version} || []
219
237
  v, rv_rel = versions_with_release.find {|v, r| v == recommended_version} || []
metadata CHANGED
@@ -1,55 +1,55 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: myprecious
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Balki Kodarapu
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-17 00:00:00.000000000 Z
11
+ date: 2020-12-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: gems
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '1.0'
20
17
  - - ">="
21
18
  - !ruby/object:Gem::Version
22
19
  version: 1.0.0
20
+ - - "~>"
21
+ - !ruby/object:Gem::Version
22
+ version: '1.0'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
- - - "~>"
28
- - !ruby/object:Gem::Version
29
- version: '1.0'
30
27
  - - ">="
31
28
  - !ruby/object:Gem::Version
32
29
  version: 1.0.0
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.0'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: git
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '1.5'
40
37
  - - ">="
41
38
  - !ruby/object:Gem::Version
42
39
  version: 1.5.0
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: '1.5'
43
43
  type: :runtime
44
44
  prerelease: false
45
45
  version_requirements: !ruby/object:Gem::Requirement
46
46
  requirements:
47
- - - "~>"
48
- - !ruby/object:Gem::Version
49
- version: '1.5'
50
47
  - - ">="
51
48
  - !ruby/object:Gem::Version
52
49
  version: 1.5.0
50
+ - - "~>"
51
+ - !ruby/object:Gem::Version
52
+ version: '1.5'
53
53
  - !ruby/object:Gem::Dependency
54
54
  name: rake-toolkit_program
55
55
  requirement: !ruby/object:Gem::Requirement
@@ -68,22 +68,22 @@ dependencies:
68
68
  name: rest-client
69
69
  requirement: !ruby/object:Gem::Requirement
70
70
  requirements:
71
- - - "~>"
72
- - !ruby/object:Gem::Version
73
- version: 2.0.2
74
71
  - - ">="
75
72
  - !ruby/object:Gem::Version
76
73
  version: '2.0'
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: 2.0.2
77
77
  type: :runtime
78
78
  prerelease: false
79
79
  version_requirements: !ruby/object:Gem::Requirement
80
80
  requirements:
81
- - - "~>"
82
- - !ruby/object:Gem::Version
83
- version: 2.0.2
84
81
  - - ">="
85
82
  - !ruby/object:Gem::Version
86
83
  version: '2.0'
84
+ - - "~>"
85
+ - !ruby/object:Gem::Version
86
+ version: 2.0.2
87
87
  - !ruby/object:Gem::Dependency
88
88
  name: parslet
89
89
  requirement: !ruby/object:Gem::Requirement
@@ -178,6 +178,7 @@ extra_rdoc_files: []
178
178
  files:
179
179
  - bin/myprecious
180
180
  - lib/myprecious.rb
181
+ - lib/myprecious/cves.rb
181
182
  - lib/myprecious/data_caches.rb
182
183
  - lib/myprecious/python_packages.rb
183
184
  - lib/myprecious/ruby_gems.rb
@@ -200,7 +201,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
200
201
  - !ruby/object:Gem::Version
201
202
  version: '0'
202
203
  requirements: []
203
- rubygems_version: 3.1.2
204
+ rubygems_version: 3.0.3
204
205
  signing_key:
205
206
  specification_version: 4
206
207
  summary: Your precious dependencies!