The xslt we use to convert the vulnerabilities.xml is clever, but esoteric, so
[openssl-web.git] / bin / mk-cvepage
diff --git a/bin/mk-cvepage b/bin/mk-cvepage
new file mode 100755 (executable)
index 0000000..26a4858
--- /dev/null
@@ -0,0 +1,114 @@
+#! /usr/bin/python
+#
+# Convert our XML file to a HTML file for the web page
+# let's replace vulnerabilities.xsl
+#
+
+from xml.dom import minidom
+import simplejson as json
+import codecs
+import re
+from optparse import OptionParser
+import datetime
+import sys
+
+# Versions of OpenSSL we never released, to allow us to display ranges, it's not a big deal if they
+# are not included here, it just makes things look better if they are.
+neverreleased = "1.0.0h,";
+
+def merge_affects(issue,base):
+    # 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
+       version = affects.getAttribute("version")
+       if (not base or base in version):
+           vlist.append(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("-i", "--input", help="input vulnerability file live openssl-web/news/vulnerabilities.xml", dest="input")
+parser.add_option("-o", "--outdir", help="output directory to write html files", dest="outdir")
+(options, args) = parser.parse_args()
+
+# We need an output directory not stdout because we might write multiple files
+if not options.input or not options.outdir:
+   print "needs input file and output directory"
+   parser.print_help()
+   exit();
+
+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')
+
+thisyear = ""
+allyears = []
+# Display issues latest by date first, if same date then by highest CVE
+allissues = ""
+for issue in sorted(issues, key=lambda x: (x.getAttribute('public'), x.getElementsByTagName('cve')[0].getAttribute('name')),reverse=True):
+    date = issue.getAttribute('public')
+    year = date[:-4]
+    if (year != thisyear):
+        if (thisyear != ""):
+            allissues += "</dl>";
+        allissues += "<h3><a name=\"y%s\">%s</a><dl>" %(year,year)
+        allyears.append(year)
+        thisyear = year
+    cve = issue.getElementsByTagName('cve')[0].getAttribute('name')
+
+    allissues += "<dt>"
+    if cve:
+        allissues += "<a href=\"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-%s\">CVE-%s</a> " %(cve,cve)
+    for adv in issue.getElementsByTagName('advisory'):
+        allissues += "<a href=\"%s\">(OpenSSL advisory)</a> " %(adv.getAttribute("url"))
+    for sev in issue.getElementsByTagName('impact'):
+        allissues += "<a href=\"/policies/secpolicy.html#%s\">[%s severity]</a> " %(sev.getAttribute('severity'),sev.getAttribute('severity'))
+    t = datetime.datetime(int(date[:4]), int(date[4:6]), int(date[6:8]), 0, 0)
+    allissues += t.strftime("%d %B %Y: ")
+
+    allissues += "<a href=\"#toc\"><img src=\"/img/up.gif\"/></a></dt><dd>"
+    allissues += issue.getElementsByTagName('description')[0].childNodes[0].nodeValue.strip()
+    for reported in issue.getElementsByTagName('reported'):
+       allissues += " Reported by %s. " %(reported.getAttribute("source"))
+    allissues += "<ul>"
+
+    for affects in issue.getElementsByTagName('fixed'):
+        allissues += "<li>Fixed in OpenSSL %s " %(affects.getAttribute('version'))
+        for git in affects.getElementsByTagName('git'):
+            allissues += "<a href=\"https://github.com/openssl/openssl/commit/%s\">(git commit)</a> " %(git.getAttribute('hash'))            
+        allissues += "(Affected "+merge_affects(issue,affects.getAttribute("base"))+")"       
+        allissues += "</li>"
+    allissues += "</ul></dd>"
+
+allissues += "</dl>"
+preface = "<!-- do not edit this file it is autogenerated, edit vulnerabilities.xml -->"
+preface += "<p><a name=\"toc\">Jump to year: </a>"
+preface += ", ".join( "<a href=\"#y%s\">%s</a>" %(year,year) for year in allyears)
+preface += "</p>"
+preface += allissues
+
+with codecs.open(options.outdir+"/vulnerabilities.html","w","utf-8") as htmlfile:
+    htmlfile.write(preface)