Mercurial > farol > farolluz
diff farolluz/vulnerability.py @ 26:809db989cac5
Reorganize the code in smaller mpodules
author | Benoît Allard <benoit.allard@greenbone.net> |
---|---|
date | Fri, 24 Oct 2014 17:01:26 +0200 |
parents | farolluz/cvrf.py@4004b67216a9 |
children | e317097af486 |
line wrap: on
line diff
--- /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) +