#!/usr/bin/python # Reasonably well-formed announcements to the debian-security-announce # mailing list can be piped through this script. The result is an # entry suitable for data/DSA/list. import os import os.path import re import string import sys import time import urllib2 def setup_paths(): check_file = 'lib/python/debian_support.py' paths = [os.getcwd(), os.path.dirname(sys.argv[0])] try: paths.append(os.path.dirname(os.readlink(sys.argv[0]))) except OSError: pass for path in paths: while 1: if os.path.exists("%s/%s" % (path, check_file)): sys.path = [path + '/lib/python'] + sys.path return path idx = string.rfind(path, '/') if idx == -1: break path = path[0:idx] raise ImportError, "could not setup paths" os.chdir(setup_paths()) import debian_support # DSAs do not contain version numbers with epochs, so they are useless # for our purposes. def fetch_dsc(url): u = urllib2.urlopen(url) assert u.readline()[0] == '-' # OpenPGP cleartext signature header def parse(*regexps): result = [None] * len(regexps) for line in u: for i in range(len(regexps)): match = regexps[i].match(line) if match: result[i] = match.groups()[0] continue if line[0] == '-': break return result (source, version)= parse(re.compile("^Source: (\S+)$"), re.compile("^Version: (\S+)$")) assert source is not None assert version is not None return (source, version) re_title = re.compile(r'^Subject: .*\[DSA[ -](\d+-\d+)\] .* fix(?:es)? (.*)$') re_date = re.compile(r'^([A-Z][a-z][a-z])[a-z]* (\d+)[a-z]*, (\d+)\s+http://.*') re_cve = re.compile('(CVE-\d{4}-\d{4})') release_headline_re = re.compile( r'^Debian GNU/Linux [0-9.]+ (?:\(|alias) ([a-z]+).*') dscurl_re = re.compile(r'^\s*(http://\S+\.dsc).*') # Variants used by "dak new-security-install" re_date1 = re.compile(r'^([A-Z][a-z][a-z])[a-z]* (\d+), (2\d{3}).*') release_headline1_re = re.compile(r'^Debian [0-9.]+ \(([a-z]+)\).*') release_map = {'stable' : 'lenny', 'oldstable' : 'etch'} def process_file(file): cve_names = {} package_notes = [] release = '' date = '' dsa_name = '' title = '' packages = {} for line in file.readlines(): match = re_title.match(line) if match: (dsa_name, title) = match.groups() continue match = re_date.match(line) if match: (m, d, y) = match.groups() date = "%02d %s %s" % (int(d), m, y) continue for cve in re_cve.findall(line): cve_names[cve] = True match = release_headline_re.match(line) if match: (release,) = match.groups() continue match = dscurl_re.match(line) if match: assert release (source, version) = fetch_dsc(match.groups()[0]) packages[source] = True package_notes.append((release, source, version)) # Variants used by "dak new-security-install" match = re_date1.match(line) if match: (m, d, y) = match.groups() date = "%02d %s %s" % (int(d), m, y) continue match = release_headline1_re.match(line) if match: (release,) = match.groups() release = release_map[release] continue assert date assert title packages = packages.keys() packages.sort() print "[%s] DSA-%s %s - %s" % (date, dsa_name, ' '.join(packages), title) cve_names = cve_names.keys() if cve_names: cve_names.sort() print "\t{%s}" % (' '.join(cve_names)) for (release, source, version) in package_notes: print "\t[%s] - %s %s" % (release, source, version) if len(sys.argv) == 1: process_file(sys.stdin) else: l = sys.argv[1:] l.reverse() def is_bad(f): if os.path.exists(f): return True sys.stderr.write("error: file does not exist: %s\n" % f) return False l = filter(is_bad, l) for x in l: process_file(file(x))