Skip to content

Commit 793be7e

Browse files
committed
Add test for ruby importer
Signed-off-by: sbs2001 <shivam.sandbhor@gmail.com>
1 parent 2ab09b7 commit 793be7e

3 files changed

Lines changed: 79 additions & 88 deletions

File tree

vulnerabilities/data_dump.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -195,17 +195,10 @@ def npm_dump(extract_data):
195195
def ruby_dump(extract_data):
196196
for package_data in extract_data:
197197

198-
if(package_data['severity']):
199-
vulnerability, _ = Vulnerability.objects.get_or_create(
200-
cve_id=package_data['cve_id'],
201-
summary=package_data['summary'],
202-
cvss=package_data['severity']
203-
)
204-
else:
205-
vulnerability, _ = Vulnerability.objects.get_or_create(
206-
cve_id=package_data['cve_id'],
207-
summary=package_data['summary']
208-
)
198+
vulnerability, _ = Vulnerability.objects.get_or_create(
199+
cve_id=package_data['cve_id'],
200+
summary=package_data['summary'],
201+
)
209202

210203
VulnerabilityReference.objects.get_or_create(
211204
vulnerability=vulnerability,
@@ -215,6 +208,7 @@ def ruby_dump(extract_data):
215208
for version in package_data['affected_versions']:
216209
affected_package = Package.objects.create(
217210
name=package_data['package_name'],
211+
type='gem',
218212
version=version
219213
)
220214
ImpactedPackage.objects.create(
@@ -225,6 +219,7 @@ def ruby_dump(extract_data):
225219
for version in package_data['fixed_versions']:
226220
unaffected_package = Package.objects.create(
227221
name=package_data['package_name'],
222+
type='gem',
228223
version=version
229224
)
230225
ResolvedPackage.objects.create(

vulnerabilities/scraper/ruby.py

Lines changed: 57 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,21 @@
11
import os
22
import urllib.request
3-
import saneyaml
4-
5-
from shutil import rmtree
63
from urllib.error import HTTPError
74
from zipfile import ZipFile
8-
from itertools import chain
5+
from io import BytesIO
6+
import saneyaml
97
from dephell_specifier import RangeSpecifier
8+
from urllib.request import urlopen
109

11-
RUBYCVE_LINK = 'https://github.com/rubysec/ruby-advisory-db/archive/master.zip'
12-
DOWNLOAD_PATH = os.path.dirname(os.path.realpath(__file__))
13-
10+
RUBYSEC_DB_URL = 'https://github.com/rubysec/ruby-advisory-db/archive/master.zip'
1411

15-
def get_rubycve_db():
16-
pathToZip, _ = urllib.request.urlretrieve(
17-
RUBYCVE_LINK, os.path.join(
18-
DOWNLOAD_PATH, 'ruby.zip'))
19-
ZipFile(pathToZip).extractall(DOWNLOAD_PATH)
20-
os.remove(pathToZip)
2112

22-
23-
def path_of_yaml_of_all_packages():
24-
gemPath = os.path.join(DOWNLOAD_PATH, 'ruby-advisory-db-master', 'gems')
25-
rubiesPath = os.path.join(
26-
DOWNLOAD_PATH,
27-
'ruby-advisory-db-master',
28-
'rubies')
29-
for (
30-
packagePath,
31-
_,
32-
yamlNames) in chain(
33-
os.walk(gemPath),
34-
os.walk(rubiesPath)):
35-
for yamlName in yamlNames:
36-
yield os.path.join(packagePath, yamlName)
13+
def rubygem_advisories(url, prefix='ruby-advisory-db-master/gems/'):
14+
with urlopen(url) as response:
15+
with ZipFile(BytesIO(response.read())) as zf:
16+
for path in zf.namelist():
17+
if path.startswith(prefix) and path.endswith('.yml'):
18+
yield saneyaml.load(zf.open(path))
3719

3820

3921
def get_all_versions_of_package(package_name):
@@ -48,57 +30,55 @@ def get_all_versions_of_package(package_name):
4830

4931

5032
def get_patched_range(spec_list):
51-
if spec_list:
52-
def remove_space(string): return string.replace(' ', '')
53-
spec_list = list(map(remove_space, spec_list))
54-
for spec in spec_list:
55-
if 'rc' in spec:
56-
continue
57-
yield RangeSpecifier(spec)
58-
else:
59-
return None
33+
spec_list = [string.replace(' ', '') for string in spec_list]
34+
for spec in spec_list:
35+
if 'rc' in spec:
36+
continue
37+
yield RangeSpecifier(spec)
6038

6139

6240
def import_vulnerabilities():
63-
get_rubycve_db()
6441
ids = set()
65-
vulnerability_to_package_map = []
66-
for vulnerability_path in path_of_yaml_of_all_packages():
67-
with open(vulnerability_path) as yamlFile:
68-
vulnerability = saneyaml.load(yamlFile)
69-
package_name = vulnerability.get(
70-
'engine', vulnerability.get('gem'))
71-
summary = vulnerability.get('description', '')
72-
if 'cve' in vulnerability:
73-
vulnerability_id = 'CVE-{}'.format(vulnerability['cve'])
74-
if vulnerability_id in ids:
75-
continue
76-
ids.add(vulnerability_id)
77-
else:
42+
vulnerability_package_dicts = []
43+
for vulnerability in rubygem_advisories(RUBYSEC_DB_URL):
44+
45+
package_name = vulnerability.get(
46+
'gem')
47+
48+
if not package_name:
49+
continue
50+
51+
summary = vulnerability.get('description', '')
52+
53+
if 'cve' in vulnerability:
54+
vulnerability_id = 'CVE-{}'.format(vulnerability['cve'])
55+
if vulnerability_id in ids:
7856
continue
79-
severity = vulnerability.get(
80-
'cvss_v3', vulnerability.get('cvss_v2'))
81-
advisoryUrl = vulnerability.get('url')
82-
specs = list(
83-
get_patched_range(
84-
vulnerability.get('patched_versions')))
85-
allVersions = set(list(get_all_versions_of_package(package_name)))
86-
unaffected_versions = set()
87-
if specs:
88-
for version in allVersions:
89-
for spec in specs:
90-
if(version in spec):
91-
unaffected_versions.add(version)
92-
break
93-
affected_versions = allVersions - unaffected_versions
94-
vulnerability_to_package_map.append({
95-
'package_name': package_name,
96-
'summary': summary,
97-
'cve_id': vulnerability_id,
98-
'fixed_versions': unaffected_versions,
99-
'affected_versions': affected_versions,
100-
'severity': severity,
101-
'advisory': advisoryUrl
102-
})
103-
rmtree(os.path.join(DOWNLOAD_PATH, 'ruby-advisory-db-master'))
104-
return vulnerability_to_package_map
57+
ids.add(vulnerability_id)
58+
else:
59+
continue
60+
61+
advisory_url = vulnerability.get('url')
62+
patched_version_ranges = list(
63+
get_patched_range(
64+
vulnerability.get('patched_versions', [])))
65+
all_versions = set(get_all_versions_of_package(package_name))
66+
unaffected_versions = set()
67+
68+
if patched_version_ranges:
69+
for version in all_versions:
70+
for spec in patched_version_ranges:
71+
if version in spec:
72+
unaffected_versions.add(version)
73+
break
74+
75+
affected_versions = all_versions - unaffected_versions
76+
vulnerability_package_dicts.append({
77+
'package_name': package_name,
78+
'summary': summary,
79+
'cve_id': vulnerability_id,
80+
'fixed_versions': unaffected_versions,
81+
'affected_versions': affected_versions,
82+
'advisory': advisory_url
83+
})
84+
return vulnerability_package_dicts

vulnerabilities/tests/test_ruby.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from dephell_specifier import RangeSpecifier
2+
3+
from vulnerabilities.scraper.ruby import get_all_versions_of_package
4+
from vulnerabilities.scraper.ruby import get_patched_range
5+
6+
7+
def test_get_all_versions_of_package():
8+
versions = set(get_all_versions_of_package('actionpack'))
9+
expected = set(['1.10.2', '4.0.3', '3.2.3', '4.2.0.beta3',
10+
'5.2.3', '4.1.14.2', '4.0.13'])
11+
assert versions >= expected
12+
13+
14+
def test_get_patched_range():
15+
expected = set([RangeSpecifier('>4'), RangeSpecifier('~>4.1')])
16+
assert set(get_patched_range(['>4', '~>4.1', '>4.2rc'])) == expected

0 commit comments

Comments
 (0)