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 +4 -4
- data/bin/myprecious +1 -1
- data/lib/myprecious.rb +124 -39
- data/lib/myprecious/cves.rb +239 -0
- data/lib/myprecious/python_packages.rb +16 -1
- data/lib/myprecious/ruby_gems.rb +24 -6
- metadata +22 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d1d5adc30e0ccc2fb1c7a183402c7ac0015a9fbdde70e18267ad61d21bf4d790
|
4
|
+
data.tar.gz: '07394d3a1ff9a238220b9ae4d2be7328a9878ee08c47ec7179e9564af53e9e60'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6f152193febe7482d2e9aeb1f3435b56c84f127a897cd0e3c3f8e35c5027a93d955bcb04b09c1e16f6d3a691bb92e85ff15047e31ba0b956daa5664ff6430287
|
7
|
+
data.tar.gz: ba2ea748b6a1c5f566f28375776639cb774449c885b11db306d02f709b7d6c86d7727acad771fd9eedaab610fa5be93914aa30b30607ada1d66691a9362c1f20
|
data/bin/myprecious
CHANGED
data/lib/myprecious.rb
CHANGED
@@ -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
|
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 (
|
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 (
|
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
|
-
|
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}[#{
|
441
|
-
rescue
|
442
|
-
|
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
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
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
|
-
|
464
|
-
|
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
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
549
|
+
next "[on #{uri.hostname}](#{base_val})"
|
495
550
|
end
|
496
551
|
rescue StandardError
|
497
552
|
end
|
498
|
-
|
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
|
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
|
data/lib/myprecious/ruby_gems.rb
CHANGED
@@ -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('
|
40
|
-
raise "No
|
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.
|
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-
|
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.
|
204
|
+
rubygems_version: 3.0.3
|
204
205
|
signing_key:
|
205
206
|
specification_version: 4
|
206
207
|
summary: Your precious dependencies!
|