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