comparison farolluz/parsers/cvrf.py @ 0:e18b61a73a68

Initial Release
author Benoît Allard <benoit.allard@greenbone.net>
date Tue, 23 Sep 2014 15:19:14 +0200
parents
children d47e1164740f
comparison
equal deleted inserted replaced
-1:000000000000 0:e18b61a73a68
1 # -*- coding: utf-8 -*-
2 # Description:
3 # Methods for parsing CVRF documents
4 #
5 # Authors:
6 # BenoƮt Allard <benoit.allard@greenbone.net>
7 #
8 # Copyright:
9 # Copyright (C) 2014 Greenbone Networks GmbH
10 #
11 # This program is free software; you can redistribute it and/or
12 # modify it under the terms of the GNU General Public License
13 # as published by the Free Software Foundation; either version 2
14 # of the License, or (at your option) any later version.
15 #
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
20 #
21 # You should have received a copy of the GNU General Public License
22 # along with this program; if not, write to the Free Software
23 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24
25 """\
26 Methods for parsing of CVRF Documents
27 """
28
29 from __future__ import print_function
30
31 import re
32 import textwrap
33 import xml.etree.ElementTree as ET
34 from datetime import datetime, timedelta
35
36 try:
37 from datetime import timezone
38 except ImportError:
39 from ..py2 import FixedTimeZone as timezone
40
41 from ..cvrf import (CVRF, CVRFPublisher, CVRFTracking, CVRFRevision, CVRFNote,
42 CVRFAcknowledgment, CVRFProductBranch, CVRFFullProductName, CVRFGenerator,
43 CVRFRelationship, CVRFVulnerability, CVRFVulnerabilityID, CVRFThreat,
44 CVRFProductStatus, CVRFCVSSSet, CVRFReference, CVRFRemediation, CVRFGroup,
45 CVRFInvolvement, CVRFCWE, CVRFTrackingID)
46
47 NAMESPACES = {
48 'cvrf': "http://www.icasi.org/CVRF/schema/cvrf/1.1",
49 'prod': "http://www.icasi.org/CVRF/schema/prod/1.1",
50 'vuln': "http://www.icasi.org/CVRF/schema/vuln/1.1",
51 'xml': "http://www.w3.org/XML/1998/namespace",
52 }
53
54
55 def UN(ns, name):
56 """ UN for Universal Name """
57 return "{%s}%s" % (NAMESPACES[ns], name)
58
59
60 def parseVersion(string):
61 return tuple(int(i) for i in string.split('.'))
62
63
64 def parseDate(string):
65 m = re.match('(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:([+-])(\d{2}):(\d{2})|(Z))?', string)
66 if (m.group(7) is None) or (m.group(7) == 'Z'):
67 tzhours = 0
68 tzmin = 0
69 else:
70 tzhours = int(m.group(8))
71 if m.group(7) == '-':
72 tzhours = - tzhours
73 tzmin = int(m.group(9))
74 return datetime(int(m.group(1)), int(m.group(2)), int(m.group(3)), int(m.group(4)), int(m.group(5)), int(m.group(6)), tzinfo=timezone(timedelta(hours=tzhours, minutes=tzmin)))
75
76
77 def parseNote(elem):
78 return CVRFNote(
79 elem.attrib['Type'],
80 int(elem.attrib['Ordinal']),
81 textwrap.dedent(elem.text).strip(),
82 elem.attrib.get('Title'),
83 elem.attrib.get('Audience')
84 )
85
86
87 def parseReference(elem, ns='cvrf'):
88 """ ns is the current namespace """
89 return CVRFReference(
90 elem.findtext(UN(ns, 'URL')).strip(),
91 textwrap.dedent(elem.findtext(UN(ns, 'Description'))).strip(),
92 elem.attrib.get('Type')
93 )
94
95
96 def parseAcknowledgment(elem, ns='cvrf'):
97 return CVRFAcknowledgment(
98 elem.findtext(UN(ns, 'Name')),
99 elem.findtext(UN(ns, 'Organization')),
100 elem.findtext(UN(ns, 'Description')),
101 elem.findtext(UN(ns, 'URL')),
102 )
103
104
105 def parseFullProductName(elem, parent):
106 return CVRFFullProductName(
107 elem.attrib['ProductID'],
108 elem.text.strip(),
109 parent,
110 cpe=elem.attrib.get('CPE')
111 )
112
113
114 def parseProdBranch(elem, ptree, parentbranch=None):
115 """ Recursively parses the branches and the terminal productnames """
116 fpncvrf = elem.find(UN('prod', 'FullProductName'))
117 if (parentbranch is not None) and (fpncvrf is not None):
118 # Don't process the products at the root of the tree
119 prod = parseFullProductName(fpncvrf, parentbranch)
120 ptree.addProduct(prod)
121
122 if parentbranch is None:
123 parentbranch = ptree
124 for brcvrf in elem.findall(UN('prod', 'Branch')):
125 br = CVRFProductBranch(brcvrf.attrib['Type'], brcvrf.attrib['Name'], parentbranch)
126 # And go into recursion ...
127 br._childs = list(parseProdBranch(brcvrf, ptree, br))
128 yield br
129
130
131 def parseVulnerability(elem):
132 vuln = CVRFVulnerability(int(elem.attrib['Ordinal']))
133
134 xmltitle = elem.findtext(UN('vuln', 'Title'))
135 if xmltitle is not None:
136 vuln.setTitle(xmltitle.strip())
137
138 xmlID = elem.find(UN('vuln', 'ID'))
139 if xmlID is not None:
140 vuln.setID(CVRFVulnerabilityID(xmlID.attrib['SystemName'], xmlID.text.strip()))
141
142 for xmlnote in elem.findall('/'.join([UN('vuln', 'Notes'), UN('vuln', 'Note')])):
143 vuln.addNote(parseNote(xmlnote))
144
145 xmldiscoverydate = elem.findtext(UN('vuln', 'DiscoveryDate'))
146 if xmldiscoverydate is not None:
147 vuln.setDiscoveryDate(parseDate(xmldiscoverydate))
148 xmlreleasedate = elem.findtext(UN('vuln', 'ReleaseDate'))
149 if xmlreleasedate is not None:
150 vuln.setReleaseDate(parseDate(xmlreleasedate))
151
152 for xmlinv in elem.findall('/'.join([UN('vuln', 'Involvements'), UN('vuln', 'Involvement')])):
153 involvement = CVRFInvolvement(
154 xmlinv.attrib['Party'],
155 xmlinv.attrib['Status']
156 )
157 xmldescr = xmlinv.findtext(UN('vuln', 'Description'))
158 if xmldescr is not None:
159 involvement.setDescription(textwrap.dedent(xmldescr).strip())
160 vuln.addInvolvement(involvement)
161
162 xmlcve = elem.findtext(UN('vuln', 'CVE'))
163 if xmlcve is not None:
164 vuln.setCVE(xmlcve.strip())
165
166 for xmlcwe in elem.findall(UN('vuln', 'CWE')):
167 vuln.addCWE(CVRFCWE(
168 xmlcwe.attrib['ID'],
169 xmlcwe.text.strip()
170 ))
171
172 for xmlstatus in elem.findall('/'.join([UN('vuln', 'ProductStatuses'), UN('vuln', 'Status')])):
173 status = CVRFProductStatus(xmlstatus.attrib['Type'])
174 for xmlproductid in xmlstatus.findall(UN('vuln', 'ProductID')):
175 status.addProductID(xmlproductid.text.strip())
176
177 vuln.addProductStatus(status)
178
179 for xmlthreat in elem.findall('/'.join([UN('vuln', 'Threats'), UN('vuln', 'Threat')])):
180 threat = CVRFThreat(
181 xmlthreat.attrib['Type'],
182 textwrap.dedent(xmlthreat.findtext(UN('vuln', 'Description'))).strip()
183 )
184 xmldate = xmlthreat.findtext(UN('vuln', 'Date'))
185 if xmldate is not None:
186 threat.setDate(parseDate(xmldate))
187 for xmlpid in xmlthreat.findall(UN('vuln', 'ProductID')):
188 threat.addProductID(xmlpid.text.strip())
189 for xmlgid in xmlthreat.findall(UN('vuln', 'GroupID')):
190 threat.addGroupID(xmlgid.text.strip())
191
192 vuln.addThreat(threat)
193
194 for xmlcvss in elem.findall('/'.join([UN('vuln', 'CVSSScoreSets'), UN('vuln', 'ScoreSet')])):
195 cvss_set = CVRFCVSSSet(float(xmlcvss.findtext(UN('vuln', 'BaseScore')).strip()))
196 xmltempscore = xmlcvss.findtext(UN('vuln', 'TemporalScore'))
197 if xmltempscore is not None:
198 cvss_set.setTemporalScore(float(xmltempscore.strip()))
199 xmlenvscore = xmlcvss.findtext(UN('vuln', 'EnvironmentalScore'))
200 if xmlenvscore is not None:
201 cvss_set.setEnvironmentalScore(float(xmlenvscore.strip()))
202 xmlvector = xmlcvss.findtext(UN('vuln', 'Vector'))
203 if xmlvector is not None:
204 cvss_set.setVector(xmlvector.strip())
205 for xmlprodid in xmlcvss.findall(UN('vuln', 'ProductID')):
206 cvss_set.addProductID(xmlprodid.text.strip())
207
208 vuln.addCVSSSet(cvss_set)
209
210 for xmlremediation in elem.findall('/'.join([UN('vuln', 'Remediations'), UN('vuln', 'Remediation')])):
211 remediation = CVRFRemediation(
212 xmlremediation.attrib['Type'],
213 textwrap.dedent(xmlremediation.findtext(UN('vuln', 'Description'))).strip()
214 )
215 xmldate = xmlremediation.findtext(UN('vuln', 'Date'))
216 if xmldate is not None:
217 remediation.setDate(parseDate(xmldate))
218 xmlentitlement = xmlremediation.findtext(UN('vuln', 'Entitlement'))
219 if xmlentitlement is not None:
220 remediation.setEntitlement(textwrap.dedent(xmlentitlement).strip())
221 xmlurl = xmlremediation.findtext(UN('vuln', 'URL'))
222 if xmlurl is not None:
223 remediation.setURL(xmlurl.strip())
224 for xmlpid in xmlremediation.findall(UN('vuln', 'ProductID')):
225 remediation.addProductID(xmlpid.text.strip())
226 for xmlgid in xmlremediation.findall(UN('vuln', 'GroupID')):
227 remediation.addGroupID(xmlgid.text.strip())
228
229 vuln.addRemediation(remediation)
230
231 for xmlref in elem.findall('/'.join([UN('vuln', 'References'), UN('vuln', 'Reference')])):
232 vuln.addReference(parseReference(xmlref, 'vuln'))
233
234 for xmlack in elem.findall('/'.join([UN('vuln', 'Acknowledgments'), UN('vuln', 'Acknowledgment')])):
235 vuln.addAcknowledgment(parseAcknowledgment(xmlack, 'vuln'))
236
237 return vuln
238
239
240 def parse(xml):
241 if hasattr(xml, 'read'):
242 xml = xml.read()
243 cvrfdoc = ET.fromstring(xml)
244 if cvrfdoc.tag != UN('cvrf', 'cvrfdoc'):
245 raise ValueError('Not a CVRF document !')
246 doc = CVRF(
247 cvrfdoc.findtext(UN('cvrf', 'DocumentTitle')).strip(),
248 cvrfdoc.findtext(UN('cvrf', 'DocumentType')).strip()
249 )
250 cvrfpub = cvrfdoc.find(UN('cvrf', 'DocumentPublisher'))
251 pub = CVRFPublisher(cvrfpub.attrib['Type'], cvrfpub.attrib.get('VendorID'))
252 doc.setPublisher(pub)
253 contact = cvrfpub.find(UN('cvrf', 'ContactDetails'))
254 if contact is not None:
255 pub.setContact(contact.text.strip())
256 authority = cvrfpub.find(UN('cvrf', 'IssuingAuthority'))
257 if authority is not None:
258 pub.setAuthority(authority.text.strip())
259 cvrftracking = cvrfdoc.find(UN('cvrf', 'DocumentTracking'))
260 identification = CVRFTrackingID(
261 cvrftracking.findtext('/'.join([UN('cvrf', 'Identification'), UN('cvrf', 'ID')])).strip()
262 )
263 for cvrfalias in cvrftracking.findall('/'.join([UN('cvrf', 'Identification'), UN('cvrf', 'Alias')])):
264 identification.addAlias(cvrfalias.text.strip())
265 tracking = CVRFTracking(
266 identification,
267 cvrftracking.findtext(UN('cvrf', 'Status')).strip(),
268 parseVersion(cvrftracking.findtext(UN('cvrf', 'Version')).strip()),
269 parseDate(cvrftracking.findtext(UN('cvrf', 'InitialReleaseDate')).strip()),
270 parseDate(cvrftracking.findtext(UN('cvrf', 'CurrentReleaseDate')).strip())
271 )
272 doc.setTracking(tracking)
273 for cvrfrev in cvrftracking.findall('/'.join([UN('cvrf', 'RevisionHistory'), UN('cvrf', 'Revision')])):
274 rev = CVRFRevision(
275 parseVersion(cvrfrev.findtext(UN('cvrf', 'Number')).strip()),
276 parseDate(cvrfrev.findtext(UN('cvrf', 'Date')).strip()),
277 cvrfrev.findtext(UN('cvrf', 'Description')).strip(),
278 )
279 tracking.addRevision(rev)
280
281 xmlgenerator = cvrftracking.find(UN('cvrf', 'Generator'))
282 if xmlgenerator is not None:
283 generator = CVRFGenerator()
284 xmlengine = xmlgenerator.findtext(UN('cvrf', 'Engine'))
285 if xmlengine is not None:
286 generator.setEngine(xmlengine.strip())
287 xmldate = xmlgenerator.findtext(UN('cvrf', 'Date'))
288 if xmldate is not None:
289 generator.setDate(parseDate(xmldate.strip()))
290 tracking.setGenerator(generator)
291
292 for cvrfnote in cvrfdoc.findall('/'.join([UN('cvrf', 'DocumentNotes'), UN('cvrf', 'Note')])):
293 doc.addNote(parseNote(cvrfnote))
294
295 distr = cvrfdoc.findtext(UN('cvrf', 'DocumentDistribution'))
296 if distr is not None:
297 doc.setDistribution(textwrap.dedent(distr).strip())
298
299 # This is in a quite free format, not sure how to do something with it ...
300 xmlaggsev = cvrfdoc.find(UN('cvrf', 'AggregateSeverity'))
301
302 for xmlref in cvrfdoc.findall('/'.join([UN('cvrf', 'DocumentReferences'), UN('cvrf', 'Reference')])):
303 doc.addReference(parseReference(xmlref))
304
305 for cvrfack in cvrfdoc.findall('/'.join([UN('cvrf', 'Acknowledgments'), UN('cvrf', 'Acknowledgment')])):
306 doc.addAcknowledgment(parseAcknowledgment(cvrfack))
307
308 # --- The ProductTree
309
310 cvrfptree = cvrfdoc.find(UN('prod', 'ProductTree'))
311 if cvrfptree is not None:
312 producttree = doc.createProductTree()
313 for branch in parseProdBranch(cvrfptree, producttree):
314 producttree.addBranch(branch)
315
316 for product in cvrfptree.findall(UN('prod', 'FullProductName')):
317 producttree.addProduct(parseFullProductName(product, producttree))
318
319 for cvrfrel in cvrfptree.findall(UN('prod', 'Relationship')):
320 rel = CVRFRelationship(
321 cvrfrel.attrib['ProductReference'],
322 cvrfrel.attrib['RelationType'],
323 cvrfrel.attrib['RelatesToProductReference']
324 )
325 producttree.addRelationship(rel)
326 producttree.addProduct(parseFullProductName(cvrfrel.find(UN('prod', 'FullProductName')), rel))
327
328 for xmlgroup in cvrfptree.findall('/'.join([UN('prod', 'ProductGroups'), UN('prod', 'Group')])):
329 group = CVRFGroup(xmlgroup.attrib['GroupID'])
330 xmldescr = xmlgroup.findtext(UN('prod', 'Description'))
331 if xmldescr is not None:
332 group.setDescription(textwrap.dedent(xmldescr).strip())
333 for xmlpid in xmlgroup.findall(UN('prod', 'ProductID')):
334 group.addProductID(xmlpid.text.strip())
335 producttree.addGroup(group)
336
337 # --- The Vulnerabilities
338
339 for cvrfvuln in cvrfdoc.findall(UN('vuln', 'Vulnerability')):
340 doc.addVulnerability(parseVulnerability(cvrfvuln))
341
342 return doc
343
344
345 if __name__ == "__main__":
346 import sys
347 with open(sys.argv[1], 'rt') as f:
348 cvrf = parse(f)
349 cvrf.validate()
350 print(cvrf)
351 print(cvrf.getHighestCVSS()._vector)
352 print(cvrf.getProductList())
353 print(cvrf._producttree._branches)
354 # print(cvrf._producttree._branches[0]._childs)
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)