#!/usr/bin/python2
import sys, getopt, os, glob
# TODO:
# Add code for updating a DTSA
# Include SHA-1 checksums in advisories
# Note: This has to be run inside secure-testing/data/DTSA/
# Prerequisites:
# subdirectories advs/plain-text, advs/html and templates
# Templates must include header.html and footer.html, but can be blank
# mailx package installed
announce_mail_address = "secure-testing-announce@lists.alioth.debian.org"
testing_name = "lenny"
stable_name = "etch"
oldstable_name = "sarge"
def print_usage():
print "dtsa [-p | -u] dtsa-id major number"
print " -p Process a new DTSA from a template"
print " -u Update an existing DTSA from a template"
sys.exit(-1)
def process_dtsa(id, sid):
filename=glob.glob("advs/" + id + "-*.adv")
src = ""
date = ""
vuln_type = ""
cve = ""
testing_fix = ""
sid_fix = ""
vendor_advisory = ""
d = False
descr = []
author = ""
scope = ""
upgrade = "apt-get upgrade"
debian_specific = False
dtsa_id = "DTSA-" + id + "-" + str(sid)
t_f = open(filename[0], "r")
t_l = t_f.readlines()
for i in t_l:
if i.startswith("source:"):
src = i[7:].strip()
elif i.startswith("date:"):
date = i[5:].strip()
elif i.startswith("author:"):
author = i[7:].strip()
elif i.startswith("vendor-advisory:"):
vendor_advisory = i[16:].strip()
elif i.startswith("vuln-type:"):
vuln_type = i[10:].strip()
elif i.startswith("problem-scope:"):
scope = i[14:].strip()
elif i.startswith("debian-specific:"):
if i[16:].strip() == "yes":
debian_specific = True
elif i.startswith("cve:"):
cve = i[4:].strip().split(" ")
elif i.startswith("testing-fix:"):
testing_fix = i[12:].strip()
elif i.startswith("sid-fix:"):
sid_fix = i[8:].strip()
elif i.startswith("upgrade:"):
upgrade = i[8:].strip()
elif d:
if i[-1] == '\n': i = i[:-1]
descr.append(i)
elif i == "\n" and d == False:
d = True
if len(cve) == 0:
print "No CVE assignments seem to have been made for this issue"
export_html(src, date, vuln_type, cve, testing_fix, sid_fix, descr, vendor_advisory, dtsa_id, 1, author, scope, debian_specific, upgrade)
print "A html representation has been generated as",dtsa_id + ".html"
export_ascii(src, date, vuln_type, cve, testing_fix, sid_fix, descr, vendor_advisory, dtsa_id, 1, author, scope, debian_specific, upgrade)
print "A textual representation has been generated as", dtsa_id
print "You can publish it with the sndadvisory script"
print
construct_dtsa_list(date, dtsa_id, cve, src, vuln_type, testing_fix)
print "Added new DTSA to the list of DTSAs"
print
# This adds a published DTSA to the list, so that it can be cross-referenced with DSAs and CVE IDs
def construct_dtsa_list(date, dtsa_id, cve, src, vuln_type, testing_fix):
l_f = open(os.getcwd() + "/list", "a")
# What do we need the date for?
l_f.write("[" + date + "] " + dtsa_id + " " + src + " - " + vuln_type + "\n")
cves = ""
if len(cve) > 0:
for i in cve:
cves += i
cves += " "
l_f.write("\t{" + cves + "}\n")
l_f.write("\t[" + testing_name + "] - " + src + " " + testing_fix + "\n")
l_f.write("\tTODO: unreleased\n")
l_f.close()
def export_html(src, date, vuln_type, cve, testing_fix, sid_fix, descr, vendor_advisory, id, rev, author, scope, debian_specific, upgrade):
html = open(os.getcwd() + "/" + id + ".html", "w")
# Open, read, write and close the header
header = open(os.getcwd() + "/templates/header.html","r")
for line in header.readlines():
html.write(line);
header.close
# Write the actual html
html.write("
"+ id + "
\n")
html.write("\n")
html.write("- Date Reported:
\n- " + date + "
\n")
html.write("- Affected Package:
\n- " + src + "
\n")
html.write("- Vulnerability:
\n- " + vuln_type + "
\n")
html.write("- Problem-Scope:
\n- " + scope + "
\n")
html.write("- Debian-specific:
\n- " + yn(debian_specific) + "
\n")
# if len(vendor_advisory) > 0:
# html.write("Vendor advisory: " + vendor_advisory + "\n")
# else:
# html.write("Vendor advisory: Not available\n")
cves = "- CVE:
\n- \n"
if len(cve) > 0:
for i in cve:
cves += ""
cves += i
cves += " \n"
else:
cves += "None so far\n"
html.write(cves + "
\n")
html.write("
")
html.write("- More information:
\n")
html.write("- ");
for i in descr:
html.write(i + "
\n")
html.write(" \n")
html.write("
")
html.write("- For the testing distribution (" + testing_name + ") this is fixed in version " + testing_fix + "
\n")
if len(sid_fix) > 0:
html.write("- For the unstable distribution (sid) this is fixed in version " + sid_fix + "
\n")
else:
html.write("- For the unstable distribution this problem will be fixed soon
\n")
html.write("
")
html.write("- This upgrade is recommended if you use " + src + ".
- \n")
html.write("
")
html.write("- If you have the secure testing lines in your sources.list, you can update by running this command as root:
\n")
html.write("\n")
html.write(" - apt-get update && "+ upgrade + "
\n")
html.write("
\n")
html.write("\n")
# FIXME, use python-crypto for inclusion of SHA-1 checksums
print "HTML representation has been exported"
# Open, read, write and close the footer
footer = open(os.getcwd() + "/templates/footer.html","r")
for line in footer.readlines():
html.write(line);
footer.close
# Be nice and close the html file
html.close;
pass
def export_ascii(src, date, vuln_type, cve, testing_fix, sid_fix, descr, vendor_advisory, id, rev, author, scope, debian_specific, upgrade):
ascii = open(os.getcwd() + "/" + id, "w")
# FIXME: use a nice external template with alignment specifiers
# like it used it.
ascii.write("--------------------------------------------------------------------------\n")
ascii.write("Debian Testing Security Advisory "+ id + ((41-len(id)-len(date))*" ") + date + "\n")
ascii.write("secure-testing-team@lists.alioth.debian.org " + ((30-len(author))*" ") + author + "\n")
ascii.write("http://testing-security.debian.net/\n")
ascii.write("--------------------------------------------------------------------------\n")
ascii.write("\n")
ascii.write("Package : " + src + "\n")
ascii.write("Vulnerability : " + vuln_type + "\n")
ascii.write("Problem-Scope : " + scope + "\n")
ascii.write("Debian-specific: " + yn(debian_specific) + "\n")
# if len(vendor_advisory) > 0:
# ascii.write("Vendor advisory: " + vendor_advisory + "\n")
# else:
# ascii.write("Vendor advisory: Not available\n")
cves = "CVE ID : "
if len(cve) > 0:
for i in cve:
cves += i
cves += " "
ascii.write(cves + "\n")
else:
ascii.write(cves + "None so far\n")
ascii.write("\n")
for i in descr:
ascii.write(i + "\n")
ascii.write("\n")
ascii.write("For the testing distribution (" + testing_name + ") this is fixed in version\n")
ascii.write(testing_fix + "\n")
ascii.write("\n")
if len(sid_fix) > 0:
ascii.write("For the unstable distribution (sid) this is fixed in version\n")
ascii.write(sid_fix + "\n")
else:
ascii.write("For the unstable distribution this problem will be fixed soon\n")
ascii.write("\n")
ascii.write("This upgrade is recommended if you use " + src + ".\n")
ascii.write("\n")
ascii.write("The Debian testing security team does not track security issues for the\n")
ascii.write("stable (" + stable_name + ") and oldstable (" + oldstable_name + ") distributions. If stable is vulnerable,\n")
ascii.write("the Debian security team will make an announcement once a fix is ready.\n")
ascii.write("\n")
ascii.write("Upgrade Instructions\n")
ascii.write("--------------------\n")
ascii.write("\n")
ascii.write("To use the Debian testing security archive, add the following lines to\n")
ascii.write("your /etc/apt/sources.list:\n")
ascii.write("\n")
ascii.write("deb http://security.debian.org/ testing-security main contrib non-free\n")
ascii.write("deb-src http://security.debian.org/ testing-security main contrib non-free\n")
ascii.write("\n")
ascii.write("To install the update, run this command as root:\n")
ascii.write("\n")
ascii.write("apt-get update && "+ upgrade + "\n")
ascii.write("\n")
ascii.write("For further information about the Debian testing security team, please refer\n")
ascii.write("to http://testing-security.debian.net/\n")
# FIXME, use python-crypto for inclusion of SHA-1 checksums
print "ASCII representation has been exported"
def yn(v):
if v:
return "Yes"
else:
return "No"
def update_dtsa(id):
filename=glob.glob("DTSA-" + id + "*")
for i in filename: # prune HTML reports
if i.endswith(".html"):
filename.remove(i)
sub_id = int(filename[-1].split("-")[-1])
sub_id += 1
process_dtsa(id, sub_id)
opts, pargs = getopt.getopt(sys.argv[1:], "up")
# FIXME, better cmdline error handling
if len(opts) < 1:
print_usage()
if len(opts) != 1:
print_usage()
if opts[0][0] == "-u":
update_dtsa(pargs[0].strip())
if opts[0][0] == "-p":
process_dtsa(pargs[0].strip(), 1)