Mercurial > farol > farolluz
annotate farolluz/cvrf.py @ 17:90852c11fabd
Add methods to extract Product references in a document.
author | Benoît Allard <benoit.allard@greenbone.net> |
---|---|
date | Tue, 14 Oct 2014 16:48:22 +0200 |
parents | dcc946b30343 |
children | 4b53e7bcff0d |
rev | line source |
---|---|
0 | 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 class ValidationError(Exception): pass | |
28 | |
29 class CVRFPublisher(object): | |
30 TYPES = ('Vendor', 'Discoverer', 'Coordinator', 'User', 'Other') | |
31 def __init__(self, _type, vendorid=None): | |
32 self._type = _type | |
33 self._vendorid = vendorid | |
34 self._contact = None | |
35 self._authority = None | |
36 | |
37 def setContact(self, contact): | |
38 self._contact = contact | |
39 | |
40 def setAuthority(self, authority): | |
41 self._authority = authority | |
42 | |
43 def validate(self): | |
44 if not self._type: | |
45 raise ValidationError('Document Publisher needs to have a type') | |
46 if self._type not in self.TYPES: | |
47 raise ValidationError('Document Publisher Type needs to be one of %s' % ', '.join(self.TYPES)) | |
48 | |
49 def __str__(self): | |
50 s = 'CVRFPublisher: %s' % self._type | |
51 if self._vendorid is not None: | |
52 s += ' ID: %s' % self._vendorid | |
53 if self._contact is not None: | |
54 s += ' Contact: "%s"' % self._contact | |
55 if self._authority is not None: | |
56 s += ' Authority: "%s"' % self._authority | |
57 return s | |
58 | |
59 | |
60 class CVRFTrackingID(object): | |
61 def __init__(self, _id): | |
62 self._id = _id | |
63 self._aliases = [] | |
64 | |
65 def addAlias(self, alias): | |
66 self._aliases.append(alias) | |
67 | |
6
633ebfcff0d0
Add a method to request a Document ID
Benoît Allard <benoit.allard@greenbone.net>
parents:
1
diff
changeset
|
68 def getId(self): |
633ebfcff0d0
Add a method to request a Document ID
Benoît Allard <benoit.allard@greenbone.net>
parents:
1
diff
changeset
|
69 return self._id |
633ebfcff0d0
Add a method to request a Document ID
Benoît Allard <benoit.allard@greenbone.net>
parents:
1
diff
changeset
|
70 |
0 | 71 def validate(self): |
72 if not self._id: | |
73 raise ValidationError('Document ID cannot be left empty') | |
74 | |
75 def __str__(self): | |
76 if self._aliases: | |
77 return "%s (%s)" % (self._id, ', '.join(self._aliases)) | |
78 return self._id | |
79 | |
80 | |
81 class CVRFTracking(object): | |
82 STATUSES = ('Draft', 'Interim', 'Final') | |
83 def __init__(self, _id, status, version, initial, current): | |
84 self._identification = _id | |
85 self._status = status | |
86 self._version = version | |
87 self._history = [] | |
88 self._initialDate = initial | |
89 self._currentDate = current | |
90 self._generator = None | |
91 | |
92 def addRevision(self, revision): | |
93 self._history.append(revision) | |
94 | |
95 def setGenerator(self, generator): | |
96 self._generator = generator | |
97 | |
6
633ebfcff0d0
Add a method to request a Document ID
Benoît Allard <benoit.allard@greenbone.net>
parents:
1
diff
changeset
|
98 def getId(self): |
633ebfcff0d0
Add a method to request a Document ID
Benoît Allard <benoit.allard@greenbone.net>
parents:
1
diff
changeset
|
99 return self._identification.getId() |
633ebfcff0d0
Add a method to request a Document ID
Benoît Allard <benoit.allard@greenbone.net>
parents:
1
diff
changeset
|
100 |
0 | 101 def validate(self): |
102 if self._identification is None: | |
103 raise ValidationError('Document Tracking needs to have an Identification') | |
104 self._identification.validate() | |
105 if not self._status: | |
106 raise ValidationError('Document status must be set') | |
107 if self._status not in self.STATUSES: | |
108 raise ValidationError('Document Status must be one of %s' % ', '.join(self.STATUSES)) | |
109 if not self._version: | |
110 raise ValidationError('Document Version must be set') | |
111 if len(self._version) > 4: | |
112 raise ValidationError('Document Version must be comprised between `nn` and `nn.nn.nn.nn`') | |
113 if not self._history: | |
114 raise ValidationError('Document must have at least a revision') | |
115 if not self._initialDate: | |
116 raise ValidationError('Document must have an initial Release date set') | |
117 prev_date = self._initialDate | |
118 if self._history[0]._date < self._initialDate: | |
119 # Documents could have revisions before being released | |
120 prev_date = self._history[0]._date | |
121 prev = () | |
122 for revision in self._history: | |
123 revision.validate() | |
124 if revision._number <= prev: | |
125 raise ValidationError('Revision numbers must always be increasing') | |
126 if revision._date < prev_date: | |
127 raise ValidationError('Revision dates must always be increasing') | |
128 prev = revision._number | |
129 prev_date = revision._date | |
130 if not self._currentDate: | |
131 raise ValidationError('Document must have a Current Release Date set') | |
132 if self._currentDate != self._history[-1]._date: | |
133 raise ValidationError('Current Release Date must be the same as the Date from the last Revision') | |
134 if self._initialDate > self._currentDate: | |
135 raise ValidationError('Initial date must not be after current Date') | |
136 if self._version != self._history[-1]._number: | |
137 raise ValidationError('Document version must be the same as the number of the last Revision') | |
138 | |
139 def __str__(self): | |
140 s = "ID: %s" % self._identification | |
141 s += " Status: %s" % self._status | |
142 s += " v%s" % '.'.join('%d' % i for i in self._version) | |
143 s += " %d revisions" % len(self._history) | |
144 s += " Initial release: %s" % self._initialDate.isoformat() | |
145 return s | |
146 | |
147 | |
148 class CVRFRevision(object): | |
149 def __init__(self, number, date, description): | |
150 self._number = number | |
151 self._date = date | |
152 self._description = description | |
153 | |
154 def validate(self): | |
155 if not self._number: | |
156 raise ValidationError('A Revision must have a Number') | |
157 if not self._date: | |
158 raise ValidationError('A Revision must have a Date') | |
159 if not self._description: | |
160 raise ValidationError('A Revision must have a Description') | |
161 | |
162 class CVRFGenerator(object): | |
163 def __init__(self): | |
164 self._engine = None | |
165 self._date = None | |
166 | |
167 def setEngine(self, engine): | |
168 self._engine = engine | |
169 | |
170 def setDate(self, date): | |
171 self._date = date | |
172 | |
173 def validate(self): | |
174 if (not self._engine) and (not self._date): | |
175 raise ValidationError('The Generator must have at least an Engine or a Date') | |
176 | |
177 | |
178 class CVRFNote(object): | |
179 TYPES = ('General', 'Details', 'Description', 'Summary', 'FAQ', | |
180 'Legal Disclaimer', 'Other') | |
181 def __init__(self, _type, ordinal, note, title=None, audience=None): | |
182 self._type = _type | |
183 self._ordinal = ordinal | |
184 self._note = note | |
185 self._title = title | |
186 self._audience = audience | |
187 | |
188 def getTitle(self): | |
189 """ returns something that can be used as a title """ | |
190 if self._title is None: | |
191 return "%s (#%d)" % (self._type, self._ordinal) | |
192 return "%s (%s)" % (self._title, self._type) | |
193 | |
194 def validate(self): | |
195 if not self._type: | |
196 raise ValidationError('A Note needs to have a Type set') | |
197 if self._type not in self.TYPES: | |
198 raise ValidationError('A Note Type needs to be one of %s' % ', '.join(self.TYPES)) | |
199 if self._ordinal < 0: | |
200 raise ValidationError('A Note ordinal must be a positive integer') | |
201 if not self._note: | |
202 raise ValidationError('A Note must contain some text') | |
203 | |
204 | |
205 def __str__(self): | |
206 return self._note | |
207 | |
208 | |
1
d47e1164740f
Add support for AggregateSeverity
Benoît Allard <benoit.allard@greenbone.net>
parents:
0
diff
changeset
|
209 class CVRFAggregateSeverity(object): |
d47e1164740f
Add support for AggregateSeverity
Benoît Allard <benoit.allard@greenbone.net>
parents:
0
diff
changeset
|
210 def __init__(self, severity): |
d47e1164740f
Add support for AggregateSeverity
Benoît Allard <benoit.allard@greenbone.net>
parents:
0
diff
changeset
|
211 self._severity = severity |
d47e1164740f
Add support for AggregateSeverity
Benoît Allard <benoit.allard@greenbone.net>
parents:
0
diff
changeset
|
212 self._namespace = None |
d47e1164740f
Add support for AggregateSeverity
Benoît Allard <benoit.allard@greenbone.net>
parents:
0
diff
changeset
|
213 |
d47e1164740f
Add support for AggregateSeverity
Benoît Allard <benoit.allard@greenbone.net>
parents:
0
diff
changeset
|
214 def setNamespace(self, namespace): |
d47e1164740f
Add support for AggregateSeverity
Benoît Allard <benoit.allard@greenbone.net>
parents:
0
diff
changeset
|
215 self._namespace = namespace |
d47e1164740f
Add support for AggregateSeverity
Benoît Allard <benoit.allard@greenbone.net>
parents:
0
diff
changeset
|
216 |
0 | 217 class CVRFReference(object): |
218 TYPES = ('Self', 'External') | |
219 def __init__(self, url, description, _type=None): | |
220 self._url = url | |
221 self._description = description | |
222 self._type = _type | |
223 | |
224 def validate(self): | |
225 if (self._type is not None) and (self._type not in self.TYPES): | |
226 raise ValidationError('If a Reference type is set, it mist be one of %s' % ', '.join(self.TYPES)) | |
227 if not self._url: | |
228 raise ValidationError('A Reference must contain an URL') | |
229 if not self._description: | |
230 raise ValidationError('A Reference must contain a description') | |
231 | |
232 | |
233 class CVRFAcknowledgment(object): | |
8
cb8b2a196f0b
Allow Name and Organization in anAcknowledgment to be plural
Benoît Allard <benoit.allard@greenbone.net>
parents:
7
diff
changeset
|
234 def __init__(self, names=[], organizations=[], description=None, |
0 | 235 url=None): |
8
cb8b2a196f0b
Allow Name and Organization in anAcknowledgment to be plural
Benoît Allard <benoit.allard@greenbone.net>
parents:
7
diff
changeset
|
236 self._names = names |
cb8b2a196f0b
Allow Name and Organization in anAcknowledgment to be plural
Benoît Allard <benoit.allard@greenbone.net>
parents:
7
diff
changeset
|
237 self._organizations = organizations |
0 | 238 self._description = description |
239 self._url = url | |
240 | |
241 def getTitle(self): | |
8
cb8b2a196f0b
Allow Name and Organization in anAcknowledgment to be plural
Benoît Allard <benoit.allard@greenbone.net>
parents:
7
diff
changeset
|
242 return "%s - %s" % (', '.join(self._names), |
cb8b2a196f0b
Allow Name and Organization in anAcknowledgment to be plural
Benoît Allard <benoit.allard@greenbone.net>
parents:
7
diff
changeset
|
243 ', '.join(self._organizations)) |
0 | 244 |
245 def validate(self): | |
8
cb8b2a196f0b
Allow Name and Organization in anAcknowledgment to be plural
Benoît Allard <benoit.allard@greenbone.net>
parents:
7
diff
changeset
|
246 if (not self._names) and (not self._organizations) and (not self._description): |
0 | 247 raise ValidationError('An Acknowledgment must have at least a Name, an Organization or a Description') |
248 | |
249 | |
250 class CVRFProductTree(object): | |
251 def __init__(self): | |
252 # All the branches, they can be order with their `parent` attribute | |
253 self._branches = [] | |
254 self._groups = [] | |
255 self._relationships = [] | |
256 self._products = [] | |
257 self._groups = [] | |
258 | |
259 def addProduct(self, product): | |
15
dcc946b30343
Consolidate productTree edition
Benoît Allard <benoit.allard@greenbone.net>
parents:
14
diff
changeset
|
260 """ Add to the product list """ |
dcc946b30343
Consolidate productTree edition
Benoît Allard <benoit.allard@greenbone.net>
parents:
14
diff
changeset
|
261 self._products.append(product) |
0 | 262 |
263 def addRelationship(self, rel): | |
264 self._relationships.append(rel) | |
265 | |
266 def addGroup(self, group): | |
267 self._groups.append(group) | |
268 | |
269 def getProductForID(self, productid): | |
270 for product in self._products: | |
271 if product._productid == productid: | |
272 return product | |
273 raise KeyError(productid) | |
274 | |
275 def getGroupForID(self, groupid): | |
276 for group in self._groups: | |
277 if group._groupid == groupid: | |
278 return group | |
279 raise KeyError(groupid) | |
280 | |
281 def decomposeProduct(self, productid): | |
282 """ In case of product defined as a relationship (product X installed | |
283 on OS Y), this gives us the following tuple: (OS, product). """ | |
284 product = self.getProductForID(productid) | |
285 parent = product._parent | |
286 if parent is None: | |
287 return (None, None) | |
288 if not isinstance(parent, CVRFRelationship): | |
289 return (None, None) | |
290 relationtype = parent._relationtype.replace(' ', '').lower() | |
291 if relationtype not in ('defaultcomponentof', 'installedon'): | |
292 return (None, None) | |
293 return ( | |
294 self.getProductForID(parent._relatestoproductreference), | |
295 self.getProductForID(parent._productreference) | |
296 ) | |
297 | |
298 def getBranch(self, path): | |
299 if len(path) == 0: | |
300 return self | |
301 branches = self._branches | |
302 node = None | |
303 for idx in path: | |
304 node = branches[idx] | |
305 branches = node._childs | |
306 return node | |
307 | |
308 def getBranches(self): | |
309 for branch in self._branches: | |
310 yield branch | |
311 for sub_branch in branch.getBranches(): | |
312 yield sub_branch | |
313 | |
314 def getPath(self): | |
315 return () | |
316 | |
317 def getNameOfRelationship(self, relationship): | |
318 if relationship is None: | |
319 return '' | |
320 return ' '.join((self.getProductForID(relationship._productreference)._name, 'as', | |
321 relationship._relationtype.lower(), | |
322 self.getProductForID(relationship._relatestoproductreference)._name)) | |
323 | |
324 def getOrphanedBranches(self, product=None): | |
325 """ The branches that could accept `product` as Product Definition """ | |
326 white_list = [] | |
327 if product is not None: | |
328 white_list = [product._parent] | |
329 for branch in self.getBranches(): | |
330 if (branch in white_list) or branch.isOrphaned(): | |
331 yield branch | |
332 | |
333 def getNotTerminalBranches(self, b2=None): | |
334 """\ | |
335 The branches that could accept `b2` as new sub-branches | |
336 Note that b2 and all its sub-branches cannot be listed | |
337 """ | |
338 black_list = [] | |
339 if b2 is not None: | |
340 black_list = [b2] + list(b2.getBranches()) | |
341 for branch in self.getBranches(): | |
342 if branch in black_list: | |
343 continue | |
344 if branch._product is None: | |
345 yield branch | |
346 | |
347 def getOrphanedRelationships(self, product=None): | |
348 """ The relationships that need a product defninition """ | |
349 white_list = [] | |
350 if product is not None: | |
351 white_list = [product.getCurrentRelationship()] | |
352 for i, relationship in enumerate(self._relationships): | |
353 if (relationship in white_list) or relationship.isOrphaned(): | |
354 yield (i, relationship) | |
355 | |
356 def nbProducts(self): | |
357 """ Amount of 'raw' Products """ | |
358 return len([p for p in self._products if p._parent is self]) | |
359 | |
360 def validate(self): | |
361 for branch in self._branches: | |
362 branch.validate() | |
363 productids = set() | |
364 for product in self._products: | |
365 product.validate() | |
366 if product._productid in productids: | |
367 raise ValidationError('Each ProductID must be unique (%s)' % product._productid) | |
368 productids.add(product._productid) | |
369 for relationship in self._relationships: | |
370 relationship.validate() | |
371 for productid in (relationship._productreference, | |
372 relationship._relatestoproductreference): | |
373 if productid not in productids: | |
374 raise ValidationError('ProductID %s is unknown' % productid) | |
375 groupids = set() | |
376 for group in self._groups: | |
377 group.validate() | |
378 if group._groupid in groupids: | |
379 raise ValidationError('Duplicated GroupID: %s' % group._groupid) | |
380 groupids.add(group._groupid) | |
381 for productid in group._productids: | |
382 if productid not in productids: | |
383 raise ValidationError('ProductID %s is unknown' % productid) | |
384 return productids, groupids | |
385 | |
386 def __str__(self): | |
387 return 'Products: %s' % '\n'.join(str(p) for p in self._products) | |
388 | |
389 | |
390 class CVRFProductBranch(object): | |
391 TYPES = ('Vendor', 'Product Family', 'Product Name', 'Product Version', | |
392 'Patch Level', 'Service Pack', 'Architecture', 'Language', | |
393 'Legacy', 'Specification') | |
394 def __init__(self, _type, name, parentbranch): | |
395 self._type = _type | |
396 self._name = name | |
397 self._childs = [] | |
398 self._product = None | |
15
dcc946b30343
Consolidate productTree edition
Benoît Allard <benoit.allard@greenbone.net>
parents:
14
diff
changeset
|
399 self.link(parentbranch) |
0 | 400 |
401 def getParent(self): | |
402 return self._parentbranch | |
403 | |
404 def getPath(self, string=False): | |
405 """ return the path to that branch element as a tuple """ | |
406 if self.isRoot(): | |
407 for i, b in enumerate(self._parentbranch._branches): | |
408 if b is self: | |
409 if string: | |
410 return '%d' % i | |
411 return (i, ) | |
412 else: | |
413 for i, b in enumerate(self._parentbranch._childs): | |
414 if b is self: | |
415 if string: | |
416 return '/'.join([self._parentbranch.getPath(string), '%d' % i]) | |
417 return self._parentbranch.getPath(string) + (i,) | |
418 if string: | |
419 return '' | |
420 return () | |
421 | |
422 def getTree(self): | |
423 """ this returns a list of tuples (type, name) leading to here""" | |
424 if self.isRoot(): | |
425 return [(self._type, self._name)] | |
426 return self._parentbranch.getTree() + [(self._type, self._name)] | |
427 | |
428 def getName(self): | |
429 return ' / '.join("%s: %s" % (type_, name) for type_, name in self.getTree()) | |
430 | |
431 def getParentPath(self): | |
432 """ return as string the path to the parent """ | |
433 return '/'.join('%s' % p for p in self.getPath()[:-1]) | |
434 | |
435 def isRoot(self): | |
436 return isinstance(self._parentbranch, CVRFProductTree) | |
437 | |
438 def isOrphaned(self): | |
439 """ Has no childs and no product """ | |
440 return len(self._childs) == 0 and (self._product is None) | |
441 | |
442 def getBranches(self): | |
443 for branch in self._childs: | |
444 yield branch | |
445 for sub_branch in branch.getBranches(): | |
446 yield sub_branch | |
447 | |
448 def unlink(self): | |
449 """ Unset our _parent, and remove us from the _parent._childs """ | |
450 if self.isRoot(): | |
451 self.getParent()._branches.remove(self) | |
452 else: | |
453 self.getParent()._childs.remove(self) | |
454 self._parentbranch = None | |
455 | |
15
dcc946b30343
Consolidate productTree edition
Benoît Allard <benoit.allard@greenbone.net>
parents:
14
diff
changeset
|
456 def link(self, parent): |
dcc946b30343
Consolidate productTree edition
Benoît Allard <benoit.allard@greenbone.net>
parents:
14
diff
changeset
|
457 """ Actually, only set the parent """ |
dcc946b30343
Consolidate productTree edition
Benoît Allard <benoit.allard@greenbone.net>
parents:
14
diff
changeset
|
458 self._parentbranch = parent |
dcc946b30343
Consolidate productTree edition
Benoît Allard <benoit.allard@greenbone.net>
parents:
14
diff
changeset
|
459 if self.isRoot(): |
dcc946b30343
Consolidate productTree edition
Benoît Allard <benoit.allard@greenbone.net>
parents:
14
diff
changeset
|
460 parent._branches.append(self) |
dcc946b30343
Consolidate productTree edition
Benoît Allard <benoit.allard@greenbone.net>
parents:
14
diff
changeset
|
461 else: |
dcc946b30343
Consolidate productTree edition
Benoît Allard <benoit.allard@greenbone.net>
parents:
14
diff
changeset
|
462 parent._childs.append(self) |
dcc946b30343
Consolidate productTree edition
Benoît Allard <benoit.allard@greenbone.net>
parents:
14
diff
changeset
|
463 |
dcc946b30343
Consolidate productTree edition
Benoît Allard <benoit.allard@greenbone.net>
parents:
14
diff
changeset
|
464 |
0 | 465 def validate(self): |
466 if not self._type: | |
467 raise ValidationError('A Branch must have a Type') | |
468 if self._type not in self.TYPES: | |
469 raise ValidationError('A Branch Type must be one of %s' % ', '.join(self.TYPES)) | |
470 if not self._name: | |
471 raise ValidationError('A Branch must have a Name') | |
472 for branch in self._childs: | |
473 branch.validate() | |
474 if self.isOrphaned(): | |
475 raise ValidationError('A Branch must have at least a sub-product or sub-branches') | |
476 | |
477 def __str__(self): | |
478 return "%s: %s" % (self._type, self._name) | |
479 | |
480 | |
481 class CVRFFullProductName(object): | |
482 def __init__(self, productid, name, parent, cpe=None): | |
483 self._productid = productid | |
484 self._name = name | |
15
dcc946b30343
Consolidate productTree edition
Benoît Allard <benoit.allard@greenbone.net>
parents:
14
diff
changeset
|
485 self._cpe = cpe |
0 | 486 # Can be None (directly under the tree), a ProductBranch, or a |
487 # Relationship | |
15
dcc946b30343
Consolidate productTree edition
Benoît Allard <benoit.allard@greenbone.net>
parents:
14
diff
changeset
|
488 self.link(parent) |
0 | 489 |
490 def isRoot(self): | |
491 return isinstance(self._parent, CVRFProductTree) | |
492 | |
493 def isRelationship(self): | |
494 return isinstance(self._parent, CVRFRelationship) | |
495 | |
496 def getTree(self): | |
497 if not isinstance(self._parent, CVRFProductBranch): | |
498 return [] | |
499 return self._parent.getTree() | |
500 | |
501 def getParentPath(self): | |
502 if self.isRoot() or self.isRelationship(): | |
503 return '' | |
504 return self._parent.getPath(True) | |
505 | |
506 def getCurrentRelationship(self): | |
507 if self.isRelationship(): | |
508 return self._parent | |
509 return None | |
510 | |
511 def unlink(self): | |
15
dcc946b30343
Consolidate productTree edition
Benoît Allard <benoit.allard@greenbone.net>
parents:
14
diff
changeset
|
512 """ Unset our _parent, and remove us from the _parent._childs |
dcc946b30343
Consolidate productTree edition
Benoît Allard <benoit.allard@greenbone.net>
parents:
14
diff
changeset
|
513 We are still in the product list. |
dcc946b30343
Consolidate productTree edition
Benoît Allard <benoit.allard@greenbone.net>
parents:
14
diff
changeset
|
514 """ |
dcc946b30343
Consolidate productTree edition
Benoît Allard <benoit.allard@greenbone.net>
parents:
14
diff
changeset
|
515 if not self.isRoot(): |
0 | 516 self._parent._product = None |
517 self._parent = None | |
518 | |
15
dcc946b30343
Consolidate productTree edition
Benoît Allard <benoit.allard@greenbone.net>
parents:
14
diff
changeset
|
519 def link(self, parent): |
dcc946b30343
Consolidate productTree edition
Benoît Allard <benoit.allard@greenbone.net>
parents:
14
diff
changeset
|
520 self._parent = parent |
dcc946b30343
Consolidate productTree edition
Benoît Allard <benoit.allard@greenbone.net>
parents:
14
diff
changeset
|
521 if not self.isRoot(): |
dcc946b30343
Consolidate productTree edition
Benoît Allard <benoit.allard@greenbone.net>
parents:
14
diff
changeset
|
522 parent._product = self |
dcc946b30343
Consolidate productTree edition
Benoît Allard <benoit.allard@greenbone.net>
parents:
14
diff
changeset
|
523 |
0 | 524 def validate(self): |
525 if not self._productid: | |
526 raise ValidationError('A Product must have a ProductID') | |
527 if not self._name: | |
528 raise ValidationError('A Product must have a Name') | |
529 | |
530 def __str__(self): | |
531 return "%s (%s)" % (self._productid, self._name) | |
532 | |
533 | |
534 class CVRFRelationship(object): | |
535 TYPES = ('Default Component Of', 'Optional Component Of', | |
536 'External Component Of', 'Installed On', 'Installed With') | |
537 def __init__(self, productref, reltype, relatestoproductref): | |
538 self._productreference = productref | |
539 self._relationtype = reltype | |
540 self._relatestoproductreference = relatestoproductref | |
541 self._product = None | |
542 | |
543 def getParent(self): | |
544 """ All parent element of a FullProductName should implement that | |
545 method """ | |
546 return None | |
547 | |
548 def isOrphaned(self): | |
549 return self._product is None | |
550 | |
551 def validate(self): | |
552 if not self._productreference: | |
553 raise ValidationError('A Relationship must have a Product Reference') | |
554 if not self._relationtype: | |
555 raise ValidationError('A Relationship must have a Relation Type') | |
556 if self._relationtype not in self.TYPES: | |
557 raise ValidationError('Relation Type must be one of %s' % ', '.join(self.TYPES)) | |
558 if not self._relatestoproductreference: | |
559 raise ValidationError('A Relationship must have a "Relates To product Reference"') | |
560 if self._productreference == self._relatestoproductreference: | |
561 raise ValidationError('A Relationship cannot reference twice the same Product') | |
562 | |
563 | |
564 class CVRFGroup(object): | |
565 def __init__(self, groupid): | |
566 self._groupid = groupid | |
567 self._description = None | |
568 self._productids = [] | |
569 | |
570 def setDescription(self, description): | |
571 self._description = description | |
572 | |
573 def addProductID(self, productid): | |
574 self._productids.append(productid) | |
575 | |
576 def getTitle(self): | |
577 if self._description: | |
578 return "%s (%d products)" % (self._description, len(self._productids)) | |
579 return "#%s (%d products)" % (self._groupid, len(self._productids)) | |
580 | |
581 def validate(self): | |
582 if not self._groupid: | |
583 raise ValidationError('A Group must have a GroupID') | |
584 if not self._productids or len(self._productids) < 2: | |
585 raise ValidationError('A Group must contain at least two products') | |
586 | |
587 | |
588 class CVRFVulnerabilityID(object): | |
589 def __init__(self, systemname, value): | |
590 self._systemname = systemname | |
591 self._value = value | |
592 | |
593 def validate(self): | |
594 if not self._systemname: | |
595 raise ValidationError('A Vulnerability ID must have a System Name') | |
596 if not self._value: | |
597 raise ValidationError('A Vulnerability ID must have a value') | |
598 | |
599 | |
600 class CVRFVulnerability(object): | |
601 def __init__(self, ordinal): | |
602 self._ordinal = ordinal | |
603 self._title = None | |
604 self._id = None | |
605 self._notes = [] | |
606 self._discoverydate = None | |
607 self._releasedate = None | |
608 self._involvements = [] | |
609 self._cve = None | |
610 self._cwes = [] | |
611 self._productstatuses = [] | |
612 self._threats = [] | |
613 self._cvsss = [] | |
614 self._remediations = [] | |
615 self._references = [] | |
616 self._acknowledgments = [] | |
617 | |
618 def setTitle(self, title): | |
619 self._title = title | |
620 | |
621 def setID(self, _id): | |
622 self._id = _id | |
623 | |
624 def addNote(self, note): | |
625 self._notes.append(note) | |
626 | |
627 def setDiscoveryDate(self, date): | |
628 self._discoverydate = date | |
629 | |
630 def setReleaseDate(self, date): | |
631 self._releasedate = date | |
632 | |
633 def addInvolvement(self, involvement): | |
634 self._involvements.append(involvement) | |
635 | |
636 def setCVE(self, cve): | |
637 self._cve = cve | |
638 | |
639 def addCWE(self, cwe): | |
640 self._cwes.append(cwe) | |
641 | |
642 def addProductStatus(self, productstatus): | |
643 self._productstatuses.append(productstatus) | |
644 | |
645 def addThreat(self, threat): | |
646 self._threats.append(threat) | |
647 | |
648 def addCVSSSet(self, cvss_set): | |
649 self._cvsss.append(cvss_set) | |
650 | |
651 def addRemediation(self, remediation): | |
652 self._remediations.append(remediation) | |
653 | |
654 def addReference(self, ref): | |
655 self._references.append(ref) | |
656 | |
657 def addAcknowledgment(self, ack): | |
658 self._acknowledgments.append(ack) | |
659 | |
660 def getTitle(self): | |
661 """ return something that can be used as a title """ | |
662 if self._title: | |
663 if self._id: | |
664 return "%s (%s)" % (self._title, self._id._value) | |
665 return self._title | |
666 if self._id: | |
667 return self._id._value | |
668 return "#%d" % self._ordinal | |
669 | |
7
c924c15bd110
Add a method to get a Vulnerability Note per ordinal
Benoît Allard <benoit.allard@greenbone.net>
parents:
6
diff
changeset
|
670 def getNote(self, ordinal): |
c924c15bd110
Add a method to get a Vulnerability Note per ordinal
Benoît Allard <benoit.allard@greenbone.net>
parents:
6
diff
changeset
|
671 for note in self._notes: |
c924c15bd110
Add a method to get a Vulnerability Note per ordinal
Benoît Allard <benoit.allard@greenbone.net>
parents:
6
diff
changeset
|
672 if note._ordinal == ordinal: |
c924c15bd110
Add a method to get a Vulnerability Note per ordinal
Benoît Allard <benoit.allard@greenbone.net>
parents:
6
diff
changeset
|
673 return note |
c924c15bd110
Add a method to get a Vulnerability Note per ordinal
Benoît Allard <benoit.allard@greenbone.net>
parents:
6
diff
changeset
|
674 return None |
c924c15bd110
Add a method to get a Vulnerability Note per ordinal
Benoît Allard <benoit.allard@greenbone.net>
parents:
6
diff
changeset
|
675 |
17
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
676 def mentionsProdId(self, productid): |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
677 """ Returns in which sub element, self is mentioning the productid """ |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
678 for category in (self._productstatuses, self._threats, self._cvsss, self._remediations): |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
679 for subelem in category: |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
680 if productid in subelem._productids: |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
681 yield subelem |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
682 |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
683 def isMentioningProdId(self, productid): |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
684 """ Returns if self is mentioning the productid """ |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
685 for e in self.mentionsProdId(productid): |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
686 # We only need to know if the generator yield at least one elem. |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
687 return True |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
688 return False |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
689 |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
690 def mentionsGroupId(self, groupid): |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
691 for category in (self._threats, self._remediations): |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
692 for subelem in category: |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
693 if groupid in subelem._groupids: |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
694 yield subelem |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
695 |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
696 def isMentioningGroupId(self, groupids): |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
697 """ Make sure you call this with a list (not a generator or a tuple) |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
698 when wished """ |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
699 if not isinstance(groupids, list): |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
700 groupids = [groupids] |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
701 for groupid in groupids: |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
702 print "testing GroupId: ", groupid |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
703 for _ in self.mentionsGroupId(groupid): |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
704 # We only need to know if the generator yield at least one elem. |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
705 print 'True' |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
706 return True |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
707 print 'False' |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
708 return False |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
709 |
0 | 710 def validate(self, productids, groupids): |
711 if not self._ordinal: | |
712 raise ValidationError('A Vulnerability must have an ordinal') | |
713 if self._id is not None: | |
714 self._id.validate() | |
715 ordinals = set() | |
716 for note in self._notes: | |
717 note.validate() | |
718 if note._ordinal in ordinals: | |
719 raise ValidationError('Vulnerability Note Ordinal %d duplicated' % note._ordinal) | |
720 ordinals.add(note._ordinal) | |
721 for involvement in self._involvements: | |
722 involvement.validate() | |
723 for cwe in self._cwes: | |
724 cwe.validate() | |
725 for status in self._productstatuses: | |
726 status.validate(productids) | |
13
db2a02fff101
Improve validation
Benoît Allard <benoit.allard@greenbone.net>
parents:
8
diff
changeset
|
727 pids = set() |
db2a02fff101
Improve validation
Benoît Allard <benoit.allard@greenbone.net>
parents:
8
diff
changeset
|
728 for status in self._productstatuses: |
db2a02fff101
Improve validation
Benoît Allard <benoit.allard@greenbone.net>
parents:
8
diff
changeset
|
729 for pid in status._productids: |
db2a02fff101
Improve validation
Benoît Allard <benoit.allard@greenbone.net>
parents:
8
diff
changeset
|
730 if pid in pids: |
db2a02fff101
Improve validation
Benoît Allard <benoit.allard@greenbone.net>
parents:
8
diff
changeset
|
731 raise ValidationError('ProductID %s mentionned in two different ProductStatuses for Vulnerability %d' % (pid, self._ordinal)) |
db2a02fff101
Improve validation
Benoît Allard <benoit.allard@greenbone.net>
parents:
8
diff
changeset
|
732 pids.add(pid) |
0 | 733 for threat in self._threats: |
734 threat.validate(productids, groupids) | |
735 for cvss in self._cvsss: | |
736 cvss.validate(productids) | |
14
640b88744523
Fix issue in validation of CVSS Score Sets
Benoît Allard <benoit.allard@greenbone.net>
parents:
13
diff
changeset
|
737 pids = set() |
13
db2a02fff101
Improve validation
Benoît Allard <benoit.allard@greenbone.net>
parents:
8
diff
changeset
|
738 for cvss in self._cvsss: |
db2a02fff101
Improve validation
Benoît Allard <benoit.allard@greenbone.net>
parents:
8
diff
changeset
|
739 for pid in (cvss._productids or productids): |
db2a02fff101
Improve validation
Benoît Allard <benoit.allard@greenbone.net>
parents:
8
diff
changeset
|
740 if pid in pids: |
db2a02fff101
Improve validation
Benoît Allard <benoit.allard@greenbone.net>
parents:
8
diff
changeset
|
741 raise ValidationError('ProductID %s mentionned in two different CVSS Score Sets for Vulnerability %d' % (pid, self._ordinal)) |
db2a02fff101
Improve validation
Benoît Allard <benoit.allard@greenbone.net>
parents:
8
diff
changeset
|
742 pids.add(pid) |
0 | 743 for remediation in self._remediations: |
744 remediation.validate(productids, groupids) | |
745 for reference in self._references: | |
746 reference.validate() | |
747 for acknowledgment in self._acknowledgments: | |
748 acknowledgment.validate() | |
749 | |
750 | |
751 class CVRFInvolvement(object): | |
752 PARTIES = CVRFPublisher.TYPES | |
753 STATUSES = ('Open', 'Disputed', 'In Progress', 'Completed', | |
754 'Contact Attempted', 'Not Contacted') | |
755 def __init__(self, party, status): | |
756 self._party = party | |
757 self._status = status | |
758 self._description = None | |
759 | |
760 def setDescription(self, description): | |
761 self._description = description | |
762 | |
763 def getTitle(self): | |
764 return "From %s: %s" % (self._party, self._status) | |
765 | |
766 def validate(self): | |
767 if not self._party: | |
768 raise ValidationError('An Involvement must have a Party') | |
769 if self._party not in self.PARTIES: | |
770 raise ValidationError("An Involvement's Party must be one of %s" % ', '.join(self.PARTIES)) | |
771 if not self._status: | |
772 raise ValidationError('An Involvement must have a Status') | |
773 if self._status not in self.STATUSES: | |
774 raise ValidationError("An Involvement's Status must be one of %s" % ', '.join(self.STATUSES)) | |
775 | |
776 | |
777 class CVRFCWE(object): | |
778 def __init__(self, _id, value): | |
779 self._id = _id | |
780 self._value = value | |
781 | |
782 def validate(self): | |
783 if not self._id: | |
784 raise ValidationError('A CWE must have an ID') | |
785 if not self._value: | |
786 raise ValidationError('A CWE must have a description') | |
787 | |
788 | |
789 class CVRFProductStatus(object): | |
790 TYPES = ('First Affected', 'Known Affected', 'Known Not Affected', | |
791 'First Fixed', 'Fixed', 'Recommended', 'Last Affected') | |
17
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
792 NAME = "Product Status" |
0 | 793 def __init__(self, _type): |
794 self._type = _type | |
795 self._productids = [] | |
796 | |
797 def addProductID(self, productid): | |
798 self._productids.append(productid) | |
799 | |
800 def getTitle(self): | |
801 return "%s: %d products" % (self._type, len(self._productids)) | |
802 | |
803 def validate(self, productids): | |
804 if not self._type: | |
805 raise ValidationError('A Product Status must have a Type') | |
806 if self._type not in self.TYPES: | |
807 raise ValidationError("A Product Status' Type must be one of %s" % ', '.join(self.TYPES)) | |
808 if len(self._productids) < 1: | |
809 raise ValidationError('A Product Status must mention at least one Product') | |
810 for productid in self._productids: | |
811 if productid not in productids: | |
812 raise ValidationError('Unknown ProductID: %s' % productid) | |
813 | |
814 | |
815 class CVRFThreat(object): | |
816 TYPES = ('Impact', 'Exploit Status', 'Target Set') | |
17
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
817 NAME = "Threat" |
0 | 818 def __init__(self, _type, description): |
819 self._type = _type | |
820 self._description = description | |
821 self._date = None | |
822 self._productids = [] | |
823 self._groupids = [] | |
824 | |
825 def setDate(self, date): | |
826 self._date = date | |
827 | |
828 def addProductID(self, productid): | |
829 self._productids.append(productid) | |
830 | |
831 def addGroupID(self, groupid): | |
832 self._groupids.append(groupid) | |
833 | |
834 def getTitle(self): | |
835 return self._type | |
836 | |
837 def validate(self, productids, groupids): | |
838 if not self._type: | |
839 raise ValidationError('A Threat must have a Type') | |
840 if self._type not in self.TYPES: | |
841 raise ValidationError("A Threat's Type must be one of %s" % ', '.join(self.TYPES)) | |
842 if not self._description: | |
843 raise ValidationError('A Threat must have a Description') | |
844 for productid in self._productids: | |
845 if productid not in productids: | |
846 raise ValidationError('Unknown ProductID: %s' % productid) | |
847 for groupid in self._groupids: | |
848 if groupid not in groupids: | |
849 raise ValidationError('Unknown GroupID: %s' % groupid) | |
850 | |
851 | |
852 class CVRFCVSSSet(object): | |
853 # To determine the base Score | |
854 VALUES = {'AV': {'L':0.395, 'A':0.646, 'N':1.0}, | |
855 'AC': {'H':0.35, 'M':0.61 ,'L':0.71}, | |
856 'Au': {'M':0.45, 'S':0.56, 'N':0.704}, | |
857 'C': {'N':0.0, 'P':0.275, 'C':0.66}, | |
858 'I': {'N':0.0, 'P':0.275, 'C':0.66}, | |
859 'A': {'N':0.0, 'P':0.275, 'C':0.66}} | |
17
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
860 NAME = "CVSS Score Set" |
0 | 861 def __init__(self, basescore): |
862 self._basescore = basescore | |
863 self._temporalscore = None | |
864 self._environmentalscore = None | |
865 self._vector = None | |
866 self.vector = None | |
867 self._productids = [] | |
868 | |
869 def setTemporalScore(self, tempscore): | |
870 self._temporalscore = tempscore | |
871 | |
872 def setEnvironmentalScore(self, envscore): | |
873 self._environmentalscore = envscore | |
874 | |
875 def setVector(self, vector): | |
876 self._vector = vector | |
877 if vector is None: | |
878 self.vector = vector | |
879 return | |
880 try: | |
881 self.vector = {} | |
882 for component in vector[:26].split('/'): | |
883 name, value = component.split(':') | |
884 self.vector[name] = self.VALUES[name][value] | |
885 except (KeyError, ValueError): | |
886 self.vector = None | |
887 | |
888 def addProductID(self, productid): | |
889 self._productids.append(productid) | |
890 | |
891 def baseScore(self): | |
892 v = self.vector # make an alias for shorter lines | |
893 exploitability = 20 * v['AV'] * v['AC'] * v['Au'] | |
894 impact = 10.41 * (1 - (1 - v['C']) * (1 - v['I']) * (1 - v['A'])) | |
895 def f(i): return 0 if i == 0 else 1.176 | |
896 return ((0.6 * impact) + (0.4 * exploitability) - 1.5) * f(impact) | |
897 | |
898 def validate(self, productids): | |
899 if not self._basescore: | |
900 raise ValidationError('A CVSS Score Set must have a Base Score') | |
901 if self._vector and not self.vector: | |
902 raise ValidationError('Syntax Error in CVSS Vector') | |
903 if abs(self._basescore - self.baseScore()) >= 0.05: | |
904 raise ValidationError('Inconsistency in CVSS Score Set between Vector (%f) and Base Score (%f)' % (self.baseScore(), self._basescore)) | |
905 for productid in self._productids: | |
906 if productid not in productids: | |
907 raise ValidationError('Unknown ProductID: %s' % productid) | |
908 | |
909 | |
910 class CVRFRemediation(object): | |
911 TYPES = ('Workaround', 'Mitigation', 'Vendor Fix', 'None Available', | |
912 'Will Not Fix') | |
17
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
913 NAME = "Remediation" |
0 | 914 def __init__(self, _type, description): |
915 self._type = _type | |
916 self._description = description | |
917 self._date = None | |
918 self._entitlement = None | |
919 self._url = None | |
920 self._productids = [] | |
921 self._groupids = [] | |
922 | |
923 def setDate(self, date): | |
924 self._date = date | |
925 | |
926 def setEntitlement(self, entitlement): | |
927 self._entitlement = entitlement | |
928 | |
929 def setURL(self, url): | |
930 self._url = url | |
931 | |
932 def addProductID(self, productid): | |
933 self._productids.append(productid) | |
934 | |
935 def addGroupID(self, groupid): | |
936 self._groupids.append(groupid) | |
937 | |
938 def getTitle(self): | |
939 return self._type | |
940 | |
941 def validate(self, productids, groupids): | |
942 if not self._type: | |
943 raise ValidationError('A Remediation must have a Type') | |
944 if self._type not in self.TYPES: | |
945 raise ValidationError("A Remediation's Type must be one of %s" % ', '.join(self.TYPES)) | |
946 if not self._description: | |
947 raise ValidationError('A Remediation must have a Description') | |
948 for productid in self._productids: | |
949 if productid not in productids: | |
950 raise ValidationError('Unknown ProductID: %s' % productid) | |
951 for groupid in self._groupids: | |
952 if groupid not in groupids: | |
953 raise ValidationError('Unknown GroupID: %s' % groupid) | |
954 | |
955 class CVRF(object): | |
956 def __init__(self, title, _type): | |
957 self._title = title | |
958 self._type = _type | |
959 self._publisher = None | |
960 self._tracking = None | |
961 self._notes = [] | |
962 self._distribution = None | |
1
d47e1164740f
Add support for AggregateSeverity
Benoît Allard <benoit.allard@greenbone.net>
parents:
0
diff
changeset
|
963 self._aggregateseverity = None |
0 | 964 self._references = [] |
965 self._acknowledgments = [] | |
966 self._producttree = None | |
967 self._vulnerabilities = [] | |
968 | |
969 def setPublisher(self, publisher): | |
970 self._publisher = publisher | |
971 | |
972 def setTracking(self, tracking): | |
973 self._tracking = tracking | |
974 | |
975 def addNote(self, note): | |
976 self._notes.append(note) | |
977 | |
978 def setDistribution(self, distribution): | |
979 self._distribution = distribution | |
980 | |
1
d47e1164740f
Add support for AggregateSeverity
Benoît Allard <benoit.allard@greenbone.net>
parents:
0
diff
changeset
|
981 def setAggregateSeverity(self, aggregateseverity): |
d47e1164740f
Add support for AggregateSeverity
Benoît Allard <benoit.allard@greenbone.net>
parents:
0
diff
changeset
|
982 self._aggregateseverity = aggregateseverity |
d47e1164740f
Add support for AggregateSeverity
Benoît Allard <benoit.allard@greenbone.net>
parents:
0
diff
changeset
|
983 |
0 | 984 def addReference(self, ref): |
985 self._references.append(ref) | |
986 | |
987 def addAcknowledgment(self, ack): | |
988 self._acknowledgments.append(ack) | |
989 | |
990 def createProductTree(self): | |
991 """ only done if the element is there """ | |
992 self._producttree = CVRFProductTree() | |
993 return self._producttree | |
994 | |
995 def addVulnerability(self, vuln): | |
996 self._vulnerabilities.append(vuln) | |
997 | |
998 def getProductForID(self, productid): | |
999 if self._producttree is None: | |
1000 raise ValueError('No ProductTree') | |
1001 return self._producttree.getProductForID(productid) | |
1002 | |
1003 def getGroupForID(self, groupid): | |
1004 if self._producttree is None: | |
1005 raise ValueError('No ProductTree') | |
1006 return self._producttree.getGroupForID(groupid) | |
1007 | |
1008 def getHighestCVSS(self): | |
1009 highestBaseScore = 0 | |
1010 highest = None | |
1011 for vulnerability in self._vulnerabilities: | |
1012 for cvss in vulnerability._cvsss: | |
1013 if cvss._basescore <= highestBaseScore: | |
1014 continue | |
1015 highestBaseScore = cvss._basescore | |
1016 highest = cvss | |
1017 return highest | |
1018 | |
1019 def getProductList(self, type_='Fixed'): | |
1020 products = set() | |
1021 if type_ == 'Fixed': | |
1022 # First try through the Remediation | |
1023 for vulnerability in self._vulnerabilities: | |
1024 for remediation in vulnerability._remediations: | |
1025 if remediation._type != 'Vendor Fix': | |
1026 continue | |
1027 for productid in remediation._productids: | |
1028 products.add(productid) | |
1029 for groupid in remediation._groupids: | |
1030 for productid in self.getGroupForID(groupid)._productids: | |
1031 products.add(productid) | |
1032 if not products: | |
1033 # If nothing there, try through the productstatuses | |
1034 for vulnerability in self._vulnerabilities: | |
1035 for status in vulnerability._productstatuses: | |
1036 if status._type != type_: | |
1037 continue | |
1038 for productid in status._productids: | |
1039 products.add(productid) | |
1040 return set(self.getProductForID(p) for p in products) | |
1041 | |
17
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
1042 def isProductOrphan(self, productid): |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
1043 """ Returns if a productid is mentionned nowhere in the document """ |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
1044 # We first look at the ProductTree |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
1045 ptree = self._producttree |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
1046 for relation in ptree._relationships: |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
1047 if productid == relation._productreference: |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
1048 return False |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
1049 if productid == relation._relatestoproductreference: |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
1050 return False |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
1051 groupids = [g._groupid for g in ptree._groups if productid in g._productids] |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
1052 if len(groupids) > 0: |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
1053 return False |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
1054 # Go through all the Vulnerabilities |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
1055 for vulnerability in self._vulnerabilities: |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
1056 if vulnerability.isMentioningProdId(productid): |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
1057 return False |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
1058 for groupid in groupids: |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
1059 if vulnerability.isMentioningGroupId(groupid): |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
1060 return False |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
1061 return True |
90852c11fabd
Add methods to extract Product references in a document.
Benoît Allard <benoit.allard@greenbone.net>
parents:
15
diff
changeset
|
1062 |
0 | 1063 def getNote(self, ordinal): |
1064 for note in self._notes: | |
1065 if note._ordinal == ordinal: | |
1066 return note | |
1067 return None | |
1068 | |
6
633ebfcff0d0
Add a method to request a Document ID
Benoît Allard <benoit.allard@greenbone.net>
parents:
1
diff
changeset
|
1069 def getDocId(self): |
633ebfcff0d0
Add a method to request a Document ID
Benoît Allard <benoit.allard@greenbone.net>
parents:
1
diff
changeset
|
1070 if self._tracking is not None: |
633ebfcff0d0
Add a method to request a Document ID
Benoît Allard <benoit.allard@greenbone.net>
parents:
1
diff
changeset
|
1071 return self._tracking.getId() |
633ebfcff0d0
Add a method to request a Document ID
Benoît Allard <benoit.allard@greenbone.net>
parents:
1
diff
changeset
|
1072 # Make up something ... |
633ebfcff0d0
Add a method to request a Document ID
Benoît Allard <benoit.allard@greenbone.net>
parents:
1
diff
changeset
|
1073 return self._title.lower() |
633ebfcff0d0
Add a method to request a Document ID
Benoît Allard <benoit.allard@greenbone.net>
parents:
1
diff
changeset
|
1074 |
0 | 1075 def validate(self): |
1076 if not self._title: | |
1077 raise ValidationError('Document Title cannot be empty') | |
1078 if not self._type: | |
1079 raise ValidationError('Document Type cannot be empty') | |
1080 if self._publisher is None: | |
1081 raise ValidationError('Document Publisher needs to be set') | |
1082 self._publisher.validate() | |
1083 if self._tracking is None: | |
1084 raise ValidationError('Document Tracking needs to be set') | |
1085 self._tracking.validate() | |
1086 ordinals = set() | |
1087 for note in self._notes: | |
1088 note.validate() | |
1089 if note._ordinal in ordinals: | |
1090 raise ValidationError('Document Note ordinal %d is issued twice' % note._ordinal) | |
1091 ordinals.add(note._ordinal) | |
1092 for reference in self._references: | |
1093 reference.validate() | |
1094 for acknowledgment in self._acknowledgments: | |
1095 acknowledgment.validate() | |
1096 productids = set() | |
1097 groupids = set() | |
1098 if self._producttree: | |
1099 productids, groupids = self._producttree.validate() | |
1100 ordinals = set() | |
1101 for vulnerability in self._vulnerabilities: | |
1102 vulnerability.validate(productids, groupids) | |
1103 if vulnerability._ordinal in ordinals: | |
1104 raise ValidationError('Vulnerability ordinal %d is issued twice' % vulnerability._ordinal) | |
1105 ordinals.add(vulnerability._ordinal) | |
1106 | |
1107 def __str__(self): | |
1108 s = [ | |
1109 'Title: %s' % self._title, | |
1110 'Type: %s' % self._type, | |
1111 'Publisher: %s' % self._publisher, | |
1112 'tracking: %s' % self._tracking, | |
1113 '%d Notes: %s' % (len(self._notes), ', '.join( | |
1114 str(n) for n in self._notes)) | |
1115 ] | |
1116 if self._distribution is not None: | |
1117 s.append('Distribution: %s' % self._distribution) | |
1118 s.extend([ | |
1119 '%d Acknowledgments' % len(self._acknowledgments), | |
1120 'Products: %s' % self._producttree, | |
1121 ]) | |
1122 return '\n'.join(s) |