| import json |
| import os |
| import re |
| import xml.sax.saxutils as saxutils |
| from optparse import OptionParser |
| parser = OptionParser() |
| |
| parser.add_option("-v","--version",help="major version to filter on",dest="filterversion") |
| parser.add_option("-e","--extratext",help="extra text to add to description",dest="extratext") |
| parser.add_option("-i","--inputdirectory",help="directory of json files",dest="directory") |
| (options,args) = parser.parse_args() |
| |
| |
| def natural_sort_key(s, _nsre=re.compile('([0-9]+)')): |
| return [int(text) if text.isdigit() else text.lower() |
| for text in _nsre.split(s)] |
| |
| filterversion = options.filterversion or "" |
| cves = [] |
| entries = {} |
| |
| for x in os.listdir(options.directory or "./"): |
| if x.endswith(".json"): |
| try: |
| fd = open(options.directory+x) |
| cve = json.load(fd) |
| cves.append(cve) |
| except: |
| print ("Ignoring due to error parsing: "+x) |
| continue |
| |
| # Filter on version and store by release(s) that fixed it |
| for cve in cves: |
| for time in cve["timeline"]: |
| timed = time["value"] |
| if ("release" in timed and timed.startswith(filterversion)): |
| fixedin = timed.split(" ")[0] |
| if (not fixedin in entries): |
| entries[fixedin] = [] |
| entries[fixedin].append(cve) |
| |
| # We want to sort on reverse number fixed, where our versions are dotted numbers, except some special cases |
| # like never-fixed, or -dev fixed which should always appear first |
| # Then we want to sort in the section by CVE name, handling numerics correctly |
| |
| lastfixedv = "" |
| productname = "" |
| sections = [] |
| for k,v in sorted(entries.items(), key=lambda s: [int(u) if u.isdigit() else 999 for u in s[0].split(',')[0].split('.')], reverse=True): |
| fixedv = k.split(",")[0] |
| |
| sectioncves = [] |
| for cve in sorted(v, key=lambda s: [int(u) if u.isdigit() else u for u in s["CVE_data_meta"]["ID"].split('-')]): |
| e = {} |
| e['cveid'] = cve["CVE_data_meta"]["ID"] |
| e['impact'] = cve["impact"][0]["other"] |
| e['title'] = cve["CVE_data_meta"]["TITLE"] |
| e['desc'] = cve["description"]["description_data"][0]["value"] |
| e['credit'] = "" |
| if ("credit" in cve): |
| e['credit'] = cve["credit"][0]["value"] |
| affects = [] |
| product = cve["affects"]["vendor"]["vendor_data"][0]["product"]["product_data"][0] |
| productname = product['product_name'] |
| for ver in product["version"]["version_data"]: |
| if (ver["version_affected"] == "="): |
| affects.append(ver["version_value"]) |
| elif (ver["version_affected"] == "?="): |
| # We did ?= for "maybe affects" because no one checked |
| affects.append(ver["version_value"]+"?") |
| else: |
| # Otherwise maybe we started doing things like "<2.7.8" |
| affects.append(ver["version_affected"]+ver["version_value"]) |
| # Make a natural order sort |
| affects.sort(reverse=True, key=natural_sort_key) |
| e['affects'] = ", ".join(affects) |
| e['timetable'] = []; |
| for time in cve["timeline"]: |
| timed = time["value"] |
| if ("reported" in timed): |
| timed = "Reported to security team" |
| elif ("public" in timed): |
| timed = "Issue public" |
| elif ("release" in timed): |
| timed = "Update "+timed |
| e['timetable'].append([timed,time["time"]]) |
| sectioncves.append(e) |
| sections.append({"cves":sectioncves,"fixed":fixedv,"product":productname}) |
| |
| # Everything is sorted and pretty, this should be some python template thing |
| |
| # We are generating html in markdown. Add the metadata first. |
| print ("Title: "+productname+" "+filterversion+" vulnerabilities") |
| print ("asf_headings: False") |
| print ("") |
| print ("<h1>"+productname+" "+filterversion+" vulnerabilities</h1>") |
| print ("<p>This page lists all security vulnerabilities fixed in released versions of "+productname+" "+filterversion+". Each vulnerability is given a security <a href=\"/security/impact_levels.html\">impact rating</a> by the Apache security team - please note that this rating may well vary from platform to platform. We also list the versions the flaw is known to affect, and where a flaw has not been verified list the version with a question mark.</p>") |
| print ("<p>Please note that if a vulnerability is shown below as being fixed in a \"-dev\" release then this means that a fix has been applied to the development source tree and will be part of an upcoming full release.</p>") |
| print ("<p>Please send comments or corrections for these vulnerabilities to the <a href=\"/security_report.html\">Security Team</a>.</p> <br/>") |
| |
| if (options.extratext): |
| print ("<p>"+options.extratext+"</p><br/>") |
| |
| for sectioncves in sections: |
| print ("\n<h1 id=\""+sectioncves["fixed"]+"\">Fixed in "+sectioncves["product"]+" "+sectioncves["fixed"]+"</h1><dl>\n") |
| for e in sectioncves["cves"]: |
| html = "<dt><h3 id=\""+e['cveid']+"\">"+e['impact']+": <name name=\""+e['cveid']+"\">"+saxutils.escape(e['title'])+"</name>\n"; |
| html += "(<a href=\"https://cve.mitre.org/cgi-bin/cvename.cgi?name="+e['cveid']+"\">"+e['cveid']+"</a>)</h3></dt>\n"; |
| desc = saxutils.escape(e['desc']) |
| desc = re.sub(r'\n','</p><p>', desc) |
| html += "<dd><p>"+desc+"</p>\n" |
| if (e['credit'] != ""): html += "<p>Acknowledgements: "+saxutils.escape(e['credit'])+"</p>\n" |
| html += "<table class=\"cve\">" |
| e['timetable'].append(["Affects",e['affects']]); |
| for ti in e['timetable']: |
| html+= "<tr><td class=\"cve-header\">"+ti[0]+"</td><td class=\"cve-value\">"+ti[1]+"</td></tr>\n" |
| html+= "</table></dd>" |
| print (html) |
| print ("</dl></br/>") |
| |
| |