yavdb 0.1.0.pre.alpha.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +48 -0
- data/.gitignore +115 -0
- data/.rspec +3 -0
- data/.rubocop.yml +339 -0
- data/.ruby-version +1 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +85 -0
- data/LICENSE +661 -0
- data/README.md +87 -0
- data/Rakefile +6 -0
- data/bin/console +7 -0
- data/bin/setup +8 -0
- data/bin/vulndb +3 -0
- data/bin/vulnerabilitydb +3 -0
- data/bin/yavdb +5 -0
- data/lib/yavdb.rb +68 -0
- data/lib/yavdb/cli.rb +60 -0
- data/lib/yavdb/constants.rb +34 -0
- data/lib/yavdb/crawler.rb +52 -0
- data/lib/yavdb/database.rb +95 -0
- data/lib/yavdb/dtos/advisory.rb +102 -0
- data/lib/yavdb/source_types/git_repo.rb +35 -0
- data/lib/yavdb/sources/friends_of_php.rb +87 -0
- data/lib/yavdb/sources/nodesecurity_io.rb +120 -0
- data/lib/yavdb/sources/ossindex.rb +137 -0
- data/lib/yavdb/sources/ruby_advisory.rb +117 -0
- data/lib/yavdb/sources/snyk_io.rb +311 -0
- data/lib/yavdb/sources/victims.rb +105 -0
- data/lib/yavdb/utils/cache.rb +96 -0
- data/lib/yavdb/utils/exec.rb +36 -0
- data/lib/yavdb/utils/git.rb +61 -0
- data/lib/yavdb/utils/http.rb +56 -0
- data/lib/yavdb/utils/semver.rb +79 -0
- data/lib/yavdb/utils/zip.rb +64 -0
- data/lib/yavdb/version.rb +21 -0
- data/yavdb.gemspec +44 -0
- metadata +267 -0
@@ -0,0 +1,117 @@
|
|
1
|
+
# yavdb - The Free and Open Source vulnerability database
|
2
|
+
# Copyright (C) 2017-present Rodrigo Fernandes
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU Affero General Public License as
|
6
|
+
# published by the Free Software Foundation, either version 3 of the
|
7
|
+
# License, or (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU Affero General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU Affero General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
require 'date'
|
18
|
+
require 'yaml'
|
19
|
+
|
20
|
+
require_relative '../dtos/advisory'
|
21
|
+
require_relative '../source_types/git_repo'
|
22
|
+
|
23
|
+
module YAVDB
|
24
|
+
module Sources
|
25
|
+
module RubyAdvisory
|
26
|
+
class Client
|
27
|
+
|
28
|
+
REPOSITORY_URL = 'https://github.com/rubysec/ruby-advisory-db'.freeze
|
29
|
+
PACKAGE_MANAGER = 'rubygems'.freeze
|
30
|
+
|
31
|
+
def self.advisories
|
32
|
+
YAVDB::SourceTypes::GitRepo.search('gems/**/*.yml', REPOSITORY_URL).map do |repo_path, file_paths|
|
33
|
+
Dir.chdir(repo_path) do
|
34
|
+
file_paths.map do |file_path|
|
35
|
+
advisory_hash = YAML.load_file(file_path)
|
36
|
+
create(advisory_hash)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end.flatten
|
40
|
+
end
|
41
|
+
|
42
|
+
class << self
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def create(advisory_hash)
|
47
|
+
date = Date.strptime(advisory_hash['date'].to_s, '%Y-%m-%d')
|
48
|
+
severity = severity(advisory_hash['cvss_v2'], advisory_hash['cvss_v3'])
|
49
|
+
cve = ["CVE-#{advisory_hash['cve']}"]
|
50
|
+
references = references(advisory_hash)
|
51
|
+
vulnerable_versions = if advisory_hash['unaffected_versions'] || advisory_hash['patched_versions']
|
52
|
+
nil
|
53
|
+
else
|
54
|
+
['*']
|
55
|
+
end
|
56
|
+
|
57
|
+
YAVDB::Advisory.new(
|
58
|
+
"rubyadvisory:rubygems:#{advisory_hash['gem']}:#{date}",
|
59
|
+
advisory_hash['title'],
|
60
|
+
advisory_hash['description'],
|
61
|
+
advisory_hash['gem'],
|
62
|
+
vulnerable_versions,
|
63
|
+
advisory_hash['unaffected_versions'],
|
64
|
+
advisory_hash['patched_versions'],
|
65
|
+
severity,
|
66
|
+
PACKAGE_MANAGER,
|
67
|
+
cve,
|
68
|
+
nil, #:cwe
|
69
|
+
advisory_hash['osvdb'],
|
70
|
+
nil, #:cvss_v2_vector
|
71
|
+
advisory_hash['cvss_v2'],
|
72
|
+
nil, #:cvss_v3_vector
|
73
|
+
advisory_hash['cvss_v3'],
|
74
|
+
date,
|
75
|
+
date,
|
76
|
+
date,
|
77
|
+
['Rubysec'],
|
78
|
+
references,
|
79
|
+
advisory_hash['url']
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
83
|
+
def references(advisory_hash)
|
84
|
+
references = [REPOSITORY_URL]
|
85
|
+
|
86
|
+
if advisory_hash['related'] && advisory_hash['related']['url']
|
87
|
+
references.concat(advisory_hash['related']['url'])
|
88
|
+
else
|
89
|
+
references
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def severity(cvss_v2_score, cvss_v3_score)
|
94
|
+
if cvss_v3_score
|
95
|
+
severity_level(cvss_v3_score)
|
96
|
+
elsif cvss_v2_score
|
97
|
+
severity_level(cvss_v2_score)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def severity_level(cvss_score)
|
102
|
+
case cvss_score
|
103
|
+
when 0.0..3.3 then
|
104
|
+
'low'
|
105
|
+
when 3.3..6.6 then
|
106
|
+
'medium'
|
107
|
+
else
|
108
|
+
'high'
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,311 @@
|
|
1
|
+
# yavdb - The Free and Open Source vulnerability database
|
2
|
+
# Copyright (C) 2017-present Rodrigo Fernandes
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU Affero General Public License as
|
6
|
+
# published by the Free Software Foundation, either version 3 of the
|
7
|
+
# License, or (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU Affero General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU Affero General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
require 'oga'
|
18
|
+
require 'oga/xml/entities'
|
19
|
+
require 'kramdown'
|
20
|
+
|
21
|
+
require_relative '../dtos/advisory'
|
22
|
+
require_relative '../utils/http'
|
23
|
+
|
24
|
+
module YAVDB
|
25
|
+
module Sources
|
26
|
+
module SnykIO
|
27
|
+
class Client
|
28
|
+
|
29
|
+
BASE_URL = 'https://snyk.io'
|
30
|
+
BASE_VULN_URL = "#{BASE_URL}/vuln"
|
31
|
+
INFO_SEP = '#=#'
|
32
|
+
|
33
|
+
PACKAGE_MANAGERS_RSS_FEED = ['composer', 'golang', 'maven', 'npm', 'nuget', 'pip', 'rubygems'].freeze
|
34
|
+
|
35
|
+
PACKAGE_MANAGER_ALIAS = Hash[
|
36
|
+
'composer' => 'packagist',
|
37
|
+
'go' => 'go',
|
38
|
+
'maven' => 'maven',
|
39
|
+
'npm' => 'npm',
|
40
|
+
'nuget' => 'nuget',
|
41
|
+
'pip' => 'pypi',
|
42
|
+
'rubygems' => 'rubygems'
|
43
|
+
].freeze
|
44
|
+
|
45
|
+
def self.advisories
|
46
|
+
urls = fetch_advisory_urls
|
47
|
+
urls.map do |advisory_url|
|
48
|
+
advisory_page = get_page_html(advisory_url, true, 'snyk.io/advisories')
|
49
|
+
create(advisory_url, advisory_page)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class << self
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def fetch_advisory_urls
|
58
|
+
PACKAGE_MANAGERS_RSS_FEED.map do |pm|
|
59
|
+
fetch_advisory_recursive("#{BASE_VULN_URL}?type=#{pm}")
|
60
|
+
end.flatten
|
61
|
+
end
|
62
|
+
|
63
|
+
def fetch_advisory_recursive(page_url)
|
64
|
+
snykio = get_page_html(page_url, true, 'snyk.io/feed')
|
65
|
+
|
66
|
+
page_vuln_urls = snykio
|
67
|
+
.css('table tbody tr td span a')
|
68
|
+
.map { |anchor| anchor.get('href') }
|
69
|
+
.map { |link| link if link =~ %r{\/vuln\/.+} }.compact
|
70
|
+
|
71
|
+
next_urls = if page_vuln_urls.any?
|
72
|
+
next_url = snykio.css('a.pagination__next')
|
73
|
+
if next_url
|
74
|
+
fetch_advisory_recursive(next_url.first.get('href'))
|
75
|
+
else
|
76
|
+
[]
|
77
|
+
end
|
78
|
+
else
|
79
|
+
[]
|
80
|
+
end
|
81
|
+
|
82
|
+
page_vuln_urls
|
83
|
+
.concat(next_urls)
|
84
|
+
.map do |url|
|
85
|
+
full_url = url
|
86
|
+
full_url = "#{BASE_URL}#{url}" unless url.start_with?('http')
|
87
|
+
full_url
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def create(advisory_url, advisory_page)
|
92
|
+
severity = advisory_page.css('span.label__text').text.gsub(%r{(.*?) severity}, '\1')
|
93
|
+
|
94
|
+
package_manager = advisory_page.css('.breadcrumbs__list-item')[1].text.gsub(%r{\s+}, '').downcase
|
95
|
+
package_manager = PACKAGE_MANAGER_ALIAS[package_manager] || raise("Could not find alias for package manager #{package_manager}")
|
96
|
+
|
97
|
+
title = utf8(advisory_page.css('h1.header__title span.header__title__text').text)
|
98
|
+
|
99
|
+
affected_package = advisory_page.css('.custom-package-name').text
|
100
|
+
affected_package = advisory_page.css('.header__lede .breadcrumbs__list-item__link').text if affected_package.empty?
|
101
|
+
|
102
|
+
vulnerable_versions = advisory_page.css('.custom-affected-versions').text.strip
|
103
|
+
vulnerable_versions = if vulnerable_versions.empty? || vulnerable_versions == 'ALL'
|
104
|
+
['*']
|
105
|
+
else
|
106
|
+
[vulnerable_versions]
|
107
|
+
end
|
108
|
+
|
109
|
+
sidebar_data = parse_side_bar(advisory_page)
|
110
|
+
body_data = parse_body(advisory_page)
|
111
|
+
|
112
|
+
published_date = parse_date(sidebar_data[:published_date].to_s)
|
113
|
+
disclosed_date = parse_date(sidebar_data[:disclosed_date].to_s) || published_date
|
114
|
+
last_modified_date = if sidebar_data[:last_modified_date]
|
115
|
+
parse_date(sidebar_data[:last_modified_date].to_s)
|
116
|
+
else
|
117
|
+
published_date
|
118
|
+
end
|
119
|
+
|
120
|
+
YAVDB::Advisory.new(
|
121
|
+
"snykio:#{package_manager}:#{affected_package}:#{disclosed_date}",
|
122
|
+
title,
|
123
|
+
body_data[:description],
|
124
|
+
affected_package,
|
125
|
+
vulnerable_versions,
|
126
|
+
nil, #:unaffected_versions
|
127
|
+
nil, #:patched_versions
|
128
|
+
severity,
|
129
|
+
package_manager,
|
130
|
+
sidebar_data[:cve],
|
131
|
+
sidebar_data[:cwe],
|
132
|
+
nil, #:osvdb
|
133
|
+
nil, #:cvss_v2_vector
|
134
|
+
nil, #:cvss_v2_score
|
135
|
+
nil, #:cvss_v3_vector
|
136
|
+
nil, #:cvss_v3_score
|
137
|
+
disclosed_date,
|
138
|
+
published_date,
|
139
|
+
last_modified_date,
|
140
|
+
[sidebar_data[:credit]].flatten,
|
141
|
+
body_data[:references],
|
142
|
+
advisory_url
|
143
|
+
)
|
144
|
+
end
|
145
|
+
|
146
|
+
def parse_body(advisory_page)
|
147
|
+
data = {}
|
148
|
+
|
149
|
+
description_sections = []
|
150
|
+
overview_fields = advisory_page.css('.card.card--markdown .card__content > *')
|
151
|
+
overview_fields.each do |field|
|
152
|
+
if field.name == 'h2'
|
153
|
+
description_sections.push(:header => field, :body => [])
|
154
|
+
elsif description_sections.any?
|
155
|
+
last_elem = description_sections.last
|
156
|
+
new_body = last_elem[:body].push(field)
|
157
|
+
last_elem[:body] = new_body
|
158
|
+
description_sections.push(last_elem)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
description_sections.map do |section|
|
163
|
+
header = section[:header]
|
164
|
+
body = section[:body]
|
165
|
+
|
166
|
+
case header.text
|
167
|
+
when 'Overview' then
|
168
|
+
overview_str = body
|
169
|
+
.map(&:to_xml)
|
170
|
+
.join("\n")
|
171
|
+
.force_encoding('UTF-8')
|
172
|
+
begin
|
173
|
+
data[:description] += '\n' if data[:description]
|
174
|
+
data[:description] = '' unless data[:description]
|
175
|
+
data[:description] += utf8(Kramdown::Document.new(overview_str, :html_to_native => true).to_kramdown)
|
176
|
+
rescue StandardError
|
177
|
+
# ignore
|
178
|
+
end
|
179
|
+
when 'Details' then
|
180
|
+
details_str = body
|
181
|
+
.map(&:to_xml)
|
182
|
+
.join("\n")
|
183
|
+
.force_encoding('UTF-8')
|
184
|
+
begin
|
185
|
+
data[:description] += '\n' if data[:description]
|
186
|
+
data[:description] = '' unless data[:description]
|
187
|
+
data[:description] += utf8(Kramdown::Document.new(details_str, :html_to_native => true).to_kramdown)
|
188
|
+
rescue StandardError
|
189
|
+
# ignore
|
190
|
+
end
|
191
|
+
when 'References' then
|
192
|
+
references = []
|
193
|
+
if body.any?
|
194
|
+
body.first.css('li a').map do |elem|
|
195
|
+
references.push(elem.get('href'))
|
196
|
+
end
|
197
|
+
end
|
198
|
+
data[:references] = references.flatten
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
data
|
203
|
+
end
|
204
|
+
|
205
|
+
def parse_side_bar(advisory_page)
|
206
|
+
data = {}
|
207
|
+
|
208
|
+
advisory_page.css('.l-col .card .card__content dl > *').each_slice(2).to_a.map do |key, value|
|
209
|
+
case key.text
|
210
|
+
when 'Credit' then
|
211
|
+
data[:credit] = utf8(value.text.split(',').map { |str| str.strip.sub(%r{-\s*}, '') }.reject(&:empty?))
|
212
|
+
when 'CVE' then
|
213
|
+
data[:cve] = value.css('a').map { |a| a.text.strip.split(',') }.flatten.map(&:strip).reject(&:empty?)
|
214
|
+
when 'CWE' then
|
215
|
+
data[:cwe] = value.css('a').map { |a| a.text.strip.split(',') }.flatten.map(&:strip).reject(&:empty?)
|
216
|
+
when 'Snyk ID' then
|
217
|
+
data[:id] = value.text.strip
|
218
|
+
when 'Disclosed' then
|
219
|
+
data[:disclosed_date] = value.text.strip
|
220
|
+
when 'Published' then
|
221
|
+
data[:published_date] = value.text.strip
|
222
|
+
when 'Last modified' then
|
223
|
+
data[:last_modified_date] = value.text.strip
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
data
|
228
|
+
end
|
229
|
+
|
230
|
+
def get_page_html(source_url, with_cache, group_cache_key)
|
231
|
+
source_url = "#{BASE_URL}#{source_url}" unless source_url.start_with?('http')
|
232
|
+
body_lines = YAVDB::Utils::HTTP.get_page_contents(source_url, with_cache, group_cache_key)
|
233
|
+
body_lines = escape_vulnerable_versions(body_lines)
|
234
|
+
Oga.parse_html(body_lines, :strict => true)
|
235
|
+
end
|
236
|
+
|
237
|
+
def clean_references(references)
|
238
|
+
references.map do |reference|
|
239
|
+
reference
|
240
|
+
.gsub(%r{\s*-\s*(.*)}, '\1')
|
241
|
+
.gsub(%r{\[.+?\]\((.*)\).*}, '\1')
|
242
|
+
.strip
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def parse_date(date_str)
|
247
|
+
Date.strptime(date_str, '%d %b, %Y')
|
248
|
+
rescue ArgumentError
|
249
|
+
# ignore
|
250
|
+
end
|
251
|
+
|
252
|
+
# HACK: Page contains non UTF-8 characters and we need to fix it to be able to convert to json
|
253
|
+
def utf8(value)
|
254
|
+
if value.is_a?(Array)
|
255
|
+
value.map { |sub_value| utf8(sub_value) }
|
256
|
+
elsif value.is_a?(String)
|
257
|
+
value.force_encoding('UTF-8')
|
258
|
+
else
|
259
|
+
value
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
# HACK: Page contains invalid HTML and we need to fix it to get the affected version
|
264
|
+
def escape_vulnerable_versions(body_lines)
|
265
|
+
cleaning = false
|
266
|
+
body_lines.map do |line|
|
267
|
+
if line.include?('<h2 id="overview">Overview</h2>')
|
268
|
+
cleaning = true
|
269
|
+
elsif line.include?('<h2 id=')
|
270
|
+
cleaning = false
|
271
|
+
end
|
272
|
+
|
273
|
+
if cleaning
|
274
|
+
key = '<script>'
|
275
|
+
line = line.gsub(key, Oga::XML::Entities.encode(key))
|
276
|
+
end
|
277
|
+
|
278
|
+
if line.include?(', versions')
|
279
|
+
extracted = line.gsub(%r{\s*<strong\s*>(.*)<\/strong>\s*}, '\1')
|
280
|
+
.gsub(%r{\s*<a.*>(.*)<\/a><\/strong>\s*}, '\1 ')
|
281
|
+
.gsub(%r{\s*<\/p>\s*}, '')
|
282
|
+
.gsub(%r{\s*<\/strong>\s*}, ' ')
|
283
|
+
.gsub(%r{\s*(.*)\s*}, '\1')
|
284
|
+
.gsub(%r{(\S*)(?:\s+.*)?,\s*versions\s*(.*)\s*}, "\\1#{INFO_SEP}\\2")
|
285
|
+
.tr("\n", ' ')
|
286
|
+
.gsub(' ', ' ')
|
287
|
+
.split(INFO_SEP)
|
288
|
+
fixed_version = Oga::XML::Entities.encode(extracted[1])
|
289
|
+
"</strong><span class=\"custom-package-name\">#{extracted[0]}</span><span class=\"custom-affected-versions\">#{fixed_version}</span>"
|
290
|
+
elsif line.include?(', <strong >ALL</strong> versions')
|
291
|
+
extracted = line.gsub(%r{\s*<strong\s*>(.*)<\/strong>\s*}, '\1')
|
292
|
+
.gsub(%r{\s*<a.*>(.*)<\/a><\/strong>\s*}, '\1 ')
|
293
|
+
.gsub(%r{\s*<\/p>\s*}, '')
|
294
|
+
.gsub(%r{\s*<\/strong>\s*}, ' ')
|
295
|
+
.gsub(%r{\s*(.*)\s*}, '\1')
|
296
|
+
.gsub(%r{(\S*)(?:\s+.*)?,\s*.*\s*versions\s*}, '\1')
|
297
|
+
.tr("\n", ' ')
|
298
|
+
.split(INFO_SEP)
|
299
|
+
"</strong><span class=\"custom-package-name\">#{extracted[0]}</span><span class=\"custom-affected-versions\">*</span>"
|
300
|
+
else
|
301
|
+
line
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
end
|
307
|
+
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# yavdb - The Free and Open Source vulnerability database
|
2
|
+
# Copyright (C) 2017-present Rodrigo Fernandes
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU Affero General Public License as
|
6
|
+
# published by the Free Software Foundation, either version 3 of the
|
7
|
+
# License, or (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU Affero General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU Affero General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
require 'date'
|
18
|
+
require 'yaml'
|
19
|
+
|
20
|
+
require_relative '../dtos/advisory'
|
21
|
+
require_relative '../utils/git'
|
22
|
+
|
23
|
+
module YAVDB
|
24
|
+
module Sources
|
25
|
+
module Victims
|
26
|
+
class Client
|
27
|
+
|
28
|
+
Language = Struct.new(:name, :package_manager, :name_parser)
|
29
|
+
|
30
|
+
REPOSITORY_URL = 'https://github.com/victims/victims-cve-db'.freeze
|
31
|
+
|
32
|
+
LANGUAGES = [
|
33
|
+
Language.new('java', 'maven', lambda { |affected_package| "#{affected_package['groupId']}:#{affected_package['artifactId']}" }),
|
34
|
+
Language.new('python', 'pypi', lambda { |affected_package| affected_package['name'] })
|
35
|
+
]
|
36
|
+
|
37
|
+
def self.advisories
|
38
|
+
LANGUAGES.map do |language|
|
39
|
+
glob = language_glob(language.name)
|
40
|
+
YAVDB::SourceTypes::GitRepo.search(glob, REPOSITORY_URL).map do |repo_path, file_paths|
|
41
|
+
Dir.chdir(repo_path) do
|
42
|
+
file_paths.map do |file_path|
|
43
|
+
advisory_hash = YAML.load_file(file_path)
|
44
|
+
url = "#{REPOSITORY_URL}/blob/master/#{file_path}"
|
45
|
+
create(advisory_hash, language, url)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end.flatten
|
50
|
+
end
|
51
|
+
|
52
|
+
class << self
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def language_glob(language)
|
57
|
+
"database/#{language}/*/*.*"
|
58
|
+
end
|
59
|
+
|
60
|
+
def create(advisory_hash, language, url)
|
61
|
+
advisory_hash['affected'].map do |affected_package|
|
62
|
+
YAVDB::Advisory.new(
|
63
|
+
"victims:#{language.package_manager}:#{language.name_parser[affected_package]}:date",
|
64
|
+
advisory_hash['title'],
|
65
|
+
advisory_hash['description'],
|
66
|
+
language.name_parser[affected_package],
|
67
|
+
affected_package['version'],
|
68
|
+
affected_package['unaffected'],
|
69
|
+
affected_package['fixedin'],
|
70
|
+
severity(advisory_hash['cvss_v2']),
|
71
|
+
language.package_manager,
|
72
|
+
[advisory_hash['cve']],
|
73
|
+
nil, #:cwe
|
74
|
+
nil, #:osvdb
|
75
|
+
nil, #:cvss_v2_vector
|
76
|
+
advisory_hash['cvss_v2'],
|
77
|
+
nil, #:cvss_v3_vector
|
78
|
+
nil, #:cvss_v3
|
79
|
+
nil,
|
80
|
+
nil,
|
81
|
+
nil,
|
82
|
+
['Victims CVE Database'],
|
83
|
+
advisory_hash['references'],
|
84
|
+
url
|
85
|
+
)
|
86
|
+
end.flatten
|
87
|
+
end
|
88
|
+
|
89
|
+
def severity(cvss_score)
|
90
|
+
case cvss_score
|
91
|
+
when 0.0..3.3 then
|
92
|
+
'low'
|
93
|
+
when 3.3..6.6 then
|
94
|
+
'medium'
|
95
|
+
else
|
96
|
+
'high'
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|