3
# Author: Kees Cook <kees@ubuntu.com>
4
# Author: Jamie Strandboge <jamie@ubuntu.com>
5
# Copyright (C) 2005-2009 Canonical Ltd.
7
# This script is distributed under the terms and conditions of the GNU General
8
# Public License, Version 3 or later. See http://www.gnu.org/copyleft/gpl.html
11
# ./scripts/report-cve-age.py hardy
12
# ./scripts/report-cve-age.py hardy | cut -d" " -f2 | sort | uniq -c | sort -n | awk '{print $2 " " $1}' | sort -n > age.data
14
# Report age of CVEs at time of USN publication
20
import cve_lib, usn_lib
23
source_map = source_map.load()
24
releases = cve_lib.releases
25
config = cve_lib.read_config()
27
default_db = config['usn_db_copy']
28
if '-all' not in default_db:
29
tmp = os.path.splitext(default_db)
31
default_db = "%s-all%s" % (tmp[0], tmp[1])
33
parser = optparse.OptionParser()
34
parser.add_option("--with-eol", help="Also show those CVEs in EOL releases", action="store_true", default=False)
35
parser.add_option("-S", "--skip-devel", help="Show only those CVEs *not* in the current devel release", action="store_true")
36
parser.add_option("-D", "--only-devel", help="Show only those CVEs in the current devel release", action="store_true")
37
parser.add_option("--db", help="Specify the USN database to load", metavar="FILENAME", default=default_db)
38
parser.add_option("-m", "--only-supported", help="Show only those CVEs that are supported", action="store_true")
39
parser.add_option("-d", "--debug", help="Report debug information while loading", action="store_true")
40
parser.add_option("--priority", help="Comma separated list of priorities to include in the report", action="store", metavar="PRIORITY")
41
parser.add_option("--action", help="Change report style ('list'(default), 'plot'", action="store", metavar="ACTION", default='list')
42
parser.add_option("--buckets", help="Comma separate list of value buckets to group plot ages into (default is None)", action="store", metavar="AGES", default=None)
43
parser.add_option("--html", help="With the 'plot' action, generate an HTML output file with links to CVEs and USNs", action="store", metavar="FILENAME", default=None)
44
(opt, args) = parser.parse_args()
46
if not os.path.exists(opt.db):
47
print >>sys.stderr, "Cannot read %s" % (opt.db)
49
db = usn_lib.load_database(opt.db)
51
releases = cve_lib.releases
52
if opt.with_eol is False:
53
for eol in cve_lib.eol_releases:
56
if opt.skip_devel and len(cve_lib.devel_release)>0:
57
releases.remove(cve_lib.devel_release)
60
releases = [cve_lib.devel_release]
63
opt.priority = opt.priority.split(',')
70
for value in [int(x) for x in opt.buckets.split(',')]:
74
days = '%d+' % (prev+1)
75
elif prev != value - 1:
76
days = '%d-%d' % (prev+1, value)
78
bucket_values.append(value)
79
bucket_names.setdefault(value,days)
80
if bucket_values[-1] != -1:
81
raise ValueError, "Last bucket value must be -1"
83
# Global CVE info cache
89
if release and release not in releases:
90
raise ValueError, "'%s' must be one of '%s'" % (release, "', '".join(releases))
95
for bucket in bucket_values:
103
def fixed_map(priority=None):
105
for usn in sorted(db.keys()):
106
if not db[usn].has_key('cves'):
108
for cve in db[usn]['cves']:
109
if not cve.startswith('CVE-'):
112
if not release or db[usn]['releases'].has_key(release):
113
# Load CVE if it isn't already cached
114
if not info.has_key(cve):
116
info.setdefault(cve, cve_lib.load_cve(cve_lib.find_cve(cve)))
118
print >> sys.stderr, "Skipping %s: %s" % (cve, str(e))
120
# Skip those without PublicDates for the moment
121
if info[cve]['PublicDate'].strip() == "":
122
print >>sys.stderr, "%s: empty PublicDate" % (cve)
126
# from the all releases or a specific release, find the
127
# mapping of CVE priority based on the package that was
128
# fixed in the USN. In the case of multiple match, first
129
# most specific wins.
131
cve_priority = info[cve]['Priority']
133
for rel in db[usn]['releases']:
134
if not release or release == rel:
135
if db[usn]['releases'][rel].has_key('sources'):
136
for pkg in db[usn]['releases'][rel]['sources']:
137
specificity, specific_priority = cve_lib.contextual_priority(info[cve], pkg, rel)
138
if specificity > max_specificity:
139
cve_priority = specific_priority
140
max_specificity = specificity
142
if not priority or cve_priority == priority:
145
oldest = cve_lib.release_stamps[release]
146
fixed.append([cve, cve_lib.cve_age(cve, info[cve]['PublicDate'], db[usn]['timestamp'], oldest), usn, cve_priority])
149
if opt.action == 'list':
150
fixed = fixed_map(opt.priority)
151
for i in range(0, len(fixed)):
152
print fixed[i][0], fixed[i][1], fixed[i][2], fixed[i][3]
153
elif opt.action == 'plot':
154
priorities = ['untriaged'] + cve_lib.priorities
156
priorities = opt.priority
158
for priority in priorities:
159
fixed = fixed_map(priority)
161
age = bucketize(item[1])
162
byage.setdefault(age,dict())
163
byage[age].setdefault(priority, dict())
164
byage[age][priority].setdefault('count',0)
165
byage[age][priority]['count'] += 1
166
byage[age][priority].setdefault('fixed',[])
167
byage[age][priority]['fixed'].append(item)
171
ages = sorted(byage.keys())
174
print bucket_names[age],
178
for priority in priorities:
179
if byage.has_key(age) and byage[age].has_key(priority):
180
count = byage[age][priority]['count']
190
html = open(opt.html,'w')
191
release_name = release
196
print >>html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
197
print >>html, '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">'
198
print >>html, "<head><title>Ubuntu %sExposure</title>" % (release_name)
199
print >>html, '''<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
200
<meta name="author" content="Canonical Ltd, Kees Cook and Jamie Strandboge" />
201
<meta name="description" content="Ubuntu Exposure Report" />
202
<meta name="copyright" content="Canonical Ltd" />
203
<link rel="StyleSheet" href="toplevel.css" type="text/css" />
206
print >>html, '<img src="%s.png"><br />' % (".".join(os.path.basename(opt.html).split('.')[0:-1]))
207
print >>html, '<table>'
208
print >>html, '<tr><th>Days</th><th>Priority</th><th>CVE</th><th>USN</th></tr>'
211
for priority in priorities:
212
if byage.has_key(age) and byage[age].has_key(priority):
213
for item in byage[age][priority]['fixed']:
214
cve, days, usn = item[0:3]
217
print >>html, '<tr class="%s"><td class="needed">%d</td><td class="needed">%s</td><td class="cve"><a href="http://people.canonical.com/~ubuntu-security/cve/%s">%s</a></td><td><a href="http://www.ubuntu.com/usn/USN-%s">USN-%s</a></td></tr>' % (priority, days, priority, cve, cve, usn, usn)
219
print >>html, '</table>'
220
print >>html, "</body></html>"
223
raise ValueError, "No such --action '%s'" % (opt.action)