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