| 1 |
#!/usr/bin/python
|
| 2 |
|
| 3 |
import sys, getopt, os, glob
|
| 4 |
|
| 5 |
# TODO:
|
| 6 |
# Add code for updating a DTSA
|
| 7 |
# Include SHA-1 checksums in advisories
|
| 8 |
|
| 9 |
# Note: This has to be run inside secure-testing/data/DTSA/
|
| 10 |
|
| 11 |
# Prerequisites:
|
| 12 |
# subdirectories advs/plain-text, advs/html and templates
|
| 13 |
# Templates must include header.html and footer.html, but can be blank
|
| 14 |
# mailx package installed
|
| 15 |
|
| 16 |
announce_mail_address = "secure-testing-announce@lists.alioth.debian.org"
|
| 17 |
|
| 18 |
def print_usage():
|
| 19 |
print "dtsa [-a | -u] dtsa-id major number"
|
| 20 |
print " -p Process a new DTSA from a template"
|
| 21 |
print " -u Update an existing DTSA from a template"
|
| 22 |
sys.exit(-1)
|
| 23 |
|
| 24 |
|
| 25 |
def process_dtsa(id, sid):
|
| 26 |
filename=glob.glob("advs/" + id + "-*.adv")
|
| 27 |
|
| 28 |
src = ""
|
| 29 |
date = ""
|
| 30 |
vuln_type = ""
|
| 31 |
cve = ""
|
| 32 |
testing_fix = ""
|
| 33 |
sid_fix = ""
|
| 34 |
vendor_advisory = ""
|
| 35 |
d = False
|
| 36 |
descr = []
|
| 37 |
author = ""
|
| 38 |
scope = ""
|
| 39 |
upgrade = "apt-get upgrade"
|
| 40 |
debian_specific = False
|
| 41 |
|
| 42 |
dtsa_id = "DTSA-" + id + "-" + str(sid)
|
| 43 |
|
| 44 |
t_f = open(filename[0], "r")
|
| 45 |
t_l = t_f.readlines()
|
| 46 |
|
| 47 |
for i in t_l:
|
| 48 |
if i.startswith("source:"):
|
| 49 |
src = i[7:].strip()
|
| 50 |
elif i.startswith("date:"):
|
| 51 |
date = i[5:].strip()
|
| 52 |
elif i.startswith("author:"):
|
| 53 |
author = i[7:].strip()
|
| 54 |
elif i.startswith("vendor-advisory:"):
|
| 55 |
vendor_advisory = i[16:].strip()
|
| 56 |
elif i.startswith("vuln-type:"):
|
| 57 |
vuln_type = i[10:].strip()
|
| 58 |
elif i.startswith("problem-scope:"):
|
| 59 |
scope = i[14:].strip()
|
| 60 |
elif i.startswith("debian-specific:"):
|
| 61 |
if i[16:].strip() == "yes":
|
| 62 |
debian_specific = True
|
| 63 |
elif i.startswith("cve:"):
|
| 64 |
cve = i[4:].strip().split(" ")
|
| 65 |
elif i.startswith("testing-fix:"):
|
| 66 |
testing_fix = i[12:].strip()
|
| 67 |
elif i.startswith("sid-fix:"):
|
| 68 |
sid_fix = i[8:].strip()
|
| 69 |
elif i.startswith("upgrade:"):
|
| 70 |
upgrade = i[8:].strip()
|
| 71 |
elif d:
|
| 72 |
descr.append(i.strip())
|
| 73 |
elif i == "\n" and d == False:
|
| 74 |
d = True
|
| 75 |
|
| 76 |
if len(cve) == 0:
|
| 77 |
print "No CVE assignments seem to have been made for this issue"
|
| 78 |
|
| 79 |
export_html(src, date, vuln_type, cve, testing_fix, sid_fix, descr, vendor_advisory, dtsa_id, 1, author, scope, debian_specific, upgrade)
|
| 80 |
|
| 81 |
print "A html representation has been generated as",dtsa_id + ".html"
|
| 82 |
|
| 83 |
export_ascii(src, date, vuln_type, cve, testing_fix, sid_fix, descr, vendor_advisory, dtsa_id, 1, author, scope, debian_specific, upgrade)
|
| 84 |
|
| 85 |
print "A textual representation has been generated as", dtsa_id
|
| 86 |
print "You can publish it with the sndadvisory script"
|
| 87 |
print
|
| 88 |
|
| 89 |
construct_dtsa_list(date, dtsa_id, cve, src, vuln_type, testing_fix)
|
| 90 |
|
| 91 |
print "Added new DTSA to the list of DTSAs"
|
| 92 |
print
|
| 93 |
|
| 94 |
# This adds a published DTSA to the list, so that it can be cross-referenced with DSAs and CVE IDs
|
| 95 |
def construct_dtsa_list(date, dtsa_id, cve, src, vuln_type, testing_fix):
|
| 96 |
l_f = open(os.getcwd() + "/list", "a")
|
| 97 |
# What do we need the date for?
|
| 98 |
l_f.write("[" + date + "] " + dtsa_id + " " + src + " - " + vuln_type + "\n")
|
| 99 |
cves = ""
|
| 100 |
if len(cve) > 0:
|
| 101 |
for i in cve:
|
| 102 |
cves += i
|
| 103 |
cves += " "
|
| 104 |
l_f.write("\t{" + cves + "}\n")
|
| 105 |
l_f.write("\t- " + src + " " + testing_fix + "\n")
|
| 106 |
l_f.write("\tTODO: unreleased\n")
|
| 107 |
l_f.close()
|
| 108 |
|
| 109 |
def export_html(src, date, vuln_type, cve, testing_fix, sid_fix, descr, vendor_advisory, id, rev, author, scope, debian_specific, upgrade):
|
| 110 |
html = open(os.getcwd() + "/" + id + ".html", "w")
|
| 111 |
|
| 112 |
# Open, read, write and close the header
|
| 113 |
header = open(os.getcwd() + "/templates/header.html","r")
|
| 114 |
for line in header.readlines():
|
| 115 |
html.write(line);
|
| 116 |
header.close
|
| 117 |
|
| 118 |
# Write the actual html
|
| 119 |
|
| 120 |
html.write("<h2>"+ id + "</h2>\n")
|
| 121 |
html.write("<dl>\n")
|
| 122 |
html.write("<dt>Date Reported:</dt>\n<dd>" + date + "</dd>\n")
|
| 123 |
html.write("<dt>Affected Package:</dt>\n<dd><a href='http://packages.debian.org/src:" + src + "'>" + src + "</a></dd>\n")
|
| 124 |
html.write("<dt>Vulnerability:</dt>\n<dd>" + vuln_type + "</dd>\n")
|
| 125 |
html.write("<dt>Problem-Scope:</dt>\n<dd>" + scope + "</dd>\n")
|
| 126 |
html.write("<dt>Debian-specific:</dt>\n<dd>" + yn(debian_specific) + "<br></dd>\n")
|
| 127 |
|
| 128 |
# if len(vendor_advisory) > 0:
|
| 129 |
# html.write("Vendor advisory: " + vendor_advisory + "\n")
|
| 130 |
# else:
|
| 131 |
# html.write("Vendor advisory: Not available\n")
|
| 132 |
cves = "<dt>CVE:</dt>\n<dd>\n"
|
| 133 |
if len(cve) > 0:
|
| 134 |
for i in cve:
|
| 135 |
cves += "<a href='http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=" + i +"'>"
|
| 136 |
cves += i
|
| 137 |
cves += "</a> \n"
|
| 138 |
else:
|
| 139 |
cves += "None so far\n"
|
| 140 |
html.write(cves + "<br></dd>\n")
|
| 141 |
|
| 142 |
html.write("<br>")
|
| 143 |
html.write("<dt>More information:</dt>\n")
|
| 144 |
html.write("<dd>");
|
| 145 |
for i in descr:
|
| 146 |
html.write(i + " <br>\n")
|
| 147 |
html.write("</dd>\n")
|
| 148 |
|
| 149 |
html.write("<br>")
|
| 150 |
html.write("<dt>For the testing distribution (etch) this is fixed in version " + testing_fix + "</dt>\n")
|
| 151 |
|
| 152 |
if len(sid_fix) > 0:
|
| 153 |
html.write("<dt>For the unstable distribution (sid) this is fixed in version " + sid_fix + "</dt>\n")
|
| 154 |
else:
|
| 155 |
html.write("<dt>For the unstable distribution this problem will be fixed soon</dt>\n")
|
| 156 |
|
| 157 |
html.write("<br>")
|
| 158 |
html.write("<dt>This upgrade is recommended if you use " + src + ".<dt>\n")
|
| 159 |
html.write("<br>")
|
| 160 |
|
| 161 |
html.write("<dt>If you have the secure testing lines in your sources.list, you can update by running this command as root:</dt>\n")
|
| 162 |
html.write("\n")
|
| 163 |
|
| 164 |
html.write("<dd>apt-get update && "+ upgrade + "</dd>\n")
|
| 165 |
html.write("<br>\n")
|
| 166 |
html.write("\n")
|
| 167 |
# FIXME, use python-crypto for inclusion of SHA-1 checksums
|
| 168 |
|
| 169 |
print "HTML representation has been exported"
|
| 170 |
# Open, read, write and close the footer
|
| 171 |
footer = open(os.getcwd() + "/templates/footer.html","r")
|
| 172 |
for line in footer.readlines():
|
| 173 |
html.write(line);
|
| 174 |
footer.close
|
| 175 |
|
| 176 |
# Be nice and close the html file
|
| 177 |
html.close;
|
| 178 |
pass
|
| 179 |
|
| 180 |
|
| 181 |
def export_ascii(src, date, vuln_type, cve, testing_fix, sid_fix, descr, vendor_advisory, id, rev, author, scope, debian_specific, upgrade):
|
| 182 |
ascii = open(os.getcwd() + "/" + id, "w")
|
| 183 |
|
| 184 |
# FIXME: use a nice external template with alignment specifiers
|
| 185 |
# like it used it.
|
| 186 |
ascii.write("------------------------------------------------------------------------------\n")
|
| 187 |
ascii.write("Debian Testing Security Advisory "+ id + ((45-len(id)-len(date))*" ") + date + "\n")
|
| 188 |
ascii.write("secure-testing-team@lists.alioth.debian.org " + ((34-len(author))*" ") + author + "\n")
|
| 189 |
ascii.write("http://secure-testing-master.debian.net/\n")
|
| 190 |
ascii.write("------------------------------------------------------------------------------\n")
|
| 191 |
ascii.write("\n")
|
| 192 |
ascii.write("Package : " + src + "\n")
|
| 193 |
ascii.write("Vulnerability : " + vuln_type + "\n")
|
| 194 |
ascii.write("Problem-Scope : " + scope + "\n")
|
| 195 |
ascii.write("Debian-specific: " + yn(debian_specific) + "\n")
|
| 196 |
# if len(vendor_advisory) > 0:
|
| 197 |
# ascii.write("Vendor advisory: " + vendor_advisory + "\n")
|
| 198 |
# else:
|
| 199 |
# ascii.write("Vendor advisory: Not available\n")
|
| 200 |
cves = "CVE ID : "
|
| 201 |
if len(cve) > 0:
|
| 202 |
for i in cve:
|
| 203 |
cves += i
|
| 204 |
cves += " "
|
| 205 |
ascii.write(cves + "\n")
|
| 206 |
else:
|
| 207 |
ascii.write(cves + "None so far\n")
|
| 208 |
ascii.write("\n")
|
| 209 |
for i in descr:
|
| 210 |
ascii.write(i + "\n")
|
| 211 |
ascii.write("\n")
|
| 212 |
|
| 213 |
ascii.write("For the testing distribution (etch) this is fixed in version\n")
|
| 214 |
ascii.write(testing_fix + "\n")
|
| 215 |
ascii.write("\n")
|
| 216 |
|
| 217 |
if len(sid_fix) > 0:
|
| 218 |
ascii.write("For the unstable distribution (sid) this is fixed in version\n")
|
| 219 |
ascii.write(sid_fix + "\n")
|
| 220 |
else:
|
| 221 |
ascii.write("For the unstable distribution this problem will be fixed soon\n")
|
| 222 |
ascii.write("\n")
|
| 223 |
|
| 224 |
ascii.write("This upgrade is recommended if you use " + src + ".\n")
|
| 225 |
ascii.write("\n")
|
| 226 |
|
| 227 |
ascii.write("The Debian testing security team does not track security issues for then\n")
|
| 228 |
ascii.write("stable (sarge) and oldstable (woody) distributions. If stable is vulnerable,\n")
|
| 229 |
ascii.write("the Debian security team will make an announcement once a fix is ready.\n")
|
| 230 |
ascii.write("\n")
|
| 231 |
|
| 232 |
ascii.write("Upgrade Instructions\n")
|
| 233 |
ascii.write("--------------------\n")
|
| 234 |
ascii.write("\n")
|
| 235 |
|
| 236 |
ascii.write("To use the Debian testing security archive, add the following lines to\n")
|
| 237 |
ascii.write("your /etc/apt/sources.list:\n")
|
| 238 |
ascii.write("\n")
|
| 239 |
ascii.write("deb http://secure-testing.debian.net/debian-secure-testing etch-proposed-updates/security-updates main contrib non-free\n")
|
| 240 |
ascii.write("deb-src http://secure-testing.debian.net/debian-secure-testing etch-proposed-updates/security-updates main contrib non-free\n")
|
| 241 |
ascii.write("\n")
|
| 242 |
ascii.write("The archive signing key can be downloaded from\n")
|
| 243 |
ascii.write("http://secure-testing-master.debian.net/ziyi-2005-7.asc\n")
|
| 244 |
ascii.write("\n")
|
| 245 |
|
| 246 |
ascii.write("To install the update, run this command as root:\n")
|
| 247 |
ascii.write("\n")
|
| 248 |
|
| 249 |
ascii.write("apt-get update && "+ upgrade + "\n")
|
| 250 |
ascii.write("\n")
|
| 251 |
|
| 252 |
ascii.write("For further information about the Debian testing security team, please refer\n")
|
| 253 |
ascii.write("to http://secure-testing-master.debian.net/\n")
|
| 254 |
|
| 255 |
# FIXME, use python-crypto for inclusion of SHA-1 checksums
|
| 256 |
|
| 257 |
print "ASCII representation has been exported"
|
| 258 |
|
| 259 |
def yn(v):
|
| 260 |
if v:
|
| 261 |
return "Yes"
|
| 262 |
else:
|
| 263 |
return "No"
|
| 264 |
|
| 265 |
|
| 266 |
def update_dtsa(id):
|
| 267 |
filename=glob.glob("DTSA-" + id + "*")
|
| 268 |
for i in filename: # prune HTML reports
|
| 269 |
if i.endswith(".html"):
|
| 270 |
filename.remove(i)
|
| 271 |
sub_id = int(filename[-1].split("-")[-1])
|
| 272 |
sub_id += 1
|
| 273 |
process_dtsa(id, sub_id)
|
| 274 |
|
| 275 |
opts, pargs = getopt.getopt(sys.argv[1:], "up")
|
| 276 |
|
| 277 |
# FIXME, better cmdline error handling
|
| 278 |
|
| 279 |
if len(opts) < 1:
|
| 280 |
print_usage()
|
| 281 |
|
| 282 |
if len(opts) != 1:
|
| 283 |
print_usage()
|
| 284 |
|
| 285 |
if opts[0][0] == "-u":
|
| 286 |
update_dtsa(pargs[0].strip())
|
| 287 |
|
| 288 |
if opts[0][0] == "-p":
|
| 289 |
process_dtsa(pargs[0].strip(), 1)
|
| 290 |
|