benoit@43: # -*- coding: utf-8 -*- benoit@43: # Description: benoit@43: # Methods for parsing CVE XML documents benoit@43: # benoit@43: # Authors: benoit@43: # BenoƮt Allard benoit@43: # benoit@43: # Copyright: benoit@43: # Copyright (C) 2014 Greenbone Networks GmbH benoit@43: # benoit@43: # This program is free software; you can redistribute it and/or benoit@43: # modify it under the terms of the GNU General Public License benoit@43: # as published by the Free Software Foundation; either version 2 benoit@43: # of the License, or (at your option) any later version. benoit@43: # benoit@43: # This program is distributed in the hope that it will be useful, benoit@43: # but WITHOUT ANY WARRANTY; without even the implied warranty of benoit@43: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the benoit@43: # GNU General Public License for more details. benoit@43: # benoit@43: # You should have received a copy of the GNU General Public License benoit@43: # along with this program; if not, write to the Free Software benoit@43: # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. benoit@43: benoit@43: """\ benoit@43: Methods for parsing of CVE XML Documents benoit@43: benoit@43: Ref: http://scap.nist.gov/schema/vulnerability/0.4 benoit@43: """ benoit@43: benoit@43: from __future__ import absolute_import benoit@43: benoit@43: import xml.etree.ElementTree as ET benoit@43: benoit@43: from .xml import parseDate benoit@43: benoit@43: from ..common import CVRFNote, CVRFReference benoit@43: from ..document import CVRF, CVRFPublisher, CVRFTracking, CVRFTrackingID, CVRFRevision benoit@43: from ..producttree import CVRFFullProductName benoit@43: from ..utils import utcnow benoit@43: from ..vulnerability import CVRFVulnerability, CVRFCVSSSet, CVRFCWE, CVRFProductStatus benoit@43: benoit@43: NAMESPACES = { benoit@43: 'cve': "http://scap.nist.gov/schema/feed/vulnerability/2.0", benoit@43: 'vuln': "http://scap.nist.gov/schema/vulnerability/0.4", benoit@43: 'cvss': "http://scap.nist.gov/schema/cvss-v2/0.2", benoit@43: 'xml': "http://www.w3.org/XML/1998/namespace", benoit@43: } benoit@43: benoit@43: benoit@43: def UN(ns, name): benoit@43: """ returns a Universal Name """ benoit@43: return "{%s}%s" % (NAMESPACES[ns], name) benoit@43: benoit@43: def parseCVSS(xmlElem): benoit@43: """ Make a vector out of a list of elements """ benoit@43: def get(name): benoit@43: return xmlElem.findtext('/'.join([UN('cvss', 'base_metrics'), UN('cvss', name)])) benoit@43: benoit@43: cvss_set = CVRFCVSSSet(float(get('score'))) benoit@43: vector = [ benoit@43: 'AV:%s' % {'LOCAL': 'L', benoit@43: 'ADJACENT_NETWORK': 'A', benoit@43: 'NETWORK': 'N'}[get('access-vector')], benoit@43: 'AC:%s' % {'HIGH': 'H', benoit@43: 'MEDIUM': 'M', benoit@43: 'LOW': 'L'}[get('access-complexity')], benoit@43: 'Au:%s' % {'MULTIPLE': 'M', benoit@43: 'SINGLE': 'S', benoit@43: 'NONE': 'N'}[get('authentication')], benoit@43: 'C:%s' % {'NONE': 'N', benoit@43: 'PARTIAL': 'P', benoit@43: 'COMPLETE': 'C'}[get('confidentiality-impact')], benoit@43: 'I:%s' % {'NONE': 'N', benoit@43: 'PARTIAL': 'P', benoit@43: 'COMPLETE': 'C'}[get('integrity-impact')], benoit@43: 'A:%s' % {'NONE': 'N', benoit@43: 'PARTIAL': 'P', benoit@43: 'COMPLETE': 'C'}[get('availability-impact')], benoit@43: ] benoit@43: cvss_set.setVector('/'.join(vector)) benoit@43: return cvss_set benoit@43: benoit@43: def parseXML(data): benoit@43: """ returns am ET.Element from the input stuff. benoit@43: input can be: benoit@43: - a string benoit@43: - a file handle benoit@43: - an ET.Element instance benoit@43: """ benoit@43: if isinstance(data, ET.Element): benoit@43: return data benoit@43: # To allow passing file handles benoit@43: if hasattr(data, 'read'): benoit@43: data = data.read() benoit@43: # Parse it. benoit@43: return ET.fromstring(data) benoit@43: benoit@43: def parse_CVE_from_GSA(data): benoit@43: xml = parseXML(data) benoit@43: return parse(xml.find('/'.join(['get_info', 'get_info_response', 'info', 'cve', 'raw_data', UN('cve', 'entry')]))) benoit@43: benoit@43: def parse(xml): benoit@43: xml = parseXML(xml) benoit@43: benoit@43: # Create an extra-minimal document benoit@43: doc = CVRF(xml.findtext(UN('vuln', 'cve-id')), benoit@43: 'Vulnerability Description') benoit@43: pub = CVRFPublisher("Other") benoit@43: doc.setPublisher(pub) benoit@43: now = utcnow() benoit@43: tracking = CVRFTracking( benoit@43: CVRFTrackingID('000000'), benoit@43: "Draft", benoit@43: (0,), benoit@43: now, now benoit@43: ) benoit@43: doc.setTracking(tracking) benoit@43: tracking.addRevision(CVRFRevision((0,), now, 'Document created')) benoit@43: benoit@43: # Add the CVE to that document benoit@43: return addToDoc(doc, xml) benoit@43: benoit@43: def addToDoc(doc, xml): benoit@43: """ Adds the CVE as vulnerability in the document """ benoit@43: xml = parseXML(xml) benoit@43: benoit@43: vulnid = xml.attrib['id'] benoit@43: benoit@43: # Get a new ordinal for our new Vulnerability benoit@43: if len(doc._vulnerabilities) == 0: benoit@43: ordinal = 1 benoit@43: else: benoit@43: ordinal = doc._vulnerabilities[-1]._ordinal + 1 benoit@43: benoit@43: # Create a Vulnerability benoit@43: vuln = CVRFVulnerability(ordinal) benoit@43: doc.addVulnerability(vuln) benoit@43: benoit@43: vulnerable_products = [] benoit@43: # Set the vulnerable products in productTree benoit@43: for i, cpe in enumerate(xml.findall( benoit@43: '/'.join([UN('vuln', 'vulnerable-software-list'), benoit@43: UN('vuln', 'product')]))): benoit@43: if doc._producttree is None: benoit@43: doc.createProductTree() benoit@43: try: benoit@43: prod = doc._producttree.getProductForCPE(cpe.text) benoit@43: except KeyError: benoit@43: prod = CVRFFullProductName('%s-P%d' % (vulnid, i), cpe.text, doc._producttree, cpe.text) benoit@43: doc._producttree.addProduct(prod) benoit@43: vulnerable_products.append(prod) benoit@43: benoit@43: if vulnerable_products: benoit@43: status = CVRFProductStatus('Known Affected') benoit@43: for product in vulnerable_products: benoit@43: status.addProductID(product._productid) benoit@43: vuln.addProductStatus(status) benoit@43: benoit@43: # Add the CVE-id benoit@43: vuln.setCVE(xml.findtext(UN('vuln', 'cve-id'))) benoit@43: benoit@43: # The release date benoit@43: vuln.setReleaseDate(parseDate(xml.findtext(UN('vuln', 'published-datetime')))) benoit@43: benoit@43: # Add the CVSS benoit@43: xmlcvss = xml.find(UN('vuln', 'cvss')) benoit@43: if xmlcvss is not None: benoit@43: vuln.addCVSSSet(parseCVSS(xmlcvss)) benoit@43: benoit@43: # Add the CWE id benoit@43: xmlcwe = xml.find(UN('vuln', 'cwe')) benoit@43: if xmlcwe is not None: benoit@43: # XXX: Get a Description for the CWE ! benoit@43: vuln.addCWE(CVRFCWE(xmlcwe.attrib['id'], xmlcwe.attrib['id'])) benoit@43: benoit@43: # Add references benoit@43: for xmlref in xml.findall(UN('vuln', 'references')): benoit@43: vuln.addReference(CVRFReference(xmlref.find(UN('vuln','reference')).attrib['href'], benoit@43: xmlref.findtext(UN('vuln', 'reference')))) benoit@43: benoit@43: xmlsummary = xml.findtext(UN('vuln', 'summary')) benoit@43: if xmlsummary is not None: benoit@43: vuln.addNote(CVRFNote( benoit@43: 'Summary', benoit@43: 1, benoit@43: xmlsummary benoit@43: )) benoit@43: benoit@43: return doc