~ubuntu-security/ubuntu-cve-tracker/master

« back to all changes in this revision

Viewing changes to scripts/report-cve-age.py

  • Committer: Steve Beattie
  • Date: 2019-02-19 06:18:27 UTC
  • Revision ID: sbeattie@ubuntu.com-20190219061827-oh57fzcfc1u9dlfk
The ubuntu-cve-tracker project has been converted to git.

Please use 'git clone https://git.launchpad.net/ubuntu-cve-tracker' to
get the converted tree.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/env python
2
 
 
3
 
# Author: Kees Cook <kees@ubuntu.com>
4
 
# Author: Jamie Strandboge <jamie@ubuntu.com>
5
 
# Copyright (C) 2005-2009 Canonical Ltd.
6
 
#
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
9
 
# for details.
10
 
#
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
13
 
#
14
 
# Report age of CVEs at time of USN publication
15
 
 
16
 
import os
17
 
import re
18
 
import sys
19
 
import optparse
20
 
import cve_lib, usn_lib
21
 
 
22
 
import source_map
23
 
source_map = source_map.load()
24
 
releases = cve_lib.releases
25
 
config = cve_lib.read_config()
26
 
 
27
 
default_db = config['usn_db_copy']
28
 
if '-all' not in default_db:
29
 
    tmp = os.path.splitext(default_db)
30
 
    if len(tmp) == 2:
31
 
        default_db = "%s-all%s" % (tmp[0], tmp[1])
32
 
 
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()
45
 
 
46
 
if not os.path.exists(opt.db):
47
 
    print >>sys.stderr, "Cannot read %s" % (opt.db)
48
 
    sys.exit(1)
49
 
db = usn_lib.load_database(opt.db)
50
 
 
51
 
releases = cve_lib.releases
52
 
if opt.with_eol is False:
53
 
    for eol in cve_lib.eol_releases:
54
 
        if eol in releases:
55
 
            releases.remove(eol)
56
 
if opt.skip_devel and len(cve_lib.devel_release)>0:
57
 
    releases.remove(cve_lib.devel_release)
58
 
 
59
 
if opt.only_devel:
60
 
    releases = [cve_lib.devel_release]
61
 
 
62
 
if opt.priority:
63
 
    opt.priority = opt.priority.split(',')
64
 
 
65
 
bucket_values = []
66
 
bucket_names = dict()
67
 
if opt.buckets:
68
 
    prev = None
69
 
    buckets = []
70
 
    for value in [int(x) for x in opt.buckets.split(',')]:
71
 
        days = '%d' % (value)
72
 
        if prev != None:
73
 
            if value == -1:
74
 
                days = '%d+' % (prev+1)
75
 
            elif prev != value - 1:
76
 
                days = '%d-%d' % (prev+1, value)
77
 
        prev = 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"
82
 
 
83
 
# Global CVE info cache
84
 
info = dict()
85
 
 
86
 
release = None
87
 
if len(args)>0:
88
 
    release = args[0]
89
 
if release and release not in releases:
90
 
    raise ValueError, "'%s' must be one of '%s'" % (release, "', '".join(releases))
91
 
 
92
 
def bucketize(value):
93
 
    if not opt.buckets:
94
 
        return value
95
 
    for bucket in bucket_values:
96
 
        if bucket == -1:
97
 
            return bucket
98
 
        if value <= bucket:
99
 
            return bucket
100
 
    # not possible?
101
 
    return -1
102
 
 
103
 
def fixed_map(priority=None):
104
 
    fixed = []
105
 
    for usn in sorted(db.keys()):
106
 
        if not db[usn].has_key('cves'):
107
 
            continue
108
 
        for cve in db[usn]['cves']:
109
 
            if not cve.startswith('CVE-'):
110
 
                continue
111
 
 
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):
115
 
                    try:
116
 
                        info.setdefault(cve, cve_lib.load_cve(cve_lib.find_cve(cve)))
117
 
                    except Exception, e:
118
 
                        print >> sys.stderr, "Skipping %s: %s" % (cve, str(e))
119
 
                        continue
120
 
                # Skip those without PublicDates for the moment
121
 
                if info[cve]['PublicDate'].strip() == "":
122
 
                    print >>sys.stderr, "%s: empty PublicDate" % (cve)
123
 
                    continue
124
 
 
125
 
                # Check priority
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.
130
 
                max_specificity = -1
131
 
                cve_priority = info[cve]['Priority']
132
 
                tried = []
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
141
 
 
142
 
                if not priority or cve_priority == priority:
143
 
                    oldest = None
144
 
                    if release:
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])
147
 
    return fixed
148
 
 
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
155
 
    if opt.priority:
156
 
        priorities = opt.priority
157
 
    byage = dict()
158
 
    for priority in priorities:
159
 
        fixed = fixed_map(priority)
160
 
        for item in fixed:
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)
168
 
    if opt.buckets:
169
 
        ages = bucket_values
170
 
    else:
171
 
        ages = sorted(byage.keys())
172
 
    for age in ages:
173
 
        if opt.buckets:
174
 
            print bucket_names[age],
175
 
        else:
176
 
            print age,
177
 
        sum = 0
178
 
        for priority in priorities:
179
 
            if byage.has_key(age) and byage[age].has_key(priority):
180
 
                count = byage[age][priority]['count']
181
 
                sum += count
182
 
                print count,
183
 
            else:
184
 
                print '0',
185
 
        if opt.buckets:
186
 
            print sum,
187
 
        print
188
 
 
189
 
    if opt.html:
190
 
        html = open(opt.html,'w')
191
 
        release_name = release
192
 
        if not release_name:
193
 
            release_name = ""
194
 
        else:
195
 
            release_name += " "
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" />
204
 
 
205
 
</head><body>'''
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>'
209
 
 
210
 
        for age in ages:
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]
215
 
                        if days < 0:
216
 
                            days = 0
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)
218
 
 
219
 
        print >>html, '</table>'
220
 
        print >>html, "</body></html>"
221
 
 
222
 
else:
223
 
    raise ValueError, "No such --action '%s'" % (opt.action)