changeset 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 3cab052872f4
children 934c510f8077
files CHANGES farolluz/common.py farolluz/cvrf.py farolluz/document.py farolluz/producttree.py farolluz/renderer.py farolluz/vulnerability.py
diffstat 7 files changed, 1250 insertions(+), 1128 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES	Fri Oct 24 16:43:31 2014 +0200
+++ b/CHANGES	Fri Oct 24 17:01:26 2014 +0200
@@ -5,6 +5,7 @@
 ----------------------------------
 * Add tests
 * Add method to rename productIDs in the whole document
+* Split the big cvrf.py file into smaller ones
 
 FarolLuz 0.1.1 (2014-10-17)
 ===========================
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/farolluz/common.py	Fri Oct 24 17:01:26 2014 +0200
@@ -0,0 +1,93 @@
+# -*- coding: utf-8 -*-
+#
+# Authors:
+# Benoît Allard <benoit.allard@greenbone.net>
+#
+# Copyright:
+# Copyright (C) 2014 Greenbone Networks GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+
+"""\
+Common Objects related to CVRF Documents
+"""
+
+class ValidationError(Exception): pass
+
+
+class CVRFNote(object):
+    TYPES = ('General', 'Details', 'Description', 'Summary', 'FAQ',
+             'Legal Disclaimer', 'Other')
+    def __init__(self, _type, ordinal, note, title=None, audience=None):
+        self._type = _type
+        self._ordinal = ordinal
+        self._note = note
+        self._title = title
+        self._audience = audience
+
+    def getTitle(self):
+        """ returns something that can be used as a title """
+        if self._title is None:
+            return "%s (#%d)" % (self._type, self._ordinal)
+        return "%s (%s)" % (self._title, self._type)
+
+    def validate(self):
+        if not self._type:
+            raise ValidationError('A Note needs to have a Type set')
+        if self._type not in self.TYPES:
+            raise ValidationError('A Note Type needs to be one of %s' % ', '.join(self.TYPES))
+        if self._ordinal < 0:
+            raise ValidationError('A Note ordinal must be a positive integer')
+        if not self._note:
+            raise ValidationError('A Note must contain some text')
+
+
+    def __str__(self):
+        return self._note
+
+
+class CVRFReference(object):
+    TYPES = ('Self', 'External')
+    def __init__(self, url, description, _type=None):
+        self._url = url
+        self._description = description
+        self._type = _type
+
+    def validate(self):
+        if (self._type is not None) and (self._type not in self.TYPES):
+            raise ValidationError('If a Reference type is set, it mist be one of %s' % ', '.join(self.TYPES))
+        if not self._url:
+            raise ValidationError('A Reference must contain an URL')
+        if not self._description:
+            raise ValidationError('A Reference must contain a description')
+
+
+class CVRFAcknowledgment(object):
+    def __init__(self, names=[], organizations=[], description=None,
+                 url=None):
+        self._names = names
+        self._organizations = organizations
+        self._description = description
+        self._url = url
+
+    def getTitle(self):
+        return "%s - %s" % (', '.join(self._names),
+                            ', '.join(self._organizations))
+
+    def validate(self):
+        if (not self._names) and (not self._organizations) and (not self._description):
+            raise ValidationError('An Acknowledgment must have at least a Name, an Organization or a Description')
+
+
--- a/farolluz/cvrf.py	Fri Oct 24 16:43:31 2014 +0200
+++ b/farolluz/cvrf.py	Fri Oct 24 17:01:26 2014 +0200
@@ -21,1132 +21,16 @@
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 
 """\
-Objects related to CVRF Documents
+This is the public interface to the CVRF Elements
 """
 
-class ValidationError(Exception): pass
-
-class CVRFPublisher(object):
-    TYPES = ('Vendor', 'Discoverer', 'Coordinator', 'User', 'Other')
-    def __init__(self, _type, vendorid=None):
-        self._type = _type
-        self._vendorid = vendorid
-        self._contact = None
-        self._authority = None
-
-    def setContact(self, contact):
-        self._contact = contact
-
-    def setAuthority(self, authority):
-        self._authority = authority
-
-    def validate(self):
-        if not self._type:
-            raise ValidationError('Document Publisher needs to have a type')
-        if self._type not in self.TYPES:
-            raise ValidationError('Document Publisher Type needs to be one of %s' % ', '.join(self.TYPES))
-
-    def __str__(self):
-        s = 'CVRFPublisher: %s' % self._type
-        if self._vendorid is not None:
-            s += ' ID: %s' % self._vendorid
-        if self._contact is not None:
-            s += ' Contact: "%s"' % self._contact
-        if self._authority is not None:
-            s += ' Authority: "%s"' % self._authority
-        return s
-
-
-class CVRFTrackingID(object):
-    def __init__(self, _id):
-        self._id = _id
-        self._aliases = []
-
-    def addAlias(self, alias):
-        self._aliases.append(alias)
-
-    def getId(self):
-        return self._id
-
-    def validate(self):
-        if not self._id:
-            raise ValidationError('Document ID cannot be left empty')
-
-    def __str__(self):
-        if self._aliases:
-            return "%s (%s)" % (self._id, ', '.join(self._aliases))
-        return self._id
-
-
-class CVRFTracking(object):
-    STATUSES = ('Draft', 'Interim', 'Final')
-    def __init__(self, _id, status, version, initial, current):
-        self._identification = _id
-        self._status = status
-        self._version = version
-        self._history = []
-        self._initialDate = initial
-        self._currentDate = current
-        self._generator = None
-
-    def addRevision(self, revision):
-        self._history.append(revision)
-
-    def setGenerator(self, generator):
-        self._generator = generator
-
-    def getId(self):
-        return self._identification.getId()
-
-    def validate(self):
-        if self._identification is None:
-            raise ValidationError('Document Tracking needs to have an Identification')
-        self._identification.validate()
-        if not self._status:
-            raise ValidationError('Document status must be set')
-        if self._status not in self.STATUSES:
-            raise ValidationError('Document Status must be one of %s' % ', '.join(self.STATUSES))
-        if not self._version:
-            raise ValidationError('Document Version must be set')
-        if len(self._version) > 4:
-            raise ValidationError('Document Version must be comprised between `nn` and `nn.nn.nn.nn`')
-        if not self._history:
-            raise ValidationError('Document must have at least a revision')
-        if not self._initialDate:
-            raise ValidationError('Document must have an initial Release date set')
-        prev_date = self._initialDate
-        if self._history[0]._date < self._initialDate:
-            # Documents could have revisions before being released
-            prev_date = self._history[0]._date
-        prev = ()
-        for revision in self._history:
-            revision.validate()
-            if revision._number <= prev:
-                raise ValidationError('Revision numbers must always be increasing')
-            if revision._date < prev_date:
-                raise ValidationError('Revision dates must always be increasing')
-            prev = revision._number
-            prev_date = revision._date
-        if not self._currentDate:
-            raise ValidationError('Document must have a Current Release Date set')
-        if self._currentDate != self._history[-1]._date:
-            raise ValidationError('Current Release Date must be the same as the Date from the last Revision')
-        if self._initialDate > self._currentDate:
-            raise ValidationError('Initial date must not be after current Date')
-        if self._version != self._history[-1]._number:
-            raise ValidationError('Document version must be the same as the number of the last Revision')
-
-    def __str__(self):
-        s = "ID: %s" % self._identification
-        s += " Status: %s" % self._status
-        s += " v%s" % '.'.join('%d' % i for i in self._version)
-        s += " %d revisions" % len(self._history)
-        s += " Initial release: %s" % self._initialDate.isoformat()
-        return s
-
-
-class CVRFRevision(object):
-    def __init__(self, number, date, description):
-        self._number = number
-        self._date = date
-        self._description = description
-
-    def validate(self):
-        if not self._number:
-            raise ValidationError('A Revision must have a Number')
-        if not self._date:
-            raise ValidationError('A Revision must have a Date')
-        if not self._description:
-            raise ValidationError('A Revision must have a Description')
-
-class CVRFGenerator(object):
-    def __init__(self):
-        self._engine = None
-        self._date = None
-
-    def setEngine(self, engine):
-        self._engine = engine
-
-    def setDate(self, date):
-        self._date = date
-
-    def validate(self):
-        if (not self._engine) and (not self._date):
-            raise ValidationError('The Generator must have at least an Engine or a Date')
-
-
-class CVRFNote(object):
-    TYPES = ('General', 'Details', 'Description', 'Summary', 'FAQ',
-             'Legal Disclaimer', 'Other')
-    def __init__(self, _type, ordinal, note, title=None, audience=None):
-        self._type = _type
-        self._ordinal = ordinal
-        self._note = note
-        self._title = title
-        self._audience = audience
-
-    def getTitle(self):
-        """ returns something that can be used as a title """
-        if self._title is None:
-            return "%s (#%d)" % (self._type, self._ordinal)
-        return "%s (%s)" % (self._title, self._type)
-
-    def validate(self):
-        if not self._type:
-            raise ValidationError('A Note needs to have a Type set')
-        if self._type not in self.TYPES:
-            raise ValidationError('A Note Type needs to be one of %s' % ', '.join(self.TYPES))
-        if self._ordinal < 0:
-            raise ValidationError('A Note ordinal must be a positive integer')
-        if not self._note:
-            raise ValidationError('A Note must contain some text')
-
-
-    def __str__(self):
-        return self._note
-
-
-class CVRFAggregateSeverity(object):
-    def __init__(self, severity):
-        self._severity = severity
-        self._namespace = None
-
-    def setNamespace(self, namespace):
-        self._namespace = namespace
-
-class CVRFReference(object):
-    TYPES = ('Self', 'External')
-    def __init__(self, url, description, _type=None):
-        self._url = url
-        self._description = description
-        self._type = _type
-
-    def validate(self):
-        if (self._type is not None) and (self._type not in self.TYPES):
-            raise ValidationError('If a Reference type is set, it mist be one of %s' % ', '.join(self.TYPES))
-        if not self._url:
-            raise ValidationError('A Reference must contain an URL')
-        if not self._description:
-            raise ValidationError('A Reference must contain a description')
-
-
-class CVRFAcknowledgment(object):
-    def __init__(self, names=[], organizations=[], description=None,
-                 url=None):
-        self._names = names
-        self._organizations = organizations
-        self._description = description
-        self._url = url
-
-    def getTitle(self):
-        return "%s - %s" % (', '.join(self._names),
-                            ', '.join(self._organizations))
-
-    def validate(self):
-        if (not self._names) and (not self._organizations) and (not self._description):
-            raise ValidationError('An Acknowledgment must have at least a Name, an Organization or a Description')
-
-
-class CVRFProductTree(object):
-    def __init__(self):
-        # All the branches, they can be order with their `parent` attribute
-        self._branches = []
-        self._groups = []
-        self._relationships = []
-        self._products = []
-        self._groups = []
-
-    def addProduct(self, product):
-        """ Add to the product list """
-        self._products.append(product)
-
-    def addRelationship(self, rel):
-        self._relationships.append(rel)
-
-    def addGroup(self, group):
-        self._groups.append(group)
-
-    def getProductForID(self, productid):
-        for product in self._products:
-            if product._productid == productid:
-                return product
-        raise KeyError(productid)
-
-    def getGroupForID(self, groupid):
-        for group in self._groups:
-            if group._groupid == groupid:
-                return group
-        raise KeyError(groupid)
-
-    def decomposeProduct(self, productid):
-        """ In case of product defined as a relationship (product X installed
-        on OS Y), this gives us the following tuple: (OS, product). """
-        product = self.getProductForID(productid)
-        parent = product._parent
-        if parent is None:
-            return (None, None)
-        if not isinstance(parent, CVRFRelationship):
-            return (None, None)
-        relationtype = parent._relationtype.replace(' ', '').lower()
-        if relationtype not in ('defaultcomponentof', 'installedon'):
-            return (None, None)
-        return (
-            self.getProductForID(parent._relatestoproductreference),
-            self.getProductForID(parent._productreference)
-        )
-
-    def getBranch(self, path):
-        if len(path) == 0:
-            return self
-        branches = self._branches
-        node = None
-        for idx in path:
-            node = branches[idx]
-            branches = node._childs
-        return node
-
-    def getBranches(self):
-        for branch in self._branches:
-            yield branch
-            for sub_branch in branch.getBranches():
-                yield sub_branch
-
-    def getPath(self):
-        return ()
-
-    def getNameOfRelationship(self, relationship):
-        if relationship is None:
-            return ''
-        return ' '.join((self.getProductForID(relationship._productreference)._name, 'as',
-                relationship._relationtype.lower(),
-                self.getProductForID(relationship._relatestoproductreference)._name))
-
-    def getOrphanedBranches(self, product=None):
-        """ The branches that could accept `product` as Product Definition """
-        white_list = []
-        if product is not None:
-            white_list = [product._parent]
-        for branch in self.getBranches():
-            if (branch in white_list) or branch.isOrphaned():
-                yield branch
-
-    def getNotTerminalBranches(self, b2=None):
-        """\
-        The branches that could accept `b2` as new sub-branches
-        Note that b2 and all its sub-branches cannot be listed
-        """
-        black_list = []
-        if b2 is not None:
-            black_list = [b2] + list(b2.getBranches())
-        for branch in self.getBranches():
-            if branch in black_list:
-                continue
-            if branch._product is None:
-                yield branch
-
-    def getOrphanedRelationships(self, product=None):
-        """ The relationships that need a product defninition """
-        white_list = []
-        if product is not None:
-            white_list = [product.getCurrentRelationship()]
-        for i, relationship in enumerate(self._relationships):
-            if (relationship in white_list) or relationship.isOrphaned():
-                yield (i, relationship)
-
-    def nbProducts(self):
-        """ Amount of 'raw' Products """
-        return len([p for p in self._products if p._parent is self])
-
-    def validate(self):
-        for branch in self._branches:
-            branch.validate()
-        productids = set()
-        for product in self._products:
-            product.validate()
-            if product._productid in productids:
-                raise ValidationError('Each ProductID must be unique (%s)' % product._productid)
-            productids.add(product._productid)
-        for relationship in self._relationships:
-            relationship.validate()
-            for productid in (relationship._productreference,
-                              relationship._relatestoproductreference):
-                if productid not in productids:
-                    raise ValidationError('ProductID %s is unknown' % productid)
-        groupids = set()
-        for group in self._groups:
-            group.validate()
-            if group._groupid in groupids:
-                raise ValidationError('Duplicated GroupID: %s' % group._groupid)
-            groupids.add(group._groupid)
-            for productid in group._productids:
-                if productid not in productids:
-                    raise ValidationError('ProductID %s is unknown' % productid)
-        return productids, groupids
-
-    def __str__(self):
-        return 'Products: %s' % '\n'.join(str(p) for p in self._products)
-
-
-class CVRFProductBranch(object):
-    TYPES = ('Vendor', 'Product Family', 'Product Name', 'Product Version',
-             'Patch Level', 'Service Pack', 'Architecture', 'Language',
-             'Legacy', 'Specification')
-    def __init__(self, _type, name, parentbranch):
-        self._type = _type
-        self._name = name
-        self._childs = []
-        self._product = None
-        self.link(parentbranch)
-
-    def getParent(self):
-        return self._parentbranch
-
-    def getPath(self, string=False):
-        """ return the path to that branch element as a tuple """
-        if self.isRoot():
-            for i, b in enumerate(self._parentbranch._branches):
-                if b is self:
-                    if string:
-                        return '%d' % i
-                    return (i, )
-        else:
-            for i, b in enumerate(self._parentbranch._childs):
-                if b is self:
-                    if string:
-                        return '/'.join([self._parentbranch.getPath(string), '%d' % i])
-                    return self._parentbranch.getPath(string) + (i,)
-        if string:
-            return ''
-        return ()
-
-    def getTree(self):
-        """ this returns a list of tuples (type, name) leading to here"""
-        if self.isRoot():
-            return [(self._type, self._name)]
-        return self._parentbranch.getTree() + [(self._type, self._name)]
-
-    def getName(self):
-        return ' / '.join("%s: %s" % (type_, name) for type_, name in self.getTree())
-
-    def getParentPath(self):
-        """ return as string the path to the parent """
-        return '/'.join('%s' % p for p in self.getPath()[:-1])
-
-    def isRoot(self):
-        return isinstance(self._parentbranch, CVRFProductTree)
-
-    def isOrphaned(self):
-        """ Has no childs and no product """
-        return len(self._childs) == 0 and (self._product is None)
-
-    def getBranches(self):
-        for branch in self._childs:
-            yield branch
-            for sub_branch in branch.getBranches():
-                yield sub_branch
-
-    def unlink(self):
-        """ Unset our _parent, and remove us from the _parent._childs """
-        if self.isRoot():
-            self.getParent()._branches.remove(self)
-        else:
-            self.getParent()._childs.remove(self)
-        self._parentbranch = None
-
-    def link(self, parent):
-        """ Actually, only set the parent """
-        self._parentbranch = parent
-        if self.isRoot():
-            parent._branches.append(self)
-        else:
-            parent._childs.append(self)
-
-
-    def validate(self):
-        if not self._type:
-            raise ValidationError('A Branch must have a Type')
-        if self._type not in self.TYPES:
-            raise ValidationError('A Branch Type must be one of %s' % ', '.join(self.TYPES))
-        if not self._name:
-            raise ValidationError('A Branch must have a Name')
-        for branch in self._childs:
-            branch.validate()
-        if self.isOrphaned():
-            raise ValidationError('A Branch must have at least a sub-product or sub-branches')
-
-    def __str__(self):
-        return "%s: %s" % (self._type, self._name)
-
-
-class CVRFFullProductName(object):
-    def __init__(self, productid, name, parent, cpe=None):
-        self._productid = productid
-        self._name = name
-        self._cpe = cpe
-        # Can be None (directly under the tree), a ProductBranch, or a
-        # Relationship
-        self.link(parent)
-
-    def isRoot(self):
-        return isinstance(self._parent, CVRFProductTree)
-
-    def isRelationship(self):
-        return isinstance(self._parent, CVRFRelationship)
-
-    def getTree(self):
-        if not isinstance(self._parent, CVRFProductBranch):
-            return []
-        return self._parent.getTree()
-
-    def getParentPath(self):
-        if self.isRoot() or self.isRelationship():
-            return ''
-        return self._parent.getPath(True)
-
-    def getCurrentRelationship(self):
-        if self.isRelationship():
-            return self._parent
-        return None
-
-    def unlink(self):
-        """ Unset our _parent, and remove us from the _parent._childs
-        We are still in the product list.
-        """
-        if not self.isRoot():
-            self._parent._product = None
-        self._parent = None
-
-    def link(self, parent):
-        self._parent = parent
-        if not self.isRoot():
-            parent._product = self
-
-    def validate(self):
-        if not self._productid:
-            raise ValidationError('A Product must have a ProductID')
-        if not self._name:
-            raise ValidationError('A Product must have a Name')
-
-    def __str__(self):
-        return "%s (%s)" % (self._productid, self._name)
-
-
-class CVRFRelationship(object):
-    TYPES = ('Default Component Of', 'Optional Component Of',
-             'External Component Of', 'Installed On', 'Installed With')
-    def __init__(self, productref, reltype, relatestoproductref):
-        self._productreference = productref
-        self._relationtype = reltype
-        self._relatestoproductreference = relatestoproductref
-        self._product = None
-
-    def getParent(self):
-        """ All parent element of a FullProductName should implement that
-        method """
-        return None
-
-    def isOrphaned(self):
-        return self._product is None
-
-    def validate(self):
-        if not self._productreference:
-            raise ValidationError('A Relationship must have a Product Reference')
-        if not self._relationtype:
-            raise ValidationError('A Relationship must have a Relation Type')
-        if self._relationtype not in self.TYPES:
-            raise ValidationError('Relation Type must be one of %s' % ', '.join(self.TYPES))
-        if not self._relatestoproductreference:
-            raise ValidationError('A Relationship must have a "Relates To product Reference"')
-        if self._productreference == self._relatestoproductreference:
-            raise ValidationError('A Relationship cannot reference twice the same Product')
-
-
-class CVRFGroup(object):
-    def __init__(self, groupid):
-        self._groupid = groupid
-        self._description = None
-        self._productids = []
-
-    def setDescription(self, description):
-        self._description = description
-
-    def addProductID(self, productid):
-        self._productids.append(productid)
-
-    def getTitle(self):
-        if self._description:
-            return "%s (%d products)" % (self._description, len(self._productids))
-        return "#%s (%d products)" % (self._groupid, len(self._productids))
-
-    def validate(self):
-        if not self._groupid:
-            raise ValidationError('A Group must have a GroupID')
-        if not self._productids or len(self._productids) < 2:
-            raise ValidationError('A Group must contain at least two products')
-
+from .common import (CVRFNote, CVRFReference, CVRFAcknowledgment,
+    ValidationError)
+from .document import (CVRF, CVRFPublisher, CVRFTracking, CVRFTrackingID,
+    CVRFAggregateSeverity, CVRFRevision, CVRFGenerator)
+from .producttree import (CVRFProductBranch, CVRFFullProductName,
+    CVRFRelationship, CVRFGroup)
+from .vulnerability import (CVRFVulnerability, CVRFVulnerabilityID,
+    CVRFThreat, CVRFProductStatus, CVRFCVSSSet, CVRFRemediation,
+    CVRFInvolvement, CVRFCWE)
 
-class CVRFVulnerabilityID(object):
-    def __init__(self, systemname, value):
-        self._systemname = systemname
-        self._value = value
-
-    def validate(self):
-        if not self._systemname:
-            raise ValidationError('A Vulnerability ID must have a System Name')
-        if not self._value:
-            raise ValidationError('A Vulnerability ID must have a value')
-
-
-class CVRFVulnerability(object):
-    def __init__(self, ordinal):
-        self._ordinal = ordinal
-        self._title = None
-        self._id = None
-        self._notes = []
-        self._discoverydate = None
-        self._releasedate = None
-        self._involvements = []
-        self._cve = None
-        self._cwes = []
-        self._productstatuses = []
-        self._threats = []
-        self._cvsss = []
-        self._remediations = []
-        self._references = []
-        self._acknowledgments = []
-
-    def setTitle(self, title):
-        self._title = title
-
-    def setID(self, _id):
-        self._id = _id
-
-    def addNote(self, note):
-        self._notes.append(note)
-
-    def setDiscoveryDate(self, date):
-        self._discoverydate = date
-
-    def setReleaseDate(self, date):
-        self._releasedate = date
-
-    def addInvolvement(self, involvement):
-        self._involvements.append(involvement)
-
-    def setCVE(self, cve):
-        self._cve = cve
-
-    def addCWE(self, cwe):
-        self._cwes.append(cwe)
-
-    def addProductStatus(self, productstatus):
-        self._productstatuses.append(productstatus)
-
-    def addThreat(self, threat):
-        self._threats.append(threat)
-
-    def addCVSSSet(self, cvss_set):
-        self._cvsss.append(cvss_set)
-
-    def addRemediation(self, remediation):
-        self._remediations.append(remediation)
-
-    def addReference(self, ref):
-        self._references.append(ref)
-
-    def addAcknowledgment(self, ack):
-        self._acknowledgments.append(ack)
-
-    def getTitle(self):
-        """ return something that can be used as a title """
-        if self._title:
-            if self._id:
-                return "%s (%s)" % (self._title, self._id._value)
-            return self._title
-        if self._id:
-            return self._id._value
-        return "#%d" % self._ordinal
-
-    def getNote(self, ordinal):
-        for note in self._notes:
-            if note._ordinal == ordinal:
-                return note
-        return None
-
-    def mentionsProdId(self, productid):
-        """ Returns in which sub element, self is mentioning the productid """
-        for category in (self._productstatuses, self._threats, self._cvsss, self._remediations):
-            for subelem in category:
-                if productid in subelem._productids:
-                    yield subelem
-
-    def isMentioningProdId(self, productid):
-        """ Returns if self is mentioning the productid """
-        for e in self.mentionsProdId(productid):
-            # We only need to know if the generator yield at least one elem.
-            return True
-        return False
-
-    def mentionsGroupId(self, groupid):
-        for category in (self._threats, self._remediations):
-            for subelem in category:
-                if groupid in subelem._groupids:
-                    yield subelem
-
-    def isMentioningGroupId(self, groupids):
-        """ Make sure you call this with a list (not a generator or a tuple)
-        when wished """
-        if not isinstance(groupids, list):
-            groupids = [groupids]
-        for groupid in groupids:
-            for _ in self.mentionsGroupId(groupid):
-                # We only need to know if the generator yield at least one elem.
-                return True
-        return False
-
-    def validate(self, productids, groupids):
-        if not self._ordinal:
-            raise ValidationError('A Vulnerability must have an ordinal')
-        if self._id is not None:
-            self._id.validate()
-        ordinals = set()
-        for note in self._notes:
-            note.validate()
-            if note._ordinal in ordinals:
-                raise ValidationError('Vulnerability Note Ordinal %d duplicated' % note._ordinal)
-            ordinals.add(note._ordinal)
-        for involvement in self._involvements:
-            involvement.validate()
-        for cwe in self._cwes:
-            cwe.validate()
-        for status in self._productstatuses:
-            status.validate(productids)
-        pids = set()
-        for status in self._productstatuses:
-            for pid in status._productids:
-                if pid in pids:
-                    raise ValidationError('ProductID %s mentionned in two different ProductStatuses for Vulnerability %d' % (pid, self._ordinal))
-                pids.add(pid)
-        for threat in self._threats:
-            threat.validate(productids, groupids)
-        for cvss in self._cvsss:
-            cvss.validate(productids)
-        pids = set()
-        for cvss in self._cvsss:
-            for pid in (cvss._productids or productids):
-                if pid in pids:
-                    raise ValidationError('ProductID %s mentionned in two different CVSS Score Sets for Vulnerability %d' % (pid, self._ordinal))
-                pids.add(pid)
-        for remediation in self._remediations:
-            remediation.validate(productids, groupids)
-        for reference in self._references:
-            reference.validate()
-        for acknowledgment in self._acknowledgments:
-            acknowledgment.validate()
-
-
-class CVRFInvolvement(object):
-    PARTIES = CVRFPublisher.TYPES
-    STATUSES = ('Open', 'Disputed', 'In Progress', 'Completed',
-                'Contact Attempted', 'Not Contacted')
-    def __init__(self, party, status):
-        self._party = party
-        self._status = status
-        self._description = None
-
-    def setDescription(self, description):
-        self._description = description
-
-    def getTitle(self):
-        return "From %s: %s" % (self._party, self._status)
-
-    def validate(self):
-        if not self._party:
-            raise ValidationError('An Involvement must have a Party')
-        if self._party not in self.PARTIES:
-            raise ValidationError("An Involvement's Party must be one of %s" % ', '.join(self.PARTIES))
-        if not self._status:
-            raise ValidationError('An Involvement must have a Status')
-        if self._status not in self.STATUSES:
-            raise ValidationError("An Involvement's Status must be one of %s" % ', '.join(self.STATUSES))
-
-
-class CVRFCWE(object):
-    def __init__(self, _id, value):
-        self._id = _id
-        self._value = value
-
-    def validate(self):
-        if not self._id:
-            raise ValidationError('A CWE must have an ID')
-        if not self._value:
-            raise ValidationError('A CWE must have a description')
-
-
-class CVRFProductStatus(object):
-    TYPES = ('First Affected', 'Known Affected', 'Known Not Affected',
-             'First Fixed', 'Fixed', 'Recommended', 'Last Affected')
-    NAME = "Product Status"
-    def __init__(self, _type):
-        self._type = _type
-        self._productids = []
-
-    def addProductID(self, productid):
-        self._productids.append(productid)
-
-    def getTitle(self):
-        return "%s: %d products" % (self._type, len(self._productids))
-
-    def validate(self, productids):
-        if not self._type:
-            raise ValidationError('A Product Status must have a Type')
-        if self._type not in self.TYPES:
-            raise ValidationError("A Product Status' Type must be one of %s" % ', '.join(self.TYPES))
-        if len(self._productids) < 1:
-            raise ValidationError('A Product Status must mention at least one Product')
-        for productid in self._productids:
-            if productid not in productids:
-                raise ValidationError('Unknown ProductID: %s' % productid)
-
-
-class CVRFThreat(object):
-    TYPES = ('Impact', 'Exploit Status', 'Target Set')
-    NAME = "Threat"
-    def __init__(self, _type, description):
-        self._type = _type
-        self._description = description
-        self._date = None
-        self._productids = []
-        self._groupids = []
-
-    def setDate(self, date):
-        self._date = date
-
-    def addProductID(self, productid):
-        self._productids.append(productid)
-
-    def addGroupID(self, groupid):
-        self._groupids.append(groupid)
-
-    def getTitle(self):
-        return self._type
-
-    def validate(self, productids, groupids):
-        if not self._type:
-            raise ValidationError('A Threat must have a Type')
-        if self._type not in self.TYPES:
-            raise ValidationError("A Threat's Type must be one of %s" % ', '.join(self.TYPES))
-        if not self._description:
-            raise ValidationError('A Threat must have a Description')
-        for productid in self._productids:
-            if productid not in productids:
-                raise ValidationError('Unknown ProductID: %s' % productid)
-        for groupid in self._groupids:
-            if groupid not in groupids:
-                raise ValidationError('Unknown GroupID: %s' % groupid)
-
-
-class CVRFCVSSSet(object):
-    # To determine the base Score
-    VALUES = {'AV': {'L':0.395, 'A':0.646, 'N':1.0},
-              'AC': {'H':0.35, 'M':0.61 ,'L':0.71},
-              'Au': {'M':0.45, 'S':0.56, 'N':0.704},
-              'C': {'N':0.0, 'P':0.275, 'C':0.66},
-              'I': {'N':0.0, 'P':0.275, 'C':0.66},
-              'A': {'N':0.0, 'P':0.275, 'C':0.66}}
-    NAME = "CVSS Score Set"
-    def __init__(self, basescore):
-        self._basescore = basescore
-        self._temporalscore = None
-        self._environmentalscore = None
-        self._vector = None
-        self.vector = None
-        self._productids = []
-
-    def setTemporalScore(self, tempscore):
-        self._temporalscore = tempscore
-
-    def setEnvironmentalScore(self, envscore):
-        self._environmentalscore = envscore
-
-    def setVector(self, vector):
-        self._vector = vector
-        if vector is None:
-            self.vector = vector
-            return
-        try:
-            self.vector = {}
-            for component in vector[:26].split('/'):
-                name, value = component.split(':')
-                self.vector[name] = self.VALUES[name][value]
-        except (KeyError, ValueError):
-            self.vector = None
-
-    def addProductID(self, productid):
-        self._productids.append(productid)
-
-    def baseScore(self):
-        v = self.vector # make an alias for shorter lines
-        exploitability = 20 * v['AV'] * v['AC'] * v['Au']
-        impact = 10.41 * (1 - (1 - v['C']) * (1 - v['I']) * (1 - v['A']))
-        def f(i): return 0 if i == 0 else 1.176
-        return ((0.6 * impact) + (0.4 * exploitability) - 1.5) * f(impact)
-
-    def validate(self, productids):
-        if not self._basescore:
-            raise ValidationError('A CVSS Score Set must have a Base Score')
-        if self._vector and not self.vector:
-            raise ValidationError('Syntax Error in CVSS Vector')
-        if self.vector and (abs(self._basescore - self.baseScore()) >= 0.05):
-            raise ValidationError('Inconsistency in CVSS Score Set between Vector (%f) and Base Score (%f)' % (self.baseScore(), self._basescore))
-        for productid in self._productids:
-            if productid not in productids:
-                raise ValidationError('Unknown ProductID: %s' % productid)
-
-
-class CVRFRemediation(object):
-    TYPES = ('Workaround', 'Mitigation', 'Vendor Fix', 'None Available',
-             'Will Not Fix')
-    NAME = "Remediation"
-    def __init__(self, _type, description):
-        self._type = _type
-        self._description = description
-        self._date = None
-        self._entitlement = None
-        self._url = None
-        self._productids = []
-        self._groupids = []
-
-    def setDate(self, date):
-        self._date = date
-
-    def setEntitlement(self, entitlement):
-        self._entitlement = entitlement
-
-    def setURL(self, url):
-        self._url = url
-
-    def addProductID(self, productid):
-        self._productids.append(productid)
-
-    def addGroupID(self, groupid):
-        self._groupids.append(groupid)
-
-    def getTitle(self):
-        return self._type
-
-    def validate(self, productids, groupids):
-        if not self._type:
-            raise ValidationError('A Remediation must have a Type')
-        if self._type not in self.TYPES:
-            raise ValidationError("A Remediation's Type must be one of %s" % ', '.join(self.TYPES))
-        if not self._description:
-            raise ValidationError('A Remediation must have a Description')
-        for productid in self._productids:
-            if productid not in productids:
-                raise ValidationError('Unknown ProductID: %s' % productid)
-        for groupid in self._groupids:
-            if groupid not in groupids:
-                raise ValidationError('Unknown GroupID: %s' % groupid)
-
-
-class CVRF(object):
-    def __init__(self, title, _type):
-        self._title = title
-        self._type = _type
-        self._publisher = None
-        self._tracking = None
-        self._notes = []
-        self._distribution = None
-        self._aggregateseverity = None
-        self._references = []
-        self._acknowledgments = []
-        self._producttree = None
-        self._vulnerabilities = []
-
-    def setPublisher(self, publisher):
-        self._publisher = publisher
-
-    def setTracking(self, tracking):
-        self._tracking = tracking
-
-    def addNote(self, note):
-        self._notes.append(note)
-
-    def setDistribution(self, distribution):
-        self._distribution = distribution
-
-    def setAggregateSeverity(self, aggregateseverity):
-        self._aggregateseverity = aggregateseverity
-
-    def addReference(self, ref):
-        self._references.append(ref)
-
-    def addAcknowledgment(self, ack):
-        self._acknowledgments.append(ack)
-
-    def createProductTree(self):
-        """ only done if the element is there """
-        self._producttree = CVRFProductTree()
-        return self._producttree
-
-    def addVulnerability(self, vuln):
-        self._vulnerabilities.append(vuln)
-
-    def getProductForID(self, productid):
-        if self._producttree is None:
-            raise ValueError('No ProductTree')
-        return self._producttree.getProductForID(productid)
-
-    def getGroupForID(self, groupid):
-        if self._producttree is None:
-            raise ValueError('No ProductTree')
-        return self._producttree.getGroupForID(groupid)
-
-    def getHighestCVSS(self):
-        highestBaseScore = 0
-        highest = None
-        for vulnerability in self._vulnerabilities:
-            for cvss in vulnerability._cvsss:
-                if cvss._basescore <= highestBaseScore:
-                    continue
-                highestBaseScore = cvss._basescore
-                highest = cvss
-        return highest
-
-    def getProductList(self, type_='Fixed'):
-        products = set()
-        if type_ == 'Fixed':
-            # First try through the Remediation
-            for vulnerability in self._vulnerabilities:
-                for remediation in vulnerability._remediations:
-                    if remediation._type != 'Vendor Fix':
-                        continue
-                    for productid in remediation._productids:
-                        products.add(productid)
-                    for groupid in remediation._groupids:
-                        for productid in self.getGroupForID(groupid)._productids:
-                            products.add(productid)
-        if not products:
-            # If nothing there, try through the productstatuses
-            for vulnerability in self._vulnerabilities:
-                for status in vulnerability._productstatuses:
-                    if status._type != type_:
-                        continue
-                    for productid in status._productids:
-                        products.add(productid)
-        return set(self.getProductForID(p) for p in products)
-
-    def mentionsProductId(self, productid):
-        # We first look at the ProductTree
-        ptree = self._producttree
-        for relation in ptree._relationships:
-            if productid == relation._productreference:
-                yield relation
-            elif productid == relation._relatestoproductreference:
-                yield relation
-        # Then go through the groups
-        for group in ptree._groups:
-            if productid in group._productids:
-                yield group
-        # Finally, go through all the Vulnerabilities
-        for vulnerability in self._vulnerabilities:
-            for item in vulnerability.mentionsProdId(productid):
-                yield item
-
-    def isProductOrphan(self, productid):
-        """ Returns if a productid is mentioned nowhere in the document """
-        for item in self.mentionsProductId(productid):
-            return True
-        return False
-
-    def changeProductID(self, old, new):
-        for item in self.mentionsProductId(old):
-            if isinstance(item, CVRFRelationship):
-                if old == item._productreference:
-                    item._productreference = new
-                elif old == item._relatestoproductreference:
-                    item._relatestoproductreference = new
-            else:
-                item._productids.remove(old)
-                item._productids.append(new)
-
-    def isGroupOrphan(self, groupid):
-        """ Returns if a group can be safely deleted """
-        for vulnerability in self._vulnerabilities:
-            if vulnerability.isMentioningGroupId(groupid):
-                return False
-        return True
-
-    def isProductTreeOrphan(self):
-        """ Difference with the previous method is that we don;t care about
-        inter-producttree references """
-        for vulnerability in self._vulnerabilities:
-            for product in self._producttree._products:
-                if vulnerability.isMentioningProdId(product._productid):
-                    return False
-            for group in self._producttree._groups:
-                if vulnerability.isMentioningGroupId(group._groupid):
-                    return False
-        return True
-
-    def getNote(self, ordinal):
-        for note in self._notes:
-            if note._ordinal == ordinal:
-                return note
-        return None
-
-    def getDocId(self):
-        if self._tracking is not None:
-            return self._tracking.getId()
-        # Make up something ...
-        return self._title.lower()
-
-    def validate(self):
-        if not self._title:
-            raise ValidationError('Document Title cannot be empty')
-        if not self._type:
-            raise ValidationError('Document Type cannot be empty')
-        if self._publisher is None:
-            raise ValidationError('Document Publisher needs to be set')
-        self._publisher.validate()
-        if self._tracking is None:
-            raise ValidationError('Document Tracking needs to be set')
-        self._tracking.validate()
-        ordinals = set()
-        for note in self._notes:
-            note.validate()
-            if note._ordinal in ordinals:
-                raise ValidationError('Document Note ordinal %d is issued twice' % note._ordinal)
-            ordinals.add(note._ordinal)
-        for reference in self._references:
-            reference.validate()
-        for acknowledgment in self._acknowledgments:
-            acknowledgment.validate()
-        productids = set()
-        groupids = set()
-        if self._producttree:
-            productids, groupids = self._producttree.validate()
-        ordinals = set()
-        for vulnerability in self._vulnerabilities:
-            vulnerability.validate(productids, groupids)
-            if vulnerability._ordinal in ordinals:
-                raise ValidationError('Vulnerability ordinal %d is issued twice' % vulnerability._ordinal)
-            ordinals.add(vulnerability._ordinal)
-
-    def __str__(self):
-        s = [
-            'Title: %s' % self._title,
-            'Type: %s' % self._type,
-            'Publisher: %s' % self._publisher,
-            'tracking: %s' % self._tracking,
-            '%d Notes: %s' % (len(self._notes), ', '.join(
-                str(n) for n in self._notes))
-        ]
-        if self._distribution is not None:
-            s.append('Distribution: %s' % self._distribution)
-        s.extend([
-            '%d Acknowledgments' % len(self._acknowledgments),
-            'Products: %s' % self._producttree,
-        ])
-        return '\n'.join(s)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/farolluz/document.py	Fri Oct 24 17:01:26 2014 +0200
@@ -0,0 +1,387 @@
+# -*- coding: utf-8 -*-
+#
+# Authors:
+# Benoît Allard <benoit.allard@greenbone.net>
+#
+# Copyright:
+# Copyright (C) 2014 Greenbone Networks GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+
+"""\
+Objects related to CVRF Documents
+"""
+
+from .common import ValidationError
+from .producttree import CVRFProductTree, CVRFRelationship
+
+class CVRFPublisher(object):
+    TYPES = ('Vendor', 'Discoverer', 'Coordinator', 'User', 'Other')
+    def __init__(self, _type, vendorid=None):
+        self._type = _type
+        self._vendorid = vendorid
+        self._contact = None
+        self._authority = None
+
+    def setContact(self, contact):
+        self._contact = contact
+
+    def setAuthority(self, authority):
+        self._authority = authority
+
+    def validate(self):
+        if not self._type:
+            raise ValidationError('Document Publisher needs to have a type')
+        if self._type not in self.TYPES:
+            raise ValidationError('Document Publisher Type needs to be one of %s' % ', '.join(self.TYPES))
+
+    def __str__(self):
+        s = 'CVRFPublisher: %s' % self._type
+        if self._vendorid is not None:
+            s += ' ID: %s' % self._vendorid
+        if self._contact is not None:
+            s += ' Contact: "%s"' % self._contact
+        if self._authority is not None:
+            s += ' Authority: "%s"' % self._authority
+        return s
+
+
+class CVRFTrackingID(object):
+    def __init__(self, _id):
+        self._id = _id
+        self._aliases = []
+
+    def addAlias(self, alias):
+        self._aliases.append(alias)
+
+    def getId(self):
+        return self._id
+
+    def validate(self):
+        if not self._id:
+            raise ValidationError('Document ID cannot be left empty')
+
+    def __str__(self):
+        if self._aliases:
+            return "%s (%s)" % (self._id, ', '.join(self._aliases))
+        return self._id
+
+
+class CVRFTracking(object):
+    STATUSES = ('Draft', 'Interim', 'Final')
+    def __init__(self, _id, status, version, initial, current):
+        self._identification = _id
+        self._status = status
+        self._version = version
+        self._history = []
+        self._initialDate = initial
+        self._currentDate = current
+        self._generator = None
+
+    def addRevision(self, revision):
+        self._history.append(revision)
+
+    def setGenerator(self, generator):
+        self._generator = generator
+
+    def getId(self):
+        return self._identification.getId()
+
+    def validate(self):
+        if self._identification is None:
+            raise ValidationError('Document Tracking needs to have an Identification')
+        self._identification.validate()
+        if not self._status:
+            raise ValidationError('Document status must be set')
+        if self._status not in self.STATUSES:
+            raise ValidationError('Document Status must be one of %s' % ', '.join(self.STATUSES))
+        if not self._version:
+            raise ValidationError('Document Version must be set')
+        if len(self._version) > 4:
+            raise ValidationError('Document Version must be comprised between `nn` and `nn.nn.nn.nn`')
+        if not self._history:
+            raise ValidationError('Document must have at least a revision')
+        if not self._initialDate:
+            raise ValidationError('Document must have an initial Release date set')
+        prev_date = self._initialDate
+        if self._history[0]._date < self._initialDate:
+            # Documents could have revisions before being released
+            prev_date = self._history[0]._date
+        prev = ()
+        for revision in self._history:
+            revision.validate()
+            if revision._number <= prev:
+                raise ValidationError('Revision numbers must always be increasing')
+            if revision._date < prev_date:
+                raise ValidationError('Revision dates must always be increasing')
+            prev = revision._number
+            prev_date = revision._date
+        if not self._currentDate:
+            raise ValidationError('Document must have a Current Release Date set')
+        if self._currentDate != self._history[-1]._date:
+            raise ValidationError('Current Release Date must be the same as the Date from the last Revision')
+        if self._initialDate > self._currentDate:
+            raise ValidationError('Initial date must not be after current Date')
+        if self._version != self._history[-1]._number:
+            raise ValidationError('Document version must be the same as the number of the last Revision')
+
+    def __str__(self):
+        s = "ID: %s" % self._identification
+        s += " Status: %s" % self._status
+        s += " v%s" % '.'.join('%d' % i for i in self._version)
+        s += " %d revisions" % len(self._history)
+        s += " Initial release: %s" % self._initialDate.isoformat()
+        return s
+
+
+class CVRFRevision(object):
+    def __init__(self, number, date, description):
+        self._number = number
+        self._date = date
+        self._description = description
+
+    def validate(self):
+        if not self._number:
+            raise ValidationError('A Revision must have a Number')
+        if not self._date:
+            raise ValidationError('A Revision must have a Date')
+        if not self._description:
+            raise ValidationError('A Revision must have a Description')
+
+class CVRFGenerator(object):
+    def __init__(self):
+        self._engine = None
+        self._date = None
+
+    def setEngine(self, engine):
+        self._engine = engine
+
+    def setDate(self, date):
+        self._date = date
+
+    def validate(self):
+        if (not self._engine) and (not self._date):
+            raise ValidationError('The Generator must have at least an Engine or a Date')
+
+
+class CVRFAggregateSeverity(object):
+    def __init__(self, severity):
+        self._severity = severity
+        self._namespace = None
+
+    def setNamespace(self, namespace):
+        self._namespace = namespace
+
+
+class CVRF(object):
+    def __init__(self, title, _type):
+        self._title = title
+        self._type = _type
+        self._publisher = None
+        self._tracking = None
+        self._notes = []
+        self._distribution = None
+        self._aggregateseverity = None
+        self._references = []
+        self._acknowledgments = []
+        self._producttree = None
+        self._vulnerabilities = []
+
+    def setPublisher(self, publisher):
+        self._publisher = publisher
+
+    def setTracking(self, tracking):
+        self._tracking = tracking
+
+    def addNote(self, note):
+        self._notes.append(note)
+
+    def setDistribution(self, distribution):
+        self._distribution = distribution
+
+    def setAggregateSeverity(self, aggregateseverity):
+        self._aggregateseverity = aggregateseverity
+
+    def addReference(self, ref):
+        self._references.append(ref)
+
+    def addAcknowledgment(self, ack):
+        self._acknowledgments.append(ack)
+
+    def createProductTree(self):
+        """ only done if the element is there """
+        self._producttree = CVRFProductTree()
+        return self._producttree
+
+    def addVulnerability(self, vuln):
+        self._vulnerabilities.append(vuln)
+
+    def getProductForID(self, productid):
+        if self._producttree is None:
+            raise ValueError('No ProductTree')
+        return self._producttree.getProductForID(productid)
+
+    def getGroupForID(self, groupid):
+        if self._producttree is None:
+            raise ValueError('No ProductTree')
+        return self._producttree.getGroupForID(groupid)
+
+    def getHighestCVSS(self):
+        highestBaseScore = 0
+        highest = None
+        for vulnerability in self._vulnerabilities:
+            for cvss in vulnerability._cvsss:
+                if cvss._basescore <= highestBaseScore:
+                    continue
+                highestBaseScore = cvss._basescore
+                highest = cvss
+        return highest
+
+    def getProductList(self, type_='Fixed'):
+        products = set()
+        if type_ == 'Fixed':
+            # First try through the Remediation
+            for vulnerability in self._vulnerabilities:
+                for remediation in vulnerability._remediations:
+                    if remediation._type != 'Vendor Fix':
+                        continue
+                    for productid in remediation._productids:
+                        products.add(productid)
+                    for groupid in remediation._groupids:
+                        for productid in self.getGroupForID(groupid)._productids:
+                            products.add(productid)
+        if not products:
+            # If nothing there, try through the productstatuses
+            for vulnerability in self._vulnerabilities:
+                for status in vulnerability._productstatuses:
+                    if status._type != type_:
+                        continue
+                    for productid in status._productids:
+                        products.add(productid)
+        return set(self.getProductForID(p) for p in products)
+
+    def mentionsProductId(self, productid):
+        # We first look at the ProductTree
+        ptree = self._producttree
+        for relation in ptree._relationships:
+            if productid == relation._productreference:
+                yield relation
+            elif productid == relation._relatestoproductreference:
+                yield relation
+        # Then go through the groups
+        for group in ptree._groups:
+            if productid in group._productids:
+                yield group
+        # Finally, go through all the Vulnerabilities
+        for vulnerability in self._vulnerabilities:
+            for item in vulnerability.mentionsProdId(productid):
+                yield item
+
+    def isProductOrphan(self, productid):
+        """ Returns if a productid is mentioned nowhere in the document """
+        for item in self.mentionsProductId(productid):
+            return True
+        return False
+
+    def changeProductID(self, old, new):
+        for item in self.mentionsProductId(old):
+            if isinstance(item, CVRFRelationship):
+                if old == item._productreference:
+                    item._productreference = new
+                elif old == item._relatestoproductreference:
+                    item._relatestoproductreference = new
+            else:
+                item._productids.remove(old)
+                item._productids.append(new)
+
+    def isGroupOrphan(self, groupid):
+        """ Returns if a group can be safely deleted """
+        for vulnerability in self._vulnerabilities:
+            if vulnerability.isMentioningGroupId(groupid):
+                return False
+        return True
+
+    def isProductTreeOrphan(self):
+        """ Difference with the previous method is that we don;t care about
+        inter-producttree references """
+        for vulnerability in self._vulnerabilities:
+            for product in self._producttree._products:
+                if vulnerability.isMentioningProdId(product._productid):
+                    return False
+            for group in self._producttree._groups:
+                if vulnerability.isMentioningGroupId(group._groupid):
+                    return False
+        return True
+
+    def getNote(self, ordinal):
+        for note in self._notes:
+            if note._ordinal == ordinal:
+                return note
+        return None
+
+    def getDocId(self):
+        if self._tracking is not None:
+            return self._tracking.getId()
+        # Make up something ...
+        return self._title.lower()
+
+    def validate(self):
+        if not self._title:
+            raise ValidationError('Document Title cannot be empty')
+        if not self._type:
+            raise ValidationError('Document Type cannot be empty')
+        if self._publisher is None:
+            raise ValidationError('Document Publisher needs to be set')
+        self._publisher.validate()
+        if self._tracking is None:
+            raise ValidationError('Document Tracking needs to be set')
+        self._tracking.validate()
+        ordinals = set()
+        for note in self._notes:
+            note.validate()
+            if note._ordinal in ordinals:
+                raise ValidationError('Document Note ordinal %d is issued twice' % note._ordinal)
+            ordinals.add(note._ordinal)
+        for reference in self._references:
+            reference.validate()
+        for acknowledgment in self._acknowledgments:
+            acknowledgment.validate()
+        productids = set()
+        groupids = set()
+        if self._producttree:
+            productids, groupids = self._producttree.validate()
+        ordinals = set()
+        for vulnerability in self._vulnerabilities:
+            vulnerability.validate(productids, groupids)
+            if vulnerability._ordinal in ordinals:
+                raise ValidationError('Vulnerability ordinal %d is issued twice' % vulnerability._ordinal)
+            ordinals.add(vulnerability._ordinal)
+
+    def __str__(self):
+        s = [
+            'Title: %s' % self._title,
+            'Type: %s' % self._type,
+            'Publisher: %s' % self._publisher,
+            'tracking: %s' % self._tracking,
+            '%d Notes: %s' % (len(self._notes), ', '.join(
+                str(n) for n in self._notes))
+        ]
+        if self._distribution is not None:
+            s.append('Distribution: %s' % self._distribution)
+        s.extend([
+            '%d Acknowledgments' % len(self._acknowledgments),
+            'Products: %s' % self._producttree,
+        ])
+        return '\n'.join(s)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/farolluz/producttree.py	Fri Oct 24 17:01:26 2014 +0200
@@ -0,0 +1,365 @@
+# -*- coding: utf-8 -*-
+#
+# Authors:
+# Benoît Allard <benoit.allard@greenbone.net>
+#
+# Copyright:
+# Copyright (C) 2014 Greenbone Networks GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+
+"""\
+Product Tree Objects related to CVRF Documents
+"""
+
+from .common import ValidationError
+
+class CVRFProductTree(object):
+    def __init__(self):
+        # All the branches, they can be order with their `parent` attribute
+        self._branches = []
+        self._groups = []
+        self._relationships = []
+        self._products = []
+        self._groups = []
+
+    def addProduct(self, product):
+        """ Add to the product list """
+        self._products.append(product)
+
+    def addRelationship(self, rel):
+        self._relationships.append(rel)
+
+    def addGroup(self, group):
+        self._groups.append(group)
+
+    def getProductForID(self, productid):
+        for product in self._products:
+            if product._productid == productid:
+                return product
+        raise KeyError(productid)
+
+    def getGroupForID(self, groupid):
+        for group in self._groups:
+            if group._groupid == groupid:
+                return group
+        raise KeyError(groupid)
+
+    def decomposeProduct(self, productid):
+        """ In case of product defined as a relationship (product X installed
+        on OS Y), this gives us the following tuple: (OS, product). """
+        product = self.getProductForID(productid)
+        parent = product._parent
+        if parent is None:
+            return (None, None)
+        if not isinstance(parent, CVRFRelationship):
+            return (None, None)
+        relationtype = parent._relationtype.replace(' ', '').lower()
+        if relationtype not in ('defaultcomponentof', 'installedon'):
+            return (None, None)
+        return (
+            self.getProductForID(parent._relatestoproductreference),
+            self.getProductForID(parent._productreference)
+        )
+
+    def getBranch(self, path):
+        if len(path) == 0:
+            return self
+        branches = self._branches
+        node = None
+        for idx in path:
+            node = branches[idx]
+            branches = node._childs
+        return node
+
+    def getBranches(self):
+        for branch in self._branches:
+            yield branch
+            for sub_branch in branch.getBranches():
+                yield sub_branch
+
+    def getPath(self):
+        return ()
+
+    def getNameOfRelationship(self, relationship):
+        if relationship is None:
+            return ''
+        return ' '.join((self.getProductForID(relationship._productreference)._name, 'as',
+                relationship._relationtype.lower(),
+                self.getProductForID(relationship._relatestoproductreference)._name))
+
+    def getOrphanedBranches(self, product=None):
+        """ The branches that could accept `product` as Product Definition """
+        white_list = []
+        if product is not None:
+            white_list = [product._parent]
+        for branch in self.getBranches():
+            if (branch in white_list) or branch.isOrphaned():
+                yield branch
+
+    def getNotTerminalBranches(self, b2=None):
+        """\
+        The branches that could accept `b2` as new sub-branches
+        Note that b2 and all its sub-branches cannot be listed
+        """
+        black_list = []
+        if b2 is not None:
+            black_list = [b2] + list(b2.getBranches())
+        for branch in self.getBranches():
+            if branch in black_list:
+                continue
+            if branch._product is None:
+                yield branch
+
+    def getOrphanedRelationships(self, product=None):
+        """ The relationships that need a product defninition """
+        white_list = []
+        if product is not None:
+            white_list = [product.getCurrentRelationship()]
+        for i, relationship in enumerate(self._relationships):
+            if (relationship in white_list) or relationship.isOrphaned():
+                yield (i, relationship)
+
+    def nbProducts(self):
+        """ Amount of 'raw' Products """
+        return len([p for p in self._products if p._parent is self])
+
+    def validate(self):
+        for branch in self._branches:
+            branch.validate()
+        productids = set()
+        for product in self._products:
+            product.validate()
+            if product._productid in productids:
+                raise ValidationError('Each ProductID must be unique (%s)' % product._productid)
+            productids.add(product._productid)
+        for relationship in self._relationships:
+            relationship.validate()
+            for productid in (relationship._productreference,
+                              relationship._relatestoproductreference):
+                if productid not in productids:
+                    raise ValidationError('ProductID %s is unknown' % productid)
+        groupids = set()
+        for group in self._groups:
+            group.validate()
+            if group._groupid in groupids:
+                raise ValidationError('Duplicated GroupID: %s' % group._groupid)
+            groupids.add(group._groupid)
+            for productid in group._productids:
+                if productid not in productids:
+                    raise ValidationError('ProductID %s is unknown' % productid)
+        return productids, groupids
+
+    def __str__(self):
+        return 'Products: %s' % '\n'.join(str(p) for p in self._products)
+
+
+class CVRFProductBranch(object):
+    TYPES = ('Vendor', 'Product Family', 'Product Name', 'Product Version',
+             'Patch Level', 'Service Pack', 'Architecture', 'Language',
+             'Legacy', 'Specification')
+    def __init__(self, _type, name, parentbranch):
+        self._type = _type
+        self._name = name
+        self._childs = []
+        self._product = None
+        self.link(parentbranch)
+
+    def getParent(self):
+        return self._parentbranch
+
+    def getPath(self, string=False):
+        """ return the path to that branch element as a tuple """
+        if self.isRoot():
+            for i, b in enumerate(self._parentbranch._branches):
+                if b is self:
+                    if string:
+                        return '%d' % i
+                    return (i, )
+        else:
+            for i, b in enumerate(self._parentbranch._childs):
+                if b is self:
+                    if string:
+                        return '/'.join([self._parentbranch.getPath(string), '%d' % i])
+                    return self._parentbranch.getPath(string) + (i,)
+        if string:
+            return ''
+        return ()
+
+    def getTree(self):
+        """ this returns a list of tuples (type, name) leading to here"""
+        if self.isRoot():
+            return [(self._type, self._name)]
+        return self._parentbranch.getTree() + [(self._type, self._name)]
+
+    def getName(self):
+        return ' / '.join("%s: %s" % (type_, name) for type_, name in self.getTree())
+
+    def getParentPath(self):
+        """ return as string the path to the parent """
+        return '/'.join('%s' % p for p in self.getPath()[:-1])
+
+    def isRoot(self):
+        return isinstance(self._parentbranch, CVRFProductTree)
+
+    def isOrphaned(self):
+        """ Has no childs and no product """
+        return len(self._childs) == 0 and (self._product is None)
+
+    def getBranches(self):
+        for branch in self._childs:
+            yield branch
+            for sub_branch in branch.getBranches():
+                yield sub_branch
+
+    def unlink(self):
+        """ Unset our _parent, and remove us from the _parent._childs """
+        if self.isRoot():
+            self.getParent()._branches.remove(self)
+        else:
+            self.getParent()._childs.remove(self)
+        self._parentbranch = None
+
+    def link(self, parent):
+        """ Actually, only set the parent """
+        self._parentbranch = parent
+        if self.isRoot():
+            parent._branches.append(self)
+        else:
+            parent._childs.append(self)
+
+
+    def validate(self):
+        if not self._type:
+            raise ValidationError('A Branch must have a Type')
+        if self._type not in self.TYPES:
+            raise ValidationError('A Branch Type must be one of %s' % ', '.join(self.TYPES))
+        if not self._name:
+            raise ValidationError('A Branch must have a Name')
+        for branch in self._childs:
+            branch.validate()
+        if self.isOrphaned():
+            raise ValidationError('A Branch must have at least a sub-product or sub-branches')
+
+    def __str__(self):
+        return "%s: %s" % (self._type, self._name)
+
+
+class CVRFFullProductName(object):
+    def __init__(self, productid, name, parent, cpe=None):
+        self._productid = productid
+        self._name = name
+        self._cpe = cpe
+        # Can be None (directly under the tree), a ProductBranch, or a
+        # Relationship
+        self.link(parent)
+
+    def isRoot(self):
+        return isinstance(self._parent, CVRFProductTree)
+
+    def isRelationship(self):
+        return isinstance(self._parent, CVRFRelationship)
+
+    def getTree(self):
+        if not isinstance(self._parent, CVRFProductBranch):
+            return []
+        return self._parent.getTree()
+
+    def getParentPath(self):
+        if self.isRoot() or self.isRelationship():
+            return ''
+        return self._parent.getPath(True)
+
+    def getCurrentRelationship(self):
+        if self.isRelationship():
+            return self._parent
+        return None
+
+    def unlink(self):
+        """ Unset our _parent, and remove us from the _parent._childs
+        We are still in the product list.
+        """
+        if not self.isRoot():
+            self._parent._product = None
+        self._parent = None
+
+    def link(self, parent):
+        self._parent = parent
+        if not self.isRoot():
+            parent._product = self
+
+    def validate(self):
+        if not self._productid:
+            raise ValidationError('A Product must have a ProductID')
+        if not self._name:
+            raise ValidationError('A Product must have a Name')
+
+    def __str__(self):
+        return "%s (%s)" % (self._productid, self._name)
+
+
+class CVRFRelationship(object):
+    TYPES = ('Default Component Of', 'Optional Component Of',
+             'External Component Of', 'Installed On', 'Installed With')
+    def __init__(self, productref, reltype, relatestoproductref):
+        self._productreference = productref
+        self._relationtype = reltype
+        self._relatestoproductreference = relatestoproductref
+        self._product = None
+
+    def getParent(self):
+        """ All parent element of a FullProductName should implement that
+        method """
+        return None
+
+    def isOrphaned(self):
+        return self._product is None
+
+    def validate(self):
+        if not self._productreference:
+            raise ValidationError('A Relationship must have a Product Reference')
+        if not self._relationtype:
+            raise ValidationError('A Relationship must have a Relation Type')
+        if self._relationtype not in self.TYPES:
+            raise ValidationError('Relation Type must be one of %s' % ', '.join(self.TYPES))
+        if not self._relatestoproductreference:
+            raise ValidationError('A Relationship must have a "Relates To product Reference"')
+        if self._productreference == self._relatestoproductreference:
+            raise ValidationError('A Relationship cannot reference twice the same Product')
+
+
+class CVRFGroup(object):
+    def __init__(self, groupid):
+        self._groupid = groupid
+        self._description = None
+        self._productids = []
+
+    def setDescription(self, description):
+        self._description = description
+
+    def addProductID(self, productid):
+        self._productids.append(productid)
+
+    def getTitle(self):
+        if self._description:
+            return "%s (%d products)" % (self._description, len(self._productids))
+        return "#%s (%d products)" % (self._groupid, len(self._productids))
+
+    def validate(self):
+        if not self._groupid:
+            raise ValidationError('A Group must have a GroupID')
+        if not self._productids or len(self._productids) < 2:
+            raise ValidationError('A Group must contain at least two products')
+
--- a/farolluz/renderer.py	Fri Oct 24 16:43:31 2014 +0200
+++ b/farolluz/renderer.py	Fri Oct 24 17:01:26 2014 +0200
@@ -25,8 +25,6 @@
 from __future__ import print_function
 
 import os
-import sys
-from datetime import datetime
 import jinja2
 
 from .parsers import cvrf
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/farolluz/vulnerability.py	Fri Oct 24 17:01:26 2014 +0200
@@ -0,0 +1,394 @@
+# -*- coding: utf-8 -*-
+#
+# Authors:
+# Benoît Allard <benoit.allard@greenbone.net>
+#
+# Copyright:
+# Copyright (C) 2014 Greenbone Networks GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+
+"""\
+Vulnerability Objects related to CVRF Documents
+"""
+
+from .common import ValidationError
+from .document import CVRFPublisher
+
+
+class CVRFVulnerabilityID(object):
+    def __init__(self, systemname, value):
+        self._systemname = systemname
+        self._value = value
+
+    def validate(self):
+        if not self._systemname:
+            raise ValidationError('A Vulnerability ID must have a System Name')
+        if not self._value:
+            raise ValidationError('A Vulnerability ID must have a value')
+
+
+class CVRFVulnerability(object):
+    def __init__(self, ordinal):
+        self._ordinal = ordinal
+        self._title = None
+        self._id = None
+        self._notes = []
+        self._discoverydate = None
+        self._releasedate = None
+        self._involvements = []
+        self._cve = None
+        self._cwes = []
+        self._productstatuses = []
+        self._threats = []
+        self._cvsss = []
+        self._remediations = []
+        self._references = []
+        self._acknowledgments = []
+
+    def setTitle(self, title):
+        self._title = title
+
+    def setID(self, _id):
+        self._id = _id
+
+    def addNote(self, note):
+        self._notes.append(note)
+
+    def setDiscoveryDate(self, date):
+        self._discoverydate = date
+
+    def setReleaseDate(self, date):
+        self._releasedate = date
+
+    def addInvolvement(self, involvement):
+        self._involvements.append(involvement)
+
+    def setCVE(self, cve):
+        self._cve = cve
+
+    def addCWE(self, cwe):
+        self._cwes.append(cwe)
+
+    def addProductStatus(self, productstatus):
+        self._productstatuses.append(productstatus)
+
+    def addThreat(self, threat):
+        self._threats.append(threat)
+
+    def addCVSSSet(self, cvss_set):
+        self._cvsss.append(cvss_set)
+
+    def addRemediation(self, remediation):
+        self._remediations.append(remediation)
+
+    def addReference(self, ref):
+        self._references.append(ref)
+
+    def addAcknowledgment(self, ack):
+        self._acknowledgments.append(ack)
+
+    def getTitle(self):
+        """ return something that can be used as a title """
+        if self._title:
+            if self._id:
+                return "%s (%s)" % (self._title, self._id._value)
+            return self._title
+        if self._id:
+            return self._id._value
+        return "#%d" % self._ordinal
+
+    def getNote(self, ordinal):
+        for note in self._notes:
+            if note._ordinal == ordinal:
+                return note
+        return None
+
+    def mentionsProdId(self, productid):
+        """ Returns in which sub element, self is mentioning the productid """
+        for category in (self._productstatuses, self._threats, self._cvsss, self._remediations):
+            for subelem in category:
+                if productid in subelem._productids:
+                    yield subelem
+
+    def isMentioningProdId(self, productid):
+        """ Returns if self is mentioning the productid """
+        for e in self.mentionsProdId(productid):
+            # We only need to know if the generator yield at least one elem.
+            return True
+        return False
+
+    def mentionsGroupId(self, groupid):
+        for category in (self._threats, self._remediations):
+            for subelem in category:
+                if groupid in subelem._groupids:
+                    yield subelem
+
+    def isMentioningGroupId(self, groupids):
+        """ Make sure you call this with a list (not a generator or a tuple)
+        when wished """
+        if not isinstance(groupids, list):
+            groupids = [groupids]
+        for groupid in groupids:
+            for _ in self.mentionsGroupId(groupid):
+                # We only need to know if the generator yield at least one elem.
+                return True
+        return False
+
+    def validate(self, productids, groupids):
+        if not self._ordinal:
+            raise ValidationError('A Vulnerability must have an ordinal')
+        if self._id is not None:
+            self._id.validate()
+        ordinals = set()
+        for note in self._notes:
+            note.validate()
+            if note._ordinal in ordinals:
+                raise ValidationError('Vulnerability Note Ordinal %d duplicated' % note._ordinal)
+            ordinals.add(note._ordinal)
+        for involvement in self._involvements:
+            involvement.validate()
+        for cwe in self._cwes:
+            cwe.validate()
+        for status in self._productstatuses:
+            status.validate(productids)
+        pids = set()
+        for status in self._productstatuses:
+            for pid in status._productids:
+                if pid in pids:
+                    raise ValidationError('ProductID %s mentionned in two different ProductStatuses for Vulnerability %d' % (pid, self._ordinal))
+                pids.add(pid)
+        for threat in self._threats:
+            threat.validate(productids, groupids)
+        for cvss in self._cvsss:
+            cvss.validate(productids)
+        pids = set()
+        for cvss in self._cvsss:
+            for pid in (cvss._productids or productids):
+                if pid in pids:
+                    raise ValidationError('ProductID %s mentionned in two different CVSS Score Sets for Vulnerability %d' % (pid, self._ordinal))
+                pids.add(pid)
+        for remediation in self._remediations:
+            remediation.validate(productids, groupids)
+        for reference in self._references:
+            reference.validate()
+        for acknowledgment in self._acknowledgments:
+            acknowledgment.validate()
+
+
+class CVRFInvolvement(object):
+    PARTIES = CVRFPublisher.TYPES
+    STATUSES = ('Open', 'Disputed', 'In Progress', 'Completed',
+                'Contact Attempted', 'Not Contacted')
+    def __init__(self, party, status):
+        self._party = party
+        self._status = status
+        self._description = None
+
+    def setDescription(self, description):
+        self._description = description
+
+    def getTitle(self):
+        return "From %s: %s" % (self._party, self._status)
+
+    def validate(self):
+        if not self._party:
+            raise ValidationError('An Involvement must have a Party')
+        if self._party not in self.PARTIES:
+            raise ValidationError("An Involvement's Party must be one of %s" % ', '.join(self.PARTIES))
+        if not self._status:
+            raise ValidationError('An Involvement must have a Status')
+        if self._status not in self.STATUSES:
+            raise ValidationError("An Involvement's Status must be one of %s" % ', '.join(self.STATUSES))
+
+
+class CVRFCWE(object):
+    def __init__(self, _id, value):
+        self._id = _id
+        self._value = value
+
+    def validate(self):
+        if not self._id:
+            raise ValidationError('A CWE must have an ID')
+        if not self._value:
+            raise ValidationError('A CWE must have a description')
+
+
+class CVRFProductStatus(object):
+    TYPES = ('First Affected', 'Known Affected', 'Known Not Affected',
+             'First Fixed', 'Fixed', 'Recommended', 'Last Affected')
+    NAME = "Product Status"
+    def __init__(self, _type):
+        self._type = _type
+        self._productids = []
+
+    def addProductID(self, productid):
+        self._productids.append(productid)
+
+    def getTitle(self):
+        return "%s: %d products" % (self._type, len(self._productids))
+
+    def validate(self, productids):
+        if not self._type:
+            raise ValidationError('A Product Status must have a Type')
+        if self._type not in self.TYPES:
+            raise ValidationError("A Product Status' Type must be one of %s" % ', '.join(self.TYPES))
+        if len(self._productids) < 1:
+            raise ValidationError('A Product Status must mention at least one Product')
+        for productid in self._productids:
+            if productid not in productids:
+                raise ValidationError('Unknown ProductID: %s' % productid)
+
+
+class CVRFThreat(object):
+    TYPES = ('Impact', 'Exploit Status', 'Target Set')
+    NAME = "Threat"
+    def __init__(self, _type, description):
+        self._type = _type
+        self._description = description
+        self._date = None
+        self._productids = []
+        self._groupids = []
+
+    def setDate(self, date):
+        self._date = date
+
+    def addProductID(self, productid):
+        self._productids.append(productid)
+
+    def addGroupID(self, groupid):
+        self._groupids.append(groupid)
+
+    def getTitle(self):
+        return self._type
+
+    def validate(self, productids, groupids):
+        if not self._type:
+            raise ValidationError('A Threat must have a Type')
+        if self._type not in self.TYPES:
+            raise ValidationError("A Threat's Type must be one of %s" % ', '.join(self.TYPES))
+        if not self._description:
+            raise ValidationError('A Threat must have a Description')
+        for productid in self._productids:
+            if productid not in productids:
+                raise ValidationError('Unknown ProductID: %s' % productid)
+        for groupid in self._groupids:
+            if groupid not in groupids:
+                raise ValidationError('Unknown GroupID: %s' % groupid)
+
+
+class CVRFCVSSSet(object):
+    # To determine the base Score
+    VALUES = {'AV': {'L':0.395, 'A':0.646, 'N':1.0},
+              'AC': {'H':0.35, 'M':0.61 ,'L':0.71},
+              'Au': {'M':0.45, 'S':0.56, 'N':0.704},
+              'C': {'N':0.0, 'P':0.275, 'C':0.66},
+              'I': {'N':0.0, 'P':0.275, 'C':0.66},
+              'A': {'N':0.0, 'P':0.275, 'C':0.66}}
+    NAME = "CVSS Score Set"
+    def __init__(self, basescore):
+        self._basescore = basescore
+        self._temporalscore = None
+        self._environmentalscore = None
+        self._vector = None
+        self.vector = None
+        self._productids = []
+
+    def setTemporalScore(self, tempscore):
+        self._temporalscore = tempscore
+
+    def setEnvironmentalScore(self, envscore):
+        self._environmentalscore = envscore
+
+    def setVector(self, vector):
+        self._vector = vector
+        if vector is None:
+            self.vector = vector
+            return
+        try:
+            self.vector = {}
+            for component in vector[:26].split('/'):
+                name, value = component.split(':')
+                self.vector[name] = self.VALUES[name][value]
+        except (KeyError, ValueError):
+            self.vector = None
+
+    def addProductID(self, productid):
+        self._productids.append(productid)
+
+    def baseScore(self):
+        v = self.vector # make an alias for shorter lines
+        exploitability = 20 * v['AV'] * v['AC'] * v['Au']
+        impact = 10.41 * (1 - (1 - v['C']) * (1 - v['I']) * (1 - v['A']))
+        def f(i): return 0 if i == 0 else 1.176
+        return ((0.6 * impact) + (0.4 * exploitability) - 1.5) * f(impact)
+
+    def validate(self, productids):
+        if not self._basescore:
+            raise ValidationError('A CVSS Score Set must have a Base Score')
+        if self._vector and not self.vector:
+            raise ValidationError('Syntax Error in CVSS Vector')
+        if self.vector and (abs(self._basescore - self.baseScore()) >= 0.05):
+            raise ValidationError('Inconsistency in CVSS Score Set between Vector (%f) and Base Score (%f)' % (self.baseScore(), self._basescore))
+        for productid in self._productids:
+            if productid not in productids:
+                raise ValidationError('Unknown ProductID: %s' % productid)
+
+
+class CVRFRemediation(object):
+    TYPES = ('Workaround', 'Mitigation', 'Vendor Fix', 'None Available',
+             'Will Not Fix')
+    NAME = "Remediation"
+    def __init__(self, _type, description):
+        self._type = _type
+        self._description = description
+        self._date = None
+        self._entitlement = None
+        self._url = None
+        self._productids = []
+        self._groupids = []
+
+    def setDate(self, date):
+        self._date = date
+
+    def setEntitlement(self, entitlement):
+        self._entitlement = entitlement
+
+    def setURL(self, url):
+        self._url = url
+
+    def addProductID(self, productid):
+        self._productids.append(productid)
+
+    def addGroupID(self, groupid):
+        self._groupids.append(groupid)
+
+    def getTitle(self):
+        return self._type
+
+    def validate(self, productids, groupids):
+        if not self._type:
+            raise ValidationError('A Remediation must have a Type')
+        if self._type not in self.TYPES:
+            raise ValidationError("A Remediation's Type must be one of %s" % ', '.join(self.TYPES))
+        if not self._description:
+            raise ValidationError('A Remediation must have a Description')
+        for productid in self._productids:
+            if productid not in productids:
+                raise ValidationError('Unknown ProductID: %s' % productid)
+        for groupid in self._groupids:
+            if groupid not in groupids:
+                raise ValidationError('Unknown GroupID: %s' % groupid)
+
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)