view farolluz/producttree.py @ 43:b87f2a6e613a

Add CVE parsing (from OpenVAS GSA)
author Benoît Allard <benoit.allard@greenbone.net>
date Mon, 29 Dec 2014 16:33:34 +0100
parents 809db989cac5
children 1b7f3f4f1238
line wrap: on
line source
# -*- 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 getProductForCPE(self, cpe):
        for product in self._products:
            if product._cpe == cpe:
                return product
        raise KeyError(cpe)

    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')
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)