yavdb 0.6.0 → 0.7.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 +4 -4
- data/.rubocop.yml +0 -2
- data/.ruby-version +1 -1
- data/Gemfile.lock +2 -2
- data/README.md +1 -2
- data/lib/yavdb/version.rb +1 -1
- metadata +2 -3
- data/lib/yavdb/sources/snyk_io.rb +0 -309
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e1e5f9733916e6d5f5d5be22dc13f15879d2d27c4d67f3a1da2354108de7c693
|
|
4
|
+
data.tar.gz: 8748dccfdbc49a566c1bc46d692b96d8dff6bbf17aa77b876fc20fd92066ae61
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7be1bd86bf8f7c066cc4ee6e5cbc005d226bc70e0d1a8a0f849ac447c4c64032608cfef4a25abe46bdc297e985a1050450b355014113c25efd9bfc1afe4bccf2
|
|
7
|
+
data.tar.gz: 5e44694faa7f72af21f2cb2548e3059e7ab993872e0f1ad0b08d6f60f1f2bcef28cd7af02f9fb61a9ef20ae9ca18b6bd0172afe175cb8d77e78f1450bd2b2b47
|
data/.rubocop.yml
CHANGED
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.5.
|
|
1
|
+
2.5.7
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -11,7 +11,7 @@ developers identify and fix know vulnerabilities in their apps.
|
|
|
11
11
|
|
|
12
12
|
The sources for this database include
|
|
13
13
|
[Rubysec](https://rubysec.com/),
|
|
14
|
-
[snyk](https://snyk.io/)
|
|
14
|
+
~~[snyk](https://snyk.io/),~~ (removed)
|
|
15
15
|
[Friends of PHP](https://github.com/FriendsOfPHP/security-advisories),
|
|
16
16
|
[Magento Related Security Advisories](https://github.com/victims/victims-cve-db),
|
|
17
17
|
[Victims CVE Database](https://github.com/victims/victims-cve-db),
|
|
@@ -33,7 +33,6 @@ gem install yavdb
|
|
|
33
33
|
|
|
34
34
|
* Sources
|
|
35
35
|
- [ ] [Rubysec](lib/yavdb/sources/ruby_advisory.rb)
|
|
36
|
-
- [X] [snyk](lib/yavdb/sources/snyk_io.rb)
|
|
37
36
|
- [ ] [OSSIndex](lib/yavdb/sources/ossindex.rb)
|
|
38
37
|
- [ ] [Friends of PHP and Magento Related Security Advisories](lib/yavdb/sources/friends_of_php.rb)
|
|
39
38
|
- [ ] [Victims CVE Database](lib/yavdb/sources/victims.rb)
|
data/lib/yavdb/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: yavdb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rodrigo Fernandes
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2021-01-18 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: codacy-coverage
|
|
@@ -296,7 +296,6 @@ files:
|
|
|
296
296
|
- lib/yavdb/sources/npmjs.rb
|
|
297
297
|
- lib/yavdb/sources/ruby_advisory.rb
|
|
298
298
|
- lib/yavdb/sources/rustsec.rb
|
|
299
|
-
- lib/yavdb/sources/snyk_io.rb
|
|
300
299
|
- lib/yavdb/sources/victims.rb
|
|
301
300
|
- lib/yavdb/utils/cache.rb
|
|
302
301
|
- lib/yavdb/utils/exec.rb
|
|
@@ -1,309 +0,0 @@
|
|
|
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 = ['composer', 'golang', 'maven', 'npm', 'nuget', 'pip', 'rubygems', 'cocoapods'].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
|
-
'cocoapods' => 'cocoapods'
|
|
44
|
-
].freeze
|
|
45
|
-
|
|
46
|
-
def self.advisories
|
|
47
|
-
urls = fetch_advisory_urls
|
|
48
|
-
urls.map do |advisory_url|
|
|
49
|
-
advisory_page = get_page_html(advisory_url, true, 'snyk.io/advisories')
|
|
50
|
-
create(advisory_url, advisory_page)
|
|
51
|
-
end.reject(&:nil?)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
class << self
|
|
55
|
-
|
|
56
|
-
private
|
|
57
|
-
|
|
58
|
-
def fetch_advisory_urls
|
|
59
|
-
PACKAGE_MANAGERS.map do |pm|
|
|
60
|
-
fetch_advisory_recursive("#{BASE_VULN_URL}?type=#{pm}")
|
|
61
|
-
end.flatten
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def fetch_advisory_recursive(page_url)
|
|
65
|
-
snykio = get_page_html(page_url, false, 'snyk.io/feed')
|
|
66
|
-
|
|
67
|
-
page_vuln_urls = snykio
|
|
68
|
-
.css('table tbody tr td span a')
|
|
69
|
-
.map { |anchor| anchor.get('href') }
|
|
70
|
-
.map { |link| link if %r{\/vuln\/.+}.match?(link) }.compact
|
|
71
|
-
|
|
72
|
-
next_urls = if page_vuln_urls.any?
|
|
73
|
-
next_url = snykio.css('a.pagination__next')
|
|
74
|
-
if next_url&.first
|
|
75
|
-
fetch_advisory_recursive(next_url.first.get('href'))
|
|
76
|
-
else
|
|
77
|
-
[]
|
|
78
|
-
end
|
|
79
|
-
else
|
|
80
|
-
[]
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
page_vuln_urls
|
|
84
|
-
.concat(next_urls)
|
|
85
|
-
.map do |url|
|
|
86
|
-
full_url = url
|
|
87
|
-
full_url = "#{BASE_URL}#{url}" unless url.start_with?('http')
|
|
88
|
-
full_url
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def create(advisory_url, advisory_page)
|
|
93
|
-
severity = advisory_page.css('span.label__text').text.gsub(%r{(.*?) severity}, '\1')
|
|
94
|
-
|
|
95
|
-
package_manager = advisory_page.css('.breadcrumbs__list-item')[1].text.gsub(%r{\s+}, '').downcase
|
|
96
|
-
package_manager = PACKAGE_MANAGER_ALIAS[package_manager] || return
|
|
97
|
-
|
|
98
|
-
title = utf8(advisory_page.css('h1.header__title span.header__title__text').text)
|
|
99
|
-
|
|
100
|
-
affected_package = advisory_page.css('.custom-package-name').text
|
|
101
|
-
affected_package = advisory_page.css('.header__lede .breadcrumbs__list-item__link').text if affected_package.empty?
|
|
102
|
-
|
|
103
|
-
vulnerable_versions = (advisory_page.css('.custom-affected-versions') ||
|
|
104
|
-
advisory_page.css('.header__lede strong').drop(1).first).text.strip
|
|
105
|
-
vulnerable_versions = if vulnerable_versions.empty? || vulnerable_versions == 'ALL' || vulnerable_versions == '(,)'
|
|
106
|
-
['*']
|
|
107
|
-
elsif ['maven', 'nuget', 'pypi'].include?(package_manager)
|
|
108
|
-
[vulnerable_versions]
|
|
109
|
-
else
|
|
110
|
-
[vulnerable_versions.tr(',', ' ')]
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
sidebar_data = parse_side_bar(advisory_page)
|
|
114
|
-
body_data = parse_body(advisory_page)
|
|
115
|
-
|
|
116
|
-
published_date = parse_date(sidebar_data[:published_date].to_s)
|
|
117
|
-
disclosed_date = parse_date(sidebar_data[:disclosed_date].to_s) || published_date
|
|
118
|
-
last_modified_date = if sidebar_data[:last_modified_date]
|
|
119
|
-
parse_date(sidebar_data[:last_modified_date].to_s)
|
|
120
|
-
else
|
|
121
|
-
published_date
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
vuln_id_stamp = sidebar_data[:id].split(%r{-|:}).last || disclosed_date
|
|
125
|
-
vuln_id = "snykio:#{package_manager}:#{affected_package}:#{vuln_id_stamp}"
|
|
126
|
-
|
|
127
|
-
YAVDB::Advisory.new(
|
|
128
|
-
vuln_id,
|
|
129
|
-
title,
|
|
130
|
-
body_data[:description],
|
|
131
|
-
affected_package,
|
|
132
|
-
vulnerable_versions,
|
|
133
|
-
nil, #:unaffected_versions
|
|
134
|
-
nil, #:patched_versions
|
|
135
|
-
severity,
|
|
136
|
-
package_manager,
|
|
137
|
-
sidebar_data[:cve],
|
|
138
|
-
sidebar_data[:cwe],
|
|
139
|
-
nil, #:osvdb
|
|
140
|
-
nil, #:cvss_v2_vector
|
|
141
|
-
nil, #:cvss_v2_score
|
|
142
|
-
nil, #:cvss_v3_vector
|
|
143
|
-
nil, #:cvss_v3_score
|
|
144
|
-
disclosed_date,
|
|
145
|
-
published_date,
|
|
146
|
-
last_modified_date,
|
|
147
|
-
[sidebar_data[:credit]].flatten,
|
|
148
|
-
body_data[:references],
|
|
149
|
-
advisory_url
|
|
150
|
-
)
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
def parse_body(advisory_page)
|
|
154
|
-
data = {}
|
|
155
|
-
|
|
156
|
-
description_sections = []
|
|
157
|
-
overview_fields = advisory_page.css('.card.card--markdown .card__content > *')
|
|
158
|
-
overview_fields.each do |field|
|
|
159
|
-
if field.name == 'h2'
|
|
160
|
-
description_sections.push(:header => field, :body => [])
|
|
161
|
-
elsif description_sections.any?
|
|
162
|
-
last_elem = description_sections.last
|
|
163
|
-
new_body = last_elem[:body].push(field)
|
|
164
|
-
last_elem[:body] = new_body
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
description_sections.map do |section|
|
|
169
|
-
header = section[:header]
|
|
170
|
-
body = section[:body]
|
|
171
|
-
|
|
172
|
-
case header.text
|
|
173
|
-
when %r{^(Overview|Details)$} then
|
|
174
|
-
overview_str = body
|
|
175
|
-
.map(&:to_xml)
|
|
176
|
-
.map { |e| e.force_encoding('UTF-8') }
|
|
177
|
-
.join("\n")
|
|
178
|
-
begin
|
|
179
|
-
if data[:description]
|
|
180
|
-
data[:description] += '\n'
|
|
181
|
-
else
|
|
182
|
-
data[:description] = ''
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
data[:description] += utf8(Kramdown::Document.new(overview_str, :html_to_native => true).to_kramdown)
|
|
186
|
-
rescue StandardError
|
|
187
|
-
# ignore
|
|
188
|
-
end
|
|
189
|
-
when 'References' then
|
|
190
|
-
references = []
|
|
191
|
-
if body.any?
|
|
192
|
-
body.first.css('li a').map do |elem|
|
|
193
|
-
references.push(elem.get('href'))
|
|
194
|
-
end
|
|
195
|
-
end
|
|
196
|
-
data[:references] = references.flatten
|
|
197
|
-
end
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
data
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
def parse_side_bar(advisory_page)
|
|
204
|
-
data = {}
|
|
205
|
-
|
|
206
|
-
advisory_page.css('.l-col .card .card__content dl > *').each_slice(2).to_a.map do |key, value|
|
|
207
|
-
case key.text
|
|
208
|
-
when 'Credit'
|
|
209
|
-
data[:credit] = utf8(value.text.split(',').map { |str| str.strip.sub(%r{-\s*}, '') }.reject(&:empty?))
|
|
210
|
-
when 'CVE'
|
|
211
|
-
data[:cve] = value.css('a').map { |a| a.text.strip.split(',') }.flatten.map(&:strip).reject(&:empty?)
|
|
212
|
-
when 'CWE'
|
|
213
|
-
data[:cwe] = value.css('a').map { |a| a.text.strip.split(',') }.flatten.map(&:strip).reject(&:empty?)
|
|
214
|
-
when 'Snyk ID'
|
|
215
|
-
data[:id] = value.text.strip
|
|
216
|
-
when 'Disclosed'
|
|
217
|
-
data[:disclosed_date] = value.text.strip
|
|
218
|
-
when 'Published'
|
|
219
|
-
data[:published_date] = value.text.strip
|
|
220
|
-
when 'Last modified'
|
|
221
|
-
data[:last_modified_date] = value.text.strip
|
|
222
|
-
end
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
data
|
|
226
|
-
end
|
|
227
|
-
|
|
228
|
-
def get_page_html(source_url, with_cache, group_cache_key)
|
|
229
|
-
source_url = "#{BASE_URL}#{source_url}" unless source_url.start_with?('http')
|
|
230
|
-
body_lines = YAVDB::Utils::HTTP.get_page_contents(source_url, with_cache, group_cache_key)
|
|
231
|
-
body_lines = escape_vulnerable_versions(body_lines)
|
|
232
|
-
Oga.parse_html(body_lines, :strict => true)
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
def clean_references(references)
|
|
236
|
-
references.map do |reference|
|
|
237
|
-
reference
|
|
238
|
-
.gsub(%r{\s*-\s*(.*)}, '\1')
|
|
239
|
-
.gsub(%r{\[.+?\]\((.*)\).*}, '\1')
|
|
240
|
-
.strip
|
|
241
|
-
end
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
def parse_date(date_str)
|
|
245
|
-
Date.strptime(date_str, '%d %b, %Y')
|
|
246
|
-
rescue ArgumentError
|
|
247
|
-
# ignore
|
|
248
|
-
end
|
|
249
|
-
|
|
250
|
-
# HACK: Page contains non UTF-8 characters and we need to fix it to be able to convert to json
|
|
251
|
-
def utf8(value)
|
|
252
|
-
if value.is_a?(Array)
|
|
253
|
-
value.map { |sub_value| utf8(sub_value) }
|
|
254
|
-
elsif value.is_a?(String)
|
|
255
|
-
value.force_encoding('UTF-8')
|
|
256
|
-
else
|
|
257
|
-
value
|
|
258
|
-
end
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
# HACK: Page contains invalid HTML and we need to fix it to get the affected version
|
|
262
|
-
def escape_vulnerable_versions(body_lines)
|
|
263
|
-
cleaning = false
|
|
264
|
-
body_lines.map do |line|
|
|
265
|
-
if line.include?('<h2 id="overview">Overview</h2>')
|
|
266
|
-
cleaning = true
|
|
267
|
-
elsif line.include?('<h2 id=')
|
|
268
|
-
cleaning = false
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
if cleaning
|
|
272
|
-
key = '<script>'
|
|
273
|
-
line = line.gsub(key, Oga::XML::Entities.encode(key))
|
|
274
|
-
end
|
|
275
|
-
|
|
276
|
-
if line.include?(', versions')
|
|
277
|
-
extracted = line.gsub(%r{\s*<strong\s*>(.*)<\/strong>\s*}, '\1')
|
|
278
|
-
.gsub(%r{\s*<a.*>(.*)<\/a><\/strong>\s*}, '\1 ')
|
|
279
|
-
.gsub(%r{\s*<\/p>\s*}, '')
|
|
280
|
-
.gsub(%r{\s*<\/strong>\s*}, ' ')
|
|
281
|
-
.gsub(%r{\s*(.*)\s*}, '\1')
|
|
282
|
-
.gsub(%r{(\S*)(?:\s+.*)?,\s*versions\s*(.*)\s*}, "\\1#{INFO_SEP}\\2")
|
|
283
|
-
.tr("\n", ' ')
|
|
284
|
-
.gsub(' ', ' ')
|
|
285
|
-
.split(INFO_SEP)
|
|
286
|
-
fixed_version = Oga::XML::Entities.encode(extracted[1])
|
|
287
|
-
"</strong><span class=\"custom-package-name\">#{extracted[0]}</span><span class=\"custom-affected-versions\">#{fixed_version}</span>"
|
|
288
|
-
elsif line.include?(', <strong >ALL</strong> versions')
|
|
289
|
-
extracted = line.gsub(%r{\s*<strong\s*>(.*)<\/strong>\s*}, '\1')
|
|
290
|
-
.gsub(%r{\s*<a.*>(.*)<\/a><\/strong>\s*}, '\1 ')
|
|
291
|
-
.gsub(%r{\s*<\/p>\s*}, '')
|
|
292
|
-
.gsub(%r{\s*<\/strong>\s*}, ' ')
|
|
293
|
-
.gsub(%r{\s*(.*)\s*}, '\1')
|
|
294
|
-
.gsub(%r{(\S*)(?:\s+.*)?,\s*.*\s*versions\s*}, '\1')
|
|
295
|
-
.tr("\n", ' ')
|
|
296
|
-
.split(INFO_SEP)
|
|
297
|
-
"</strong><span class=\"custom-package-name\">#{extracted[0]}</span><span class=\"custom-affected-versions\">*</span>"
|
|
298
|
-
else
|
|
299
|
-
line
|
|
300
|
-
end
|
|
301
|
-
end
|
|
302
|
-
end
|
|
303
|
-
|
|
304
|
-
end
|
|
305
|
-
|
|
306
|
-
end
|
|
307
|
-
end
|
|
308
|
-
end
|
|
309
|
-
end
|