comparison farolluz/document.py @ 26:809db989cac5

Reorganize the code in smaller mpodules
author Benoît Allard <benoit.allard@greenbone.net>
date Fri, 24 Oct 2014 17:01:26 +0200
parents farolluz/cvrf.py@4004b67216a9
children b15022ae484a
comparison
equal deleted inserted replaced
25:3cab052872f4 26:809db989cac5
1 # -*- coding: utf-8 -*-
2 #
3 # Authors:
4 # BenoƮt Allard <benoit.allard@greenbone.net>
5 #
6 # Copyright:
7 # Copyright (C) 2014 Greenbone Networks GmbH
8 #
9 # This program is free software; you can redistribute it and/or
10 # modify it under the terms of the GNU General Public License
11 # as published by the Free Software Foundation; either version 2
12 # of the License, or (at your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License
20 # along with this program; if not, write to the Free Software
21 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
22
23 """\
24 Objects related to CVRF Documents
25 """
26
27 from .common import ValidationError
28 from .producttree import CVRFProductTree, CVRFRelationship
29
30 class CVRFPublisher(object):
31 TYPES = ('Vendor', 'Discoverer', 'Coordinator', 'User', 'Other')
32 def __init__(self, _type, vendorid=None):
33 self._type = _type
34 self._vendorid = vendorid
35 self._contact = None
36 self._authority = None
37
38 def setContact(self, contact):
39 self._contact = contact
40
41 def setAuthority(self, authority):
42 self._authority = authority
43
44 def validate(self):
45 if not self._type:
46 raise ValidationError('Document Publisher needs to have a type')
47 if self._type not in self.TYPES:
48 raise ValidationError('Document Publisher Type needs to be one of %s' % ', '.join(self.TYPES))
49
50 def __str__(self):
51 s = 'CVRFPublisher: %s' % self._type
52 if self._vendorid is not None:
53 s += ' ID: %s' % self._vendorid
54 if self._contact is not None:
55 s += ' Contact: "%s"' % self._contact
56 if self._authority is not None:
57 s += ' Authority: "%s"' % self._authority
58 return s
59
60
61 class CVRFTrackingID(object):
62 def __init__(self, _id):
63 self._id = _id
64 self._aliases = []
65
66 def addAlias(self, alias):
67 self._aliases.append(alias)
68
69 def getId(self):
70 return self._id
71
72 def validate(self):
73 if not self._id:
74 raise ValidationError('Document ID cannot be left empty')
75
76 def __str__(self):
77 if self._aliases:
78 return "%s (%s)" % (self._id, ', '.join(self._aliases))
79 return self._id
80
81
82 class CVRFTracking(object):
83 STATUSES = ('Draft', 'Interim', 'Final')
84 def __init__(self, _id, status, version, initial, current):
85 self._identification = _id
86 self._status = status
87 self._version = version
88 self._history = []
89 self._initialDate = initial
90 self._currentDate = current
91 self._generator = None
92
93 def addRevision(self, revision):
94 self._history.append(revision)
95
96 def setGenerator(self, generator):
97 self._generator = generator
98
99 def getId(self):
100 return self._identification.getId()
101
102 def validate(self):
103 if self._identification is None:
104 raise ValidationError('Document Tracking needs to have an Identification')
105 self._identification.validate()
106 if not self._status:
107 raise ValidationError('Document status must be set')
108 if self._status not in self.STATUSES:
109 raise ValidationError('Document Status must be one of %s' % ', '.join(self.STATUSES))
110 if not self._version:
111 raise ValidationError('Document Version must be set')
112 if len(self._version) > 4:
113 raise ValidationError('Document Version must be comprised between `nn` and `nn.nn.nn.nn`')
114 if not self._history:
115 raise ValidationError('Document must have at least a revision')
116 if not self._initialDate:
117 raise ValidationError('Document must have an initial Release date set')
118 prev_date = self._initialDate
119 if self._history[0]._date < self._initialDate:
120 # Documents could have revisions before being released
121 prev_date = self._history[0]._date
122 prev = ()
123 for revision in self._history:
124 revision.validate()
125 if revision._number <= prev:
126 raise ValidationError('Revision numbers must always be increasing')
127 if revision._date < prev_date:
128 raise ValidationError('Revision dates must always be increasing')
129 prev = revision._number
130 prev_date = revision._date
131 if not self._currentDate:
132 raise ValidationError('Document must have a Current Release Date set')
133 if self._currentDate != self._history[-1]._date:
134 raise ValidationError('Current Release Date must be the same as the Date from the last Revision')
135 if self._initialDate > self._currentDate:
136 raise ValidationError('Initial date must not be after current Date')
137 if self._version != self._history[-1]._number:
138 raise ValidationError('Document version must be the same as the number of the last Revision')
139
140 def __str__(self):
141 s = "ID: %s" % self._identification
142 s += " Status: %s" % self._status
143 s += " v%s" % '.'.join('%d' % i for i in self._version)
144 s += " %d revisions" % len(self._history)
145 s += " Initial release: %s" % self._initialDate.isoformat()
146 return s
147
148
149 class CVRFRevision(object):
150 def __init__(self, number, date, description):
151 self._number = number
152 self._date = date
153 self._description = description
154
155 def validate(self):
156 if not self._number:
157 raise ValidationError('A Revision must have a Number')
158 if not self._date:
159 raise ValidationError('A Revision must have a Date')
160 if not self._description:
161 raise ValidationError('A Revision must have a Description')
162
163 class CVRFGenerator(object):
164 def __init__(self):
165 self._engine = None
166 self._date = None
167
168 def setEngine(self, engine):
169 self._engine = engine
170
171 def setDate(self, date):
172 self._date = date
173
174 def validate(self):
175 if (not self._engine) and (not self._date):
176 raise ValidationError('The Generator must have at least an Engine or a Date')
177
178
179 class CVRFAggregateSeverity(object):
180 def __init__(self, severity):
181 self._severity = severity
182 self._namespace = None
183
184 def setNamespace(self, namespace):
185 self._namespace = namespace
186
187
188 class CVRF(object):
189 def __init__(self, title, _type):
190 self._title = title
191 self._type = _type
192 self._publisher = None
193 self._tracking = None
194 self._notes = []
195 self._distribution = None
196 self._aggregateseverity = None
197 self._references = []
198 self._acknowledgments = []
199 self._producttree = None
200 self._vulnerabilities = []
201
202 def setPublisher(self, publisher):
203 self._publisher = publisher
204
205 def setTracking(self, tracking):
206 self._tracking = tracking
207
208 def addNote(self, note):
209 self._notes.append(note)
210
211 def setDistribution(self, distribution):
212 self._distribution = distribution
213
214 def setAggregateSeverity(self, aggregateseverity):
215 self._aggregateseverity = aggregateseverity
216
217 def addReference(self, ref):
218 self._references.append(ref)
219
220 def addAcknowledgment(self, ack):
221 self._acknowledgments.append(ack)
222
223 def createProductTree(self):
224 """ only done if the element is there """
225 self._producttree = CVRFProductTree()
226 return self._producttree
227
228 def addVulnerability(self, vuln):
229 self._vulnerabilities.append(vuln)
230
231 def getProductForID(self, productid):
232 if self._producttree is None:
233 raise ValueError('No ProductTree')
234 return self._producttree.getProductForID(productid)
235
236 def getGroupForID(self, groupid):
237 if self._producttree is None:
238 raise ValueError('No ProductTree')
239 return self._producttree.getGroupForID(groupid)
240
241 def getHighestCVSS(self):
242 highestBaseScore = 0
243 highest = None
244 for vulnerability in self._vulnerabilities:
245 for cvss in vulnerability._cvsss:
246 if cvss._basescore <= highestBaseScore:
247 continue
248 highestBaseScore = cvss._basescore
249 highest = cvss
250 return highest
251
252 def getProductList(self, type_='Fixed'):
253 products = set()
254 if type_ == 'Fixed':
255 # First try through the Remediation
256 for vulnerability in self._vulnerabilities:
257 for remediation in vulnerability._remediations:
258 if remediation._type != 'Vendor Fix':
259 continue
260 for productid in remediation._productids:
261 products.add(productid)
262 for groupid in remediation._groupids:
263 for productid in self.getGroupForID(groupid)._productids:
264 products.add(productid)
265 if not products:
266 # If nothing there, try through the productstatuses
267 for vulnerability in self._vulnerabilities:
268 for status in vulnerability._productstatuses:
269 if status._type != type_:
270 continue
271 for productid in status._productids:
272 products.add(productid)
273 return set(self.getProductForID(p) for p in products)
274
275 def mentionsProductId(self, productid):
276 # We first look at the ProductTree
277 ptree = self._producttree
278 for relation in ptree._relationships:
279 if productid == relation._productreference:
280 yield relation
281 elif productid == relation._relatestoproductreference:
282 yield relation
283 # Then go through the groups
284 for group in ptree._groups:
285 if productid in group._productids:
286 yield group
287 # Finally, go through all the Vulnerabilities
288 for vulnerability in self._vulnerabilities:
289 for item in vulnerability.mentionsProdId(productid):
290 yield item
291
292 def isProductOrphan(self, productid):
293 """ Returns if a productid is mentioned nowhere in the document """
294 for item in self.mentionsProductId(productid):
295 return True
296 return False
297
298 def changeProductID(self, old, new):
299 for item in self.mentionsProductId(old):
300 if isinstance(item, CVRFRelationship):
301 if old == item._productreference:
302 item._productreference = new
303 elif old == item._relatestoproductreference:
304 item._relatestoproductreference = new
305 else:
306 item._productids.remove(old)
307 item._productids.append(new)
308
309 def isGroupOrphan(self, groupid):
310 """ Returns if a group can be safely deleted """
311 for vulnerability in self._vulnerabilities:
312 if vulnerability.isMentioningGroupId(groupid):
313 return False
314 return True
315
316 def isProductTreeOrphan(self):
317 """ Difference with the previous method is that we don;t care about
318 inter-producttree references """
319 for vulnerability in self._vulnerabilities:
320 for product in self._producttree._products:
321 if vulnerability.isMentioningProdId(product._productid):
322 return False
323 for group in self._producttree._groups:
324 if vulnerability.isMentioningGroupId(group._groupid):
325 return False
326 return True
327
328 def getNote(self, ordinal):
329 for note in self._notes:
330 if note._ordinal == ordinal:
331 return note
332 return None
333
334 def getDocId(self):
335 if self._tracking is not None:
336 return self._tracking.getId()
337 # Make up something ...
338 return self._title.lower()
339
340 def validate(self):
341 if not self._title:
342 raise ValidationError('Document Title cannot be empty')
343 if not self._type:
344 raise ValidationError('Document Type cannot be empty')
345 if self._publisher is None:
346 raise ValidationError('Document Publisher needs to be set')
347 self._publisher.validate()
348 if self._tracking is None:
349 raise ValidationError('Document Tracking needs to be set')
350 self._tracking.validate()
351 ordinals = set()
352 for note in self._notes:
353 note.validate()
354 if note._ordinal in ordinals:
355 raise ValidationError('Document Note ordinal %d is issued twice' % note._ordinal)
356 ordinals.add(note._ordinal)
357 for reference in self._references:
358 reference.validate()
359 for acknowledgment in self._acknowledgments:
360 acknowledgment.validate()
361 productids = set()
362 groupids = set()
363 if self._producttree:
364 productids, groupids = self._producttree.validate()
365 ordinals = set()
366 for vulnerability in self._vulnerabilities:
367 vulnerability.validate(productids, groupids)
368 if vulnerability._ordinal in ordinals:
369 raise ValidationError('Vulnerability ordinal %d is issued twice' % vulnerability._ordinal)
370 ordinals.add(vulnerability._ordinal)
371
372 def __str__(self):
373 s = [
374 'Title: %s' % self._title,
375 'Type: %s' % self._type,
376 'Publisher: %s' % self._publisher,
377 'tracking: %s' % self._tracking,
378 '%d Notes: %s' % (len(self._notes), ', '.join(
379 str(n) for n in self._notes))
380 ]
381 if self._distribution is not None:
382 s.append('Distribution: %s' % self._distribution)
383 s.extend([
384 '%d Acknowledgments' % len(self._acknowledgments),
385 'Products: %s' % self._producttree,
386 ])
387 return '\n'.join(s)
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)