15
15
from vulnerabilities .helpers import split_markdown_front_matter
16
16
from vulnerabilities .severity_systems import scoring_systems
17
17
18
-
19
18
REPOSITORY = "mozilla/foundation-security-advisories"
20
19
MFSA_FILENAME_RE = re .compile (r"mfsa(\d{4}-\d{2,3})\.(md|yml)$" )
21
20
@@ -37,128 +36,146 @@ def updated_advisories(self) -> Set[Advisory]:
37
36
38
37
advisories = []
39
38
for path in files :
40
- advisories .extend (self . to_advisories (path ))
39
+ advisories .extend (to_advisories (path ))
41
40
42
41
return self .batch_advisories (advisories )
43
42
44
- def to_advisories (self , path : str ) -> List [Advisory ]:
45
- """
46
- Convert a file to corresponding advisories.
47
- This calls proper method to handle yml/md files.
48
- """
49
- mfsa_id = self .mfsa_id_from_filename (path )
50
-
51
- with open (path ) as lines :
52
- if path .endswith (".md" ):
53
- return self .get_advisories_from_md (mfsa_id , lines )
54
- if path .endswith (".yml" ):
55
- return self .get_advisories_from_yml (mfsa_id , lines )
56
43
44
+ def to_advisories (path : str ) -> List [Advisory ]:
45
+ """
46
+ Convert a file to corresponding advisories.
47
+ This calls proper method to handle yml/md files.
48
+ """
49
+ mfsa_id = mfsa_id_from_filename (path )
50
+ if not mfsa_id :
57
51
return []
58
52
59
- def get_advisories_from_yml (self , mfsa_id , lines ) -> List [Advisory ]:
60
- advisories = []
61
- data = yaml .safe_load (lines )
62
- data ["mfsa_id" ] = mfsa_id
63
-
64
- fixed_package_urls = self .get_package_urls (data .get ("fixed_in" ))
65
- references = self .get_yml_references (data )
66
-
67
- if not data .get ("advisories" ):
68
- return []
69
-
70
- for cve , advisory in data ["advisories" ].items ():
71
- advisories .append (
72
- Advisory (
73
- summary = advisory .get ("description" ),
74
- vulnerability_id = cve if is_cve (cve ) else "" ,
75
- impacted_package_urls = [],
76
- resolved_package_urls = fixed_package_urls ,
77
- references = references ,
78
- )
79
- )
53
+ with open (path ) as lines :
54
+ if path .endswith (".md" ):
55
+ return get_advisories_from_md (mfsa_id , lines )
56
+ if path .endswith (".yml" ):
57
+ return get_advisories_from_yml (mfsa_id , lines )
80
58
81
- return advisories
82
-
83
- def get_advisories_from_md (self , mfsa_id , lines ) -> List [Advisory ]:
84
- yamltext , mdtext = split_markdown_front_matter (lines .read ())
85
- data = yaml .safe_load (yamltext )
86
- data ["mfsa_id" ] = mfsa_id
87
-
88
- fixed_package_urls = self .get_package_urls (data .get ("fixed_in" ))
89
- references = self .get_yml_references (data )
90
- cves = re .findall (r"CVE-\d+-\d+" , yamltext + mdtext , re .IGNORECASE )
91
- for cve in cves :
92
- references .append (
93
- Reference (
94
- reference_id = cve , url = f"https://cve.mitre.org/cgi-bin/cvename.cgi?name={ cve } "
95
- )
96
- )
59
+ return []
60
+
61
+
62
+ def get_advisories_from_yml (mfsa_id , lines ) -> List [Advisory ]:
63
+ advisories = []
64
+ data = yaml .safe_load (lines )
65
+ data ["mfsa_id" ] = mfsa_id
97
66
98
- description = self .html_get_p_under_h3 (markdown (mdtext ), "description" )
67
+ fixed_package_urls = get_package_urls (data .get ("fixed_in" ))
68
+ references = get_yml_references (data )
99
69
100
- return [
70
+ if not data .get ("advisories" ):
71
+ return []
72
+
73
+ for cve , advisory in data ["advisories" ].items ():
74
+ # These may contain HTML tags
75
+ summary = BeautifulSoup (advisory .get ("description" , "" ), features = "lxml" ).get_text ()
76
+
77
+ advisories .append (
101
78
Advisory (
102
- summary = description ,
103
- vulnerability_id = "" ,
79
+ summary = summary ,
80
+ vulnerability_id = cve if is_cve ( cve ) else "" ,
104
81
impacted_package_urls = [],
105
82
resolved_package_urls = fixed_package_urls ,
106
83
references = references ,
107
84
)
108
- ]
109
-
110
- def html_get_p_under_h3 (self , html , h3 : str ):
111
- soup = BeautifulSoup (html , features = "lxml" )
112
- h3tag = soup .find ("h3" , text = lambda txt : txt .lower () == h3 )
113
- p = ""
114
- if h3tag :
115
- for tag in h3tag .next_siblings :
116
- if tag .name :
117
- if tag .name != "p" :
118
- break
119
- p += tag .get_text ()
120
- return p
121
-
122
- def mfsa_id_from_filename (self , filename ):
123
- match = MFSA_FILENAME_RE .search (filename )
124
- if match :
125
- return "mfsa" + match .group (1 )
126
-
127
- return None
128
-
129
- def get_package_urls (self , pkgs : List [str ]) -> List [PackageURL ]:
130
- package_urls = [
131
- PackageURL (
132
- type = "mozilla" ,
133
- # pkg is of the form "Firefox ESR 1.21" or "Thunderbird 2.21"
134
- name = pkg .rsplit (None , 1 )[0 ],
135
- version = pkg .rsplit (None , 1 )[1 ],
136
- )
137
- for pkg in pkgs
138
- ]
139
- return package_urls
140
-
141
- def get_yml_references (self , data : any ) -> List [Reference ]:
142
- """
143
- Returns a list of references
144
- Currently only considers the given mfsa as a reference
145
- """
146
- # FIXME: Needs improvement
147
- # Should we add 'bugs' section in references too?
148
- # Should we add 'impact'/severity of CVE in references too?
149
- # If yes, then fix alpine_linux importer as well
150
- # Otherwise, do we need severity field for adversary as well?
151
-
152
- # FIXME: Write a helper for cvssv3.1_qr severity detection ?
153
- severities = ["critical" , "low" , "high" , "medium" , "none" ]
154
- severity = [{severity in data .get ("impact" ): severity } for severity in severities ][0 ].get (
155
- True
156
85
)
157
86
158
- return [
87
+ return advisories
88
+
89
+
90
+ def get_advisories_from_md (mfsa_id , lines ) -> List [Advisory ]:
91
+ yamltext , mdtext = split_markdown_front_matter (lines .read ())
92
+ data = yaml .safe_load (yamltext )
93
+ data ["mfsa_id" ] = mfsa_id
94
+
95
+ fixed_package_urls = get_package_urls (data .get ("fixed_in" ))
96
+ references = get_yml_references (data )
97
+ cves = re .findall (r"CVE-\d+-\d+" , yamltext + mdtext , re .IGNORECASE )
98
+ for cve in cves :
99
+ references .append (
159
100
Reference (
160
- reference_id = data ["mfsa_id" ],
161
- url = "https://www.mozilla.org/en-US/security/advisories/{}" .format (data ["mfsa_id" ]),
162
- severities = [VulnerabilitySeverity (scoring_systems ["cvssv3.1_qr" ], severity )],
101
+ reference_id = cve ,
102
+ url = f"https://cve.mitre.org/cgi-bin/cvename.cgi?name={ cve } " ,
163
103
)
164
- ]
104
+ )
105
+
106
+ description = html_get_p_under_h3 (markdown (mdtext ), "description" )
107
+
108
+ return [
109
+ Advisory (
110
+ summary = description ,
111
+ vulnerability_id = "" ,
112
+ impacted_package_urls = [],
113
+ resolved_package_urls = fixed_package_urls ,
114
+ references = references ,
115
+ )
116
+ ]
117
+
118
+
119
+ def html_get_p_under_h3 (html , h3 : str ):
120
+ soup = BeautifulSoup (html , features = "lxml" )
121
+ h3tag = soup .find ("h3" , text = lambda txt : txt .lower () == h3 )
122
+ p = ""
123
+ if h3tag :
124
+ for tag in h3tag .next_siblings :
125
+ if tag .name :
126
+ if tag .name != "p" :
127
+ break
128
+ p += tag .get_text ()
129
+ return p
130
+
131
+
132
+ def mfsa_id_from_filename (filename ):
133
+ match = MFSA_FILENAME_RE .search (filename )
134
+ if match :
135
+ return "mfsa" + match .group (1 )
136
+
137
+ return None
138
+
139
+
140
+ def get_package_urls (pkgs : List [str ]) -> List [PackageURL ]:
141
+ package_urls = [
142
+ PackageURL (
143
+ type = "mozilla" ,
144
+ # pkg is of the form "Firefox ESR 1.21" or "Thunderbird 2.21"
145
+ name = pkg .rsplit (None , 1 )[0 ],
146
+ version = pkg .rsplit (None , 1 )[1 ],
147
+ )
148
+ for pkg in pkgs
149
+ if pkg
150
+ ]
151
+ return package_urls
152
+
153
+
154
+ def get_yml_references (data : any ) -> List [Reference ]:
155
+ """
156
+ Returns a list of references
157
+ Currently only considers the given mfsa as a reference
158
+ """
159
+ # FIXME: Needs improvement
160
+ # Should we add 'bugs' section in references too?
161
+ # Should we add 'impact'/severity of CVE in references too?
162
+ # If yes, then fix alpine_linux importer as well
163
+ # Otherwise, do we need severity field for adversary as well?
164
+
165
+ severities = ["critical" , "high" , "medium" , "low" , "none" ]
166
+ severity = "none"
167
+ if data .get ("impact" ):
168
+ impact = data .get ("impact" ).lower ()
169
+ for s in severities :
170
+ if s in impact :
171
+ severity = s
172
+ break
173
+
174
+ return [
175
+ Reference (
176
+ reference_id = data ["mfsa_id" ],
177
+ url = "https://www.mozilla.org/en-US/security/advisories/{}" .format (data ["mfsa_id" ]),
178
+ # severities=[VulnerabilitySeverity(scoring_systems["unspecified"], severity)],
179
+ severities = [VulnerabilitySeverity (scoring_systems ["cvssv3.1_qr" ], severity )],
180
+ )
181
+ ]
0 commit comments