[openssl-commits] [web] master update

Mark J. Cox mark at openssl.org
Mon Jan 29 11:15:48 UTC 2018


The branch master has been updated
       via  a0ccfe09df6a7a59a610c40e2f0e17065878e077 (commit)
      from  22e17fc35d35640a6aaa98080ebeae14833e5a37 (commit)


- Log -----------------------------------------------------------------
commit a0ccfe09df6a7a59a610c40e2f0e17065878e077
Author: Mark J. Cox <mark at awe.com>
Date:   Mon Jan 29 11:14:25 2018 +0000

    Add a script to convert our vulnerabilities.xml file to json
    as per Mitre CVE JSON format, and validate it.  We'll use this
    for submitting our CVE updates to Mitre (and we may use change the
    creation of the web site pages to use a similar script in future
    as the xslt we currently use is a little esoteric)

-----------------------------------------------------------------------

Summary of changes:
 bin/vulnxml2json.py | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 151 insertions(+)
 create mode 100755 bin/vulnxml2json.py

diff --git a/bin/vulnxml2json.py b/bin/vulnxml2json.py
new file mode 100755
index 0000000..41afbf8
--- /dev/null
+++ b/bin/vulnxml2json.py
@@ -0,0 +1,151 @@
+#! /usr/bin/python
+#
+# Convert our XML file to a JSON file as accepted by Mitre for CNA purposes
+# as per https://github.com/CVEProject/automation-working-group/blob/master/cve_json_schema/DRAFT-JSON-file-format-v4.md
+#
+
+from xml.dom import minidom
+import simplejson as json
+import codecs
+import re
+from optparse import OptionParser
+
+# for validation
+import json
+import jsonschema
+from jsonschema import validate
+from jsonschema import Draft4Validator
+import urllib
+
+# Versions of OpenSSL we never released, to allow us to display ranges
+neverreleased = "1.0.0h,";
+
+# Location of CVE JSON schema (default, can use local file etc)
+default_cve_schema = "https://raw.githubusercontent.com/CVEProject/automation-working-group/master/cve_json_schema/CVE_JSON_4.0_min_public.schema"
+
+def merge_affects(issue):
+    # let's merge the affects into a nice list which is better for Mitre text but we have to take into account our stange lettering scheme
+    prev = ""
+    anext = ""
+    alist = list()
+    vlist = list()
+    for affects in issue.getElementsByTagName('affects'): # so we can sort them
+       vlist.append(affects.getAttribute("version"))
+    for ver in sorted(vlist):
+       # print "version %s (last was %s, next was %s)" %(ver,prev,anext)
+       if (ver != anext):
+          alist.append([ver])
+       elif len(alist[-1]) > 1:
+          alist[-1][-1] = ver
+       else:
+          alist[-1].append(ver)
+       prev = ver
+       if (unicode.isdigit(ver[-1])):   # First version after 1.0.1 is 1.0.1a
+           anext = ver + "a"
+       elif (ver[-1] == "y"):
+           anext = ver[:-1] + "za"    # We ran out of letters once so y->za->zb....
+       else:
+           anext = ver[:-1]+chr(ord(ver[-1])+1) # otherwise after 1.0.1a is 1.0.1b
+       while (anext in neverreleased): # skip unreleased versions
+          anext = anext[:-1]+chr(ord(anext[-1])+1)
+
+    return ",".join(['-'.join(map(str,aff)) for aff in alist])
+        
+parser = OptionParser()
+parser.add_option("-s", "--schema", help="location of schema to check (default "+default_cve_schema+")", default=default_cve_schema,dest="schema")
+parser.add_option("-i", "--input", help="input vulnerability file live openssl-web/news/vulnerabilities.xml", dest="input")
+parser.add_option("-c", "--cve", help="comma separated list of cve names to generate a json file for (or all)", dest="cves")
+parser.add_option("-o", "--outputdir", help="output directory for json file (default ./)", default=".", dest="outputdir")
+(options, args) = parser.parse_args()
+
+if not options.input:
+   print "needs input file"
+   parser.print_help()
+   exit();
+
+if options.schema:
+   response = urllib.urlopen(options.schema)
+   schema_doc = json.loads(response.read())
+
+cvej = list()
+    
+with codecs.open(options.input,"r","utf-8") as vulnfile:
+    vulns = vulnfile.read()
+dom = minidom.parseString(vulns.encode("utf-8"))
+issues = dom.getElementsByTagName('issue')
+for issue in issues:
+    cve = issue.getElementsByTagName('cve')[0].getAttribute('name')
+    if (cve == ""):
+       continue
+    if (options.cves):
+       if (not cve in options.cves):
+          continue
+    cve = dict()
+    cve['data_type']="CVE"
+    cve['data_format']="MITRE"
+    cve['data_version']="4.0"
+    cve['CVE_data_meta']= { "ID": "CVE-"+issue.getElementsByTagName('cve')[0].getAttribute('name'), "ASSIGNER": "openssl-security at openssl.org", "STATE":"PUBLIC" }
+    datepublic = issue.getAttribute("public")
+    cve['CVE_data_meta']['DATE_PUBLIC'] = datepublic[:4]+'-'+datepublic[4:6]+'-'+datepublic[6:8]
+    if issue.getElementsByTagName('title'):
+        cve['CVE_data_meta']['TITLE'] = issue.getElementsByTagName('title')[0].childNodes[0].nodeValue.strip()            
+    desc = issue.getElementsByTagName('description')[0].childNodes[0].nodeValue.strip()
+    problemtype = "(undefined)"
+    if issue.getElementsByTagName('problemtype'):
+        problemtype = issue.getElementsByTagName('problemtype')[0].childNodes[0].nodeValue.strip()    
+    cve['problemtype'] = { "problemtype_data": [ { "description" : [ { "lang":"eng", "value": problemtype} ] } ] }
+    impact = issue.getElementsByTagName('impact')
+    if impact:
+        cve['impact'] = [ { "lang":"eng", "value":impact[0].getAttribute('severity'), "url":"https://www.openssl.org/policies/secpolicy.html#"+impact[0].getAttribute('severity') } ]
+    for reported in issue.getElementsByTagName('reported'):
+        cve['credit'] = [ { "lang":"eng", "value":reported.getAttribute("source")} ]
+    refs = list()
+    for adv in issue.getElementsByTagName('advisory'):
+       url = adv.getAttribute("url")
+       if (not url.startswith("htt")):
+          url = "https://www.openssl.org"+url
+       refs.append({"url":url})
+    for git in issue.getElementsByTagName('git'):
+       refs.append({"url":"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h="+git.getAttribute("hash")})
+    if refs:
+        cve['references'] = { "reference_data": refs  }
+
+    allaffects = list()
+    for affects in issue.getElementsByTagName('affects'):
+        allaffects.append({ "version_value":"openssl-"+affects.getAttribute("version")})
+
+    cve['affects'] = { "vendor" : { "vendor_data" : [ { "vendor_name": "OpenSSL", "product": { "product_data" : [ { "product_name": "OpenSSL", "version": { "version_data" : allaffects}}]}}]}}
+
+    # Mitre want the fixed/affected versions in the text too
+    
+    desc += " (Affects "+merge_affects(issue)+")."
+        
+    # Mitre want newlines and excess spaces stripped
+
+    desc = re.sub('[\n ]+',' ', desc)
+        
+    cve['description'] = { "description_data": [ { "lang":"eng", "value": desc} ] }
+    cvej.append(cve)
+        
+
+for issue in cvej:
+    fn = issue['CVE_data_meta']['ID'] + ".json"
+    if not issue:
+       continue
+
+    f = codecs.open(options.outputdir+"/"+fn, 'w', 'utf-8')
+    f.write(json.dumps(issue, sort_keys=True, indent=4))
+    print "wrote %s" %(options.outputdir+"/"+fn)
+    f.close()
+
+    try:
+       validate(issue, schema_doc)
+       print "%s passed validation" % (fn)
+    except jsonschema.exceptions.ValidationError as incorrect:
+       v = Draft4Validator(schema_doc)
+       errors = sorted(v.iter_errors(issue), key=lambda e: e.path)
+       for error in errors:
+          print "%s did not pass validation: %s" % (fn,str(error.message))
+    except NameError:
+       print "%s skipping validation, no schema defined" %(fn)
+       


More information about the openssl-commits mailing list