Mercurial > farol > farolluz
diff farolluz/parsers/cve.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 | |
children | b7e64d0a3a7c |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/farolluz/parsers/cve.py Mon Dec 29 16:33:34 2014 +0100 @@ -0,0 +1,190 @@ +# -*- coding: utf-8 -*- +# Description: +# Methods for parsing CVE XML documents +# +# 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. + +"""\ +Methods for parsing of CVE XML Documents + +Ref: http://scap.nist.gov/schema/vulnerability/0.4 +""" + +from __future__ import absolute_import + +import xml.etree.ElementTree as ET + +from .xml import parseDate + +from ..common import CVRFNote, CVRFReference +from ..document import CVRF, CVRFPublisher, CVRFTracking, CVRFTrackingID, CVRFRevision +from ..producttree import CVRFFullProductName +from ..utils import utcnow +from ..vulnerability import CVRFVulnerability, CVRFCVSSSet, CVRFCWE, CVRFProductStatus + +NAMESPACES = { + 'cve': "http://scap.nist.gov/schema/feed/vulnerability/2.0", + 'vuln': "http://scap.nist.gov/schema/vulnerability/0.4", + 'cvss': "http://scap.nist.gov/schema/cvss-v2/0.2", + 'xml': "http://www.w3.org/XML/1998/namespace", +} + + +def UN(ns, name): + """ returns a Universal Name """ + return "{%s}%s" % (NAMESPACES[ns], name) + +def parseCVSS(xmlElem): + """ Make a vector out of a list of elements """ + def get(name): + return xmlElem.findtext('/'.join([UN('cvss', 'base_metrics'), UN('cvss', name)])) + + cvss_set = CVRFCVSSSet(float(get('score'))) + vector = [ + 'AV:%s' % {'LOCAL': 'L', + 'ADJACENT_NETWORK': 'A', + 'NETWORK': 'N'}[get('access-vector')], + 'AC:%s' % {'HIGH': 'H', + 'MEDIUM': 'M', + 'LOW': 'L'}[get('access-complexity')], + 'Au:%s' % {'MULTIPLE': 'M', + 'SINGLE': 'S', + 'NONE': 'N'}[get('authentication')], + 'C:%s' % {'NONE': 'N', + 'PARTIAL': 'P', + 'COMPLETE': 'C'}[get('confidentiality-impact')], + 'I:%s' % {'NONE': 'N', + 'PARTIAL': 'P', + 'COMPLETE': 'C'}[get('integrity-impact')], + 'A:%s' % {'NONE': 'N', + 'PARTIAL': 'P', + 'COMPLETE': 'C'}[get('availability-impact')], + ] + cvss_set.setVector('/'.join(vector)) + return cvss_set + +def parseXML(data): + """ returns am ET.Element from the input stuff. + input can be: + - a string + - a file handle + - an ET.Element instance + """ + if isinstance(data, ET.Element): + return data + # To allow passing file handles + if hasattr(data, 'read'): + data = data.read() + # Parse it. + return ET.fromstring(data) + +def parse_CVE_from_GSA(data): + xml = parseXML(data) + return parse(xml.find('/'.join(['get_info', 'get_info_response', 'info', 'cve', 'raw_data', UN('cve', 'entry')]))) + +def parse(xml): + xml = parseXML(xml) + + # Create an extra-minimal document + doc = CVRF(xml.findtext(UN('vuln', 'cve-id')), + 'Vulnerability Description') + pub = CVRFPublisher("Other") + doc.setPublisher(pub) + now = utcnow() + tracking = CVRFTracking( + CVRFTrackingID('000000'), + "Draft", + (0,), + now, now + ) + doc.setTracking(tracking) + tracking.addRevision(CVRFRevision((0,), now, 'Document created')) + + # Add the CVE to that document + return addToDoc(doc, xml) + +def addToDoc(doc, xml): + """ Adds the CVE as vulnerability in the document """ + xml = parseXML(xml) + + vulnid = xml.attrib['id'] + + # Get a new ordinal for our new Vulnerability + if len(doc._vulnerabilities) == 0: + ordinal = 1 + else: + ordinal = doc._vulnerabilities[-1]._ordinal + 1 + + # Create a Vulnerability + vuln = CVRFVulnerability(ordinal) + doc.addVulnerability(vuln) + + vulnerable_products = [] + # Set the vulnerable products in productTree + for i, cpe in enumerate(xml.findall( + '/'.join([UN('vuln', 'vulnerable-software-list'), + UN('vuln', 'product')]))): + if doc._producttree is None: + doc.createProductTree() + try: + prod = doc._producttree.getProductForCPE(cpe.text) + except KeyError: + prod = CVRFFullProductName('%s-P%d' % (vulnid, i), cpe.text, doc._producttree, cpe.text) + doc._producttree.addProduct(prod) + vulnerable_products.append(prod) + + if vulnerable_products: + status = CVRFProductStatus('Known Affected') + for product in vulnerable_products: + status.addProductID(product._productid) + vuln.addProductStatus(status) + + # Add the CVE-id + vuln.setCVE(xml.findtext(UN('vuln', 'cve-id'))) + + # The release date + vuln.setReleaseDate(parseDate(xml.findtext(UN('vuln', 'published-datetime')))) + + # Add the CVSS + xmlcvss = xml.find(UN('vuln', 'cvss')) + if xmlcvss is not None: + vuln.addCVSSSet(parseCVSS(xmlcvss)) + + # Add the CWE id + xmlcwe = xml.find(UN('vuln', 'cwe')) + if xmlcwe is not None: + # XXX: Get a Description for the CWE ! + vuln.addCWE(CVRFCWE(xmlcwe.attrib['id'], xmlcwe.attrib['id'])) + + # Add references + for xmlref in xml.findall(UN('vuln', 'references')): + vuln.addReference(CVRFReference(xmlref.find(UN('vuln','reference')).attrib['href'], + xmlref.findtext(UN('vuln', 'reference')))) + + xmlsummary = xml.findtext(UN('vuln', 'summary')) + if xmlsummary is not None: + vuln.addNote(CVRFNote( + 'Summary', + 1, + xmlsummary + )) + + return doc