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

« back to all changes in this revision

Viewing changes to scripts/active_edit

  • 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: Jamie Strandboge <jamie@ubuntu.com>
4
 
# Author: Kees Cook <kees@ubuntu.com>
5
 
# Copyright (C) 2005-2016 Canonical Ltd.
6
 
#
7
 
# This script is distributed under the terms and conditions of the GNU General
8
 
# Public License, Version 2 or later. See http://www.gnu.org/copyleft/gpl.html
9
 
# for details.
10
 
 
11
 
import optparse
12
 
import os
13
 
import re
14
 
import sys
15
 
import time
16
 
 
17
 
import cve_lib
18
 
import source_map
19
 
 
20
 
releases = ['upstream'] + cve_lib.all_releases
21
 
 
22
 
max_file_size = 10 * 1024 * 1024  # 10MB
23
 
cvedir = cve_lib.active_dir
24
 
boilerplates = cvedir
25
 
 
26
 
parser = optparse.OptionParser()
27
 
parser.add_option("-p", "--package", dest="pkgs", help="Package name and optional version where package is fixed (with optional Ubuntu release and version in that release)", metavar="NAME[,VERSION[,RELEASE,RELEASE_VERSION]]", action="append")
28
 
parser.add_option("-b", "--bug-url", dest="bug_urls", help="Bug references", metavar="URL", action="append")
29
 
parser.add_option("-r", "--reference-url", dest="ref_urls", help="URL references", metavar="URL", action="append")
30
 
parser.add_option("-c", "--cve", dest="cve", help="CVE entry", metavar="CVE-YYYY-NNNN")
31
 
parser.add_option("-e", "--embargoed", dest="embargoed", help="This is an embargoed entry", action="store_true")
32
 
parser.add_option("-y", "--yes", dest="autoconfirm", help="Do not ask for confirmation", action="store_true")
33
 
parser.add_option("-P", "--public", help="Record date the CVE went public", metavar="YYYY-MM-DD")
34
 
(options, args) = parser.parse_args()
35
 
 
36
 
source = source_map.load()
37
 
 
38
 
def pkg_in_rel(pkg,rel):
39
 
    if rel in ['upstream', 'product']:
40
 
        return True
41
 
    if rel not in source:
42
 
        return False
43
 
    return (pkg in source[rel])
44
 
 
45
 
def get_releases(pkgname):
46
 
    # deep copy
47
 
    tmp = []
48
 
    for r in releases:
49
 
        tmp.append(r)
50
 
 
51
 
    # Handle when devel isn't open yet
52
 
    if cve_lib.devel_release == '':
53
 
        tmp.append('')
54
 
 
55
 
    #  handle product kernels
56
 
    if pkgname in cve_lib.product_kernels:
57
 
        tmp = ['product']
58
 
 
59
 
    return tmp
60
 
 
61
 
def update_cve(cve, pkgname, fixed_in=None, fixed_in_release=None, fixed_in_release_version=None):
62
 
    '''Update an existing CVE file'''
63
 
    with open(os.path.join(cvedir, cve), "r") as f:
64
 
        lines = f.read(max_file_size).split('\n')
65
 
 
66
 
    skipped = []
67
 
    added_lines = ""
68
 
 
69
 
    tmp_releases = get_releases(pkgname)
70
 
 
71
 
    # If we are using 00boilerplate.<pkgname> and the package is DNE on all
72
 
    # current releases, then don't add the the stanza for this release. This
73
 
    # allows us to use generic boilerplate names like 00boilerplate.gnutls or
74
 
    # 00boilerplate.openjdk without adding useless extra stanzas.
75
 
    # TODO: this still doesn't handle adding the contents of the boilerplate
76
 
    # to an existing CVE (ie, ./scripts/sctive_edit -p openjdk -c CVE-YYYY-NNNN
77
 
    # where CVE-YYYY-NNNN already exists)
78
 
    if os.path.exists(os.path.join(boilerplates, '00boilerplate.%s' % pkgname)):
79
 
        pkg_exists_somewhere = False
80
 
        for r in tmp_releases:
81
 
            if r == 'upstream' or r in cve_lib.eol_releases:
82
 
                continue
83
 
            if pkg_in_rel(pkgname,r):
84
 
                pkg_exists_somewhere = True
85
 
                break
86
 
        if not pkg_exists_somewhere:
87
 
            os.write(sys.stderr.fileno(), "skipping '" + pkgname + "' (DNE on all current releases)\n")
88
 
            return
89
 
 
90
 
    for line in lines:
91
 
        for r in tmp_releases:
92
 
            if r == cve_lib.devel_release or r == '':
93
 
                r = 'devel'
94
 
            if not re.match(r'^' + r + ".*:", line):
95
 
                continue
96
 
            tmp = line.split(':')
97
 
            match = "%s_%s" % (r, pkgname)
98
 
            if match == tmp[0]:
99
 
                skipped.append(r)
100
 
 
101
 
    if len(skipped) == 0:
102
 
        added_lines += '\nPatches_' + pkgname + ':\n'
103
 
 
104
 
    higher_not_affected = False
105
 
    for release in tmp_releases:
106
 
        r = release
107
 
        if r == cve_lib.devel_release or r == '':
108
 
            r = 'devel'
109
 
 
110
 
        if r in skipped:
111
 
             os.write(sys.stderr.fileno(), "skipping '" + pkgname + "' for " + r + " (already included)\n")
112
 
        else:
113
 
            # skip eol_releases releases
114
 
            if r in cve_lib.eol_releases:
115
 
                continue
116
 
            state = "needs-triage"
117
 
            if not pkg_in_rel(pkgname,release):
118
 
                state = "DNE"
119
 
            elif r == 'upstream' and fixed_in is not None:
120
 
                state = "released (%s)" % fixed_in
121
 
            elif fixed_in_release_version and r == fixed_in_release:
122
 
                state = "not-affected (%s)" % fixed_in_release_version
123
 
                higher_not_affected = True
124
 
            elif higher_not_affected:
125
 
                state = "not-affected"
126
 
            added_lines += '%s_%s: %s\n' % (r, pkgname, state)
127
 
 
128
 
    if len(releases) == len(skipped):
129
 
        os.write(sys.stdout.fileno(), "\nNothing to add!\n")
130
 
        return
131
 
 
132
 
    # If we have things to add, then make sure we update lts_unsupported on them
133
 
    if added_lines == "":
134
 
        # Nothing added, just update the whole CVE in place
135
 
        cve_lib.lts_unsupported(source, os.path.join(cvedir, cve), pkgname)
136
 
        return
137
 
    else:
138
 
        # Update the added lines only
139
 
        added_lines = cve_lib.lts_unsupported(source, os.path.join(cvedir, cve), pkgname, added_lines)
140
 
 
141
 
    if not options.autoconfirm:
142
 
        os.write(sys.stdout.fileno(), "\n" + added_lines)
143
 
        os.write(sys.stdout.fileno(), "\nAppend the above to " + os.path.join(cvedir, cve) + " (y|N)? ")
144
 
        ans = sys.stdin.readline().lower()
145
 
        os.write(sys.stdout.fileno(), "\n")
146
 
 
147
 
    if options.autoconfirm or ans.startswith("y"):
148
 
        file = open(os.path.join(cvedir, cve), "a")
149
 
        file.write(added_lines)
150
 
        file.close()
151
 
    else:
152
 
        os.write(sys.stdout.fileno(), "Aborted\n")
153
 
 
154
 
def create_cve(cve, pkgname, fixed_in=None, fixed_in_release=None, fixed_in_release_version=None):
155
 
    '''Create a new CVE file'''
156
 
    src = os.path.join(boilerplates, '00boilerplate')
157
 
    if os.path.exists(src + "." + pkgname):
158
 
        src = src + "." + pkgname
159
 
    boiler = open(src, "r")
160
 
    lines =  boiler.read(max_file_size).splitlines()
161
 
    boiler.close()
162
 
 
163
 
    cand_pat = re.compile(r'^Candidate:')
164
 
    ref_pat = re.compile(r'^References:')
165
 
    bugs_pat = re.compile(r'^Bugs:')
166
 
    pkg_pat = re.compile(r'^#[a-z/\-]+_PKG')
167
 
    patch_pat = re.compile(r'^#Patches_PKG')
168
 
 
169
 
    tmp_releases = get_releases(pkgname)
170
 
    # print(tmp_releases)
171
 
 
172
 
    contents = ""
173
 
    higher_not_affected = False
174
 
    for line in lines:
175
 
        if (cand_pat.search(line)):
176
 
            contents += line + os.path.basename(cve) + '\n'
177
 
        elif line.startswith('PublicDate:'):
178
 
            if options.embargoed:
179
 
                contents += "CRD: <TBD>\n"
180
 
            if options.public:
181
 
                contents += "PublicDate: %s\n" % options.public
182
 
            else:
183
 
                # default to today-- this will be refreshed by check-cves
184
 
                contents += "PublicDate: %s\n" % time.strftime("%Y-%m-%d", time.gmtime())
185
 
        elif (patch_pat.search(line)):
186
 
            contents += '\nPatches_' + pkgname + ':\n'
187
 
        elif (pkg_pat.search(line)):
188
 
            for release in tmp_releases:
189
 
                r = release
190
 
                # skip eol_releases releases
191
 
                if r in cve_lib.eol_releases:
192
 
                    continue
193
 
                if r == cve_lib.devel_release or r == '':
194
 
                    r = 'devel'
195
 
 
196
 
                if r == 'product':
197
 
                    # replace '#upstream_PKG:' with 'product_PKG:'
198
 
                    rel_pat = re.compile('upstream')
199
 
                else:
200
 
                    rel_pat = re.compile(r + '_')
201
 
 
202
 
                if (rel_pat.search(line)):
203
 
                    state = "needs-triage"
204
 
                    if not pkg_in_rel(pkgname,release):
205
 
                        state = "DNE"
206
 
                    elif r == 'upstream' and fixed_in is not None:
207
 
                        state = "released (%s)" % fixed_in
208
 
                    elif fixed_in_release_version and r == fixed_in_release:
209
 
                        state = "not-affected (%s)" % fixed_in_release_version
210
 
                        higher_not_affected = True
211
 
                    elif higher_not_affected:
212
 
                        state = "not-affected"
213
 
                    contents += "%s_%s: %s\n" % (r, pkgname, state)
214
 
        elif ref_pat.search(line):
215
 
            if not re.search(r'N', cve):
216
 
                contents += line + "\n https://cve.mitre.org/cgi-bin/cvename.cgi?name=" + cve + "\n"
217
 
            else:
218
 
                contents += line + "\n"
219
 
            if options.ref_urls:
220
 
                for i in options.ref_urls:
221
 
                   contents += " %s\n" % i
222
 
        elif options.bug_urls and bugs_pat.search(line):
223
 
            contents += line
224
 
            for i in options.bug_urls:
225
 
                contents += "\n %s\n" % i
226
 
        else:
227
 
            contents += line + '\n'
228
 
 
229
 
    contents = cve_lib.lts_unsupported(source, os.path.join(cvedir, cve), pkgname, contents)
230
 
    if not options.autoconfirm:
231
 
        os.write(sys.stdout.fileno(), contents)
232
 
        os.write(sys.stdout.fileno(), "\nWrite the above to " + os.path.join(cvedir, cve) + " (y|N)? ")
233
 
        ans = sys.stdin.readline().lower()
234
 
        os.write(sys.stdout.fileno(), "\n")
235
 
 
236
 
    if options.autoconfirm or ans.startswith("y"):
237
 
        newfile = open(os.path.join(cvedir, cve), 'w')
238
 
        newfile.write(contents)
239
 
        newfile.close()
240
 
    else:
241
 
        os.write(sys.stdout.fileno(), "Aborted\n")
242
 
 
243
 
 
244
 
 
245
 
if not options.pkgs:
246
 
    parser.print_help()
247
 
    sys.exit(1)
248
 
 
249
 
if not options.cve:
250
 
    parser.print_help()
251
 
    sys.exit(1)
252
 
 
253
 
cve = options.cve
254
 
pkgs = options.pkgs
255
 
pat = cve_lib.CVE_RE
256
 
 
257
 
if options.embargoed:
258
 
    cvedir = cve_lib.embargoed_dir
259
 
    pat = re.compile(r'^[\w-]*$')
260
 
 
261
 
    if not os.path.islink(cvedir):
262
 
        os.write(sys.stderr.fileno(), "embargoed/ is not a symlink. Aborting!\n")
263
 
        sys.exit(1)
264
 
 
265
 
 
266
 
if not pat.search(cve):
267
 
    if options.embargoed:
268
 
        os.write(sys.stderr.fileno(), "Bad embargoed entry.  Should be alphanumerics and dashes\n")
269
 
    else:
270
 
        os.write(sys.stderr.fileno(), "Bad CVE entry.  Should be CVE-XXXX-XXXX\n")
271
 
    sys.exit(1)
272
 
 
273
 
# more here
274
 
pat = re.compile(r'\s')
275
 
for p in pkgs:
276
 
    tmp_p = p.split(',')
277
 
    pkgname = tmp_p[0]
278
 
    fixed_in = None
279
 
    if len(tmp_p) > 1:
280
 
        fixed_in = tmp_p[1]
281
 
    fixed_in_release = None
282
 
    fixed_in_release_version = None
283
 
    if len(tmp_p) > 3:
284
 
        fixed_in_release = tmp_p[2]
285
 
        fixed_in_release_version = tmp_p[3]
286
 
 
287
 
    if pat.search(pkgname):
288
 
        os.write(sys.stderr.fileno(), "Bad package name\n")
289
 
        sys.exit(1)
290
 
 
291
 
    if not os.path.isfile(os.path.join(boilerplates, "00boilerplate")):
292
 
        os.write(sys.stderr.fileno(), "Could not find 00boilerplate in " + cvedir + "\n")
293
 
        sys.exit(1)
294
 
 
295
 
    if (os.path.isfile(os.path.join(cvedir, cve))):
296
 
        if not options.autoconfirm:
297
 
            os.write(sys.stdout.fileno(), "Found existing " + cve + "...\n\n")
298
 
        update_cve(cve, pkgname, fixed_in, fixed_in_release, fixed_in_release_version)
299
 
    else:
300
 
        if not options.autoconfirm:
301
 
            os.write(sys.stdout.fileno(), "Creating new " + cve + "...\n\n")
302
 
        create_cve(cve, pkgname, fixed_in, fixed_in_release, fixed_in_release_version)
303
 
 
304
 
sys.exit(0)