myprecious 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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!