11import os
22import urllib .request
3- import saneyaml
4-
5- from shutil import rmtree
63from urllib .error import HTTPError
74from zipfile import ZipFile
8- from itertools import chain
5+ from io import BytesIO
6+ import saneyaml
97from 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
3921def get_all_versions_of_package (package_name ):
@@ -48,57 +30,55 @@ def get_all_versions_of_package(package_name):
4830
4931
5032def 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
6240def 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
0 commit comments