Merge ubuntu-cve-tracker:check-cves-argparse into ubuntu-cve-tracker:master
- Git
- lp:ubuntu-cve-tracker
- check-cves-argparse
- Merge into master
Status: | Merged |
---|---|
Merged at revision: | d21d58c53de7daca802315de55c383e9206048fd |
Proposed branch: | ubuntu-cve-tracker:check-cves-argparse |
Merge into: | ubuntu-cve-tracker:master |
Diff against target: |
323 lines (+67/-65) 1 file modified
scripts/check-cves (+67/-65) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Marc Deslauriers | Approve | ||
Review via email: mp+462473@code.launchpad.net |
Commit message
Deprecate optparse for argparse.
Description of the change
optparse is dead, long live argparse
> Deprecated since version 3.2: The optparse module is deprecated and will not be developed further; development will continue with the argparse module. [0]
Apologize for not using my fork.
Mark Esler (eslerm) wrote : | # |
Marc Deslauriers (mdeslaur) wrote : | # |
LGTM, ack, thanks!
Mark Esler (eslerm) wrote : | # |
Thanks Marc :)
Steve Beattie (sbeattie) wrote : | # |
On Fri, Mar 15, 2024 at 05:42:17AM -0000, Mark Esler wrote:
> argparse adds features, such as `choices`, I plan to use later
Note that this branch made a subtle change to the script's behavior;
when check-cves --import-
without a uri(s) argument; this branch or the followup commit
59653e29fcc (check-cves: allow argparse to use default uri if unset,
2024-03-15) causes the mitre-allitems to be loaded by check-cves.
Now, in an ideal world, this would actually happen and the description
from the mitre index would be used if its available when presenting
CVEs debian's tracker has but UCT does not yet, because debian's
description is significantly truncated. But that's not how check-cves
is currently implemented.
--
Steve Beattie
<email address hidden>
Preview Diff
1 | diff --git a/scripts/check-cves b/scripts/check-cves |
2 | index a4541ee..2e4ac9b 100755 |
3 | --- a/scripts/check-cves |
4 | +++ b/scripts/check-cves |
5 | @@ -18,7 +18,7 @@ |
6 | from datetime import datetime, timezone, date as datetime_date |
7 | import json |
8 | import math |
9 | -import optparse |
10 | +import argparse |
11 | import os |
12 | import os.path |
13 | import random |
14 | @@ -44,24 +44,28 @@ from uct.config import read_uct_config |
15 | # load settings, if any |
16 | uct_config = read_uct_config() |
17 | |
18 | -parser = optparse.OptionParser() |
19 | -parser.add_option("-r", "--report", help="Just report CVEs that need checking", action="store_true") |
20 | -parser.add_option("-v", "--verbose", help="Report verbose XML details", action="store_true") |
21 | -parser.add_option("-k", "--known", help="Only report CVEs already known", action="store_true") |
22 | -parser.add_option("-N", "--skip-nfu", help="Skip any CVEs marked as NFU (used with -k)", action="store_true") |
23 | -parser.add_option("-R", "--refresh", help="Refresh CVE descriptions", action="store_true") |
24 | -parser.add_option("-S", "--score-refresh", help="Refresh CVSS scores values only", action="store_true") |
25 | -parser.add_option("", "--test", help="NO LONGER SUPPORTED, see test_uct_suggestions.py", action="help") |
26 | -parser.add_option("--untriaged", help="Process untriaged CVEs from output of locate_cves.py", metavar="FILE") |
27 | -parser.add_option("--mbox", help="Process untriaged CVEs from mbox file", metavar="FILE") |
28 | -parser.add_option("--rhel8oval", help="Process untriaged RHEL8 CVEs", metavar="FILE") |
29 | -parser.add_option("--import-missing-debian", help="Process missing Debian CVEs", action="store_true") |
30 | -parser.add_option("--debug", help="Report debugging information", action="store_true") |
31 | -parser.add_option("--cve", help="Check only the listed comma-separated CVEs and ignore others", action="store") |
32 | -parser.add_option("--mistriaged", help="Process the specified number of possible mistriaged CVEs compared to Debian\n" |
33 | - "Implies --import-missing-debian", |
34 | - action="store", type=int, default=0) |
35 | -(opt, args) = parser.parse_args() |
36 | +# fmt: off |
37 | +parser = argparse.ArgumentParser(prog="check_cves", description="check_cves builds UCT with new CVE data") |
38 | +parser.add_argument('uris', nargs='+', default=["https://cve.mitre.org/cve/downloads/allitems.xml",]) |
39 | +parser.add_argument("-r", "--report", help="Just report CVEs that need checking", action="store_true") |
40 | +parser.add_argument("-v", "--verbose", help="Report verbose details", action="store_true") |
41 | +parser.add_argument("-d", "--debug", help="Report debugging information", action="store_true") |
42 | +parser.add_argument("-k", "--known", help="Only report CVEs already known", action="store_true") |
43 | +parser.add_argument("-N", "--skip-nfu", help="Skip any CVEs marked as NFU (used with -k)", action="store_true") |
44 | +parser.add_argument("-R", "--refresh", help="Refresh CVE descriptions", action="store_true") |
45 | +parser.add_argument("-S", "--score-refresh", help="Refresh CVSS scores values only", action="store_true") |
46 | +# TODO: implement --refresh choices |
47 | +# parser.add_argument("-R", "--refresh", choices=["all", "cvss", "description", "public_date", "urls"], help="Refresh CVE data") |
48 | +parser.add_argument("--untriaged", help="Process untriaged CVEs from output of locate_cves.py", metavar="FILE") |
49 | +parser.add_argument("--mbox", help="Process untriaged CVEs from mbox file", metavar="FILE") |
50 | +parser.add_argument("--import-missing-debian", help="Process missing Debian CVEs", action="store_true") |
51 | +# TODO: deprecate --rhel8oval, upstream data stream has ended |
52 | +parser.add_argument("--rhel8oval", help="Process untriaged RHEL8 CVEs", metavar="FILE") |
53 | +parser.add_argument("--cve", help="Check only the listed comma-separated CVEs and ignore others", action="store") |
54 | +parser.add_argument("-e", "--experimental", help="Enable experimental mode", action="store_true") |
55 | +parser.add_argument("--mistriaged", help="Process the specified number of possible mistriaged CVEs compared to Debian\nImplies --import-missing-debian", action="store", type=int, default=0) |
56 | +args = parser.parse_args() |
57 | +# fmt: on |
58 | |
59 | experimental = os.getenv('CHECK_CVES_EXPERIMENTAL', False) |
60 | breakfix = os.getenv('CHECK_CVES_BREAKFIX', False) |
61 | @@ -132,7 +136,7 @@ def _spawn_editor(path): |
62 | |
63 | def debug(msg): |
64 | global opt |
65 | - if opt.debug: |
66 | + if args.debug: |
67 | print(msg, file=sys.stderr) |
68 | |
69 | |
70 | @@ -245,11 +249,11 @@ def import_debian(handler): |
71 | return False |
72 | |
73 | # pull in CVEs from data/DSA/list |
74 | - dsas = cve_lib.load_debian_dsas(uct_config['secure_testing_path'] + '/data/DSA/list', opt.verbose) |
75 | + dsas = cve_lib.load_debian_dsas(uct_config['secure_testing_path'] + '/data/DSA/list', args.verbose) |
76 | for dsa in dsas: |
77 | for cve in dsas[dsa]['cves']: |
78 | if not cve_lib.CVE_RE.match(cve): |
79 | - if opt.verbose: |
80 | + if args.verbose: |
81 | print("Skipping %s, not well-formed?" % cve, file=sys.stderr) |
82 | continue |
83 | |
84 | @@ -270,27 +274,27 @@ def import_debian(handler): |
85 | cves[cve]['subject'] = escape(dsas[dsa]['desc']) |
86 | cves[cve]['date'] = dsas[dsa]['date'] |
87 | |
88 | - if opt.verbose: |
89 | + if args.verbose: |
90 | print("Processing %s: %s (%s)" % (dsa, dsas[dsa]['desc'], cves[cve]['date']), file=sys.stderr) |
91 | |
92 | # Now pull in CVEs from the data/CVE/list |
93 | for cve in handler.debian: |
94 | - if opt.verbose: |
95 | + if args.verbose: |
96 | print("[--- Processing %s ---]" % cve, file=sys.stderr) |
97 | |
98 | if cve in cves: |
99 | - if opt.verbose: |
100 | + if args.verbose: |
101 | print("Skipping %s, already found in DSA" % cve, file=sys.stderr) |
102 | continue |
103 | |
104 | if not cve_lib.CVE_RE.match(cve): |
105 | - if opt.verbose: |
106 | + if args.verbose: |
107 | print("Skipping %s, not well-formed?" % cve, file=sys.stderr) |
108 | continue |
109 | |
110 | year = int(re.split('-', cve)[1]) |
111 | if year < cve_limit: |
112 | - if opt.verbose: |
113 | + if args.verbose: |
114 | print("Skipping %s, year %d predates %d" % (cve, year, cve_limit), file=sys.stderr) |
115 | continue |
116 | |
117 | @@ -300,7 +304,7 @@ def import_debian(handler): |
118 | # add a note about how this was originally classified |
119 | handler.debian[cve]['desc'] = mistriaged_hint + handler.debian[cve]['desc'] |
120 | else: |
121 | - if opt.verbose: |
122 | + if args.verbose: |
123 | print("Skipping %s, already known" % cve, file=sys.stderr) |
124 | continue |
125 | |
126 | @@ -316,7 +320,7 @@ def import_debian(handler): |
127 | date = "%s-%s-%s" % (today.year, today.month, today.day) |
128 | cves[cve]['date'] = datetime.strptime(date, "%Y-%m-%d") |
129 | |
130 | - if opt.verbose: |
131 | + if args.verbose: |
132 | print("Processing %s: %s (%s)" % (cve, handler.debian[cve]['desc'], cves[cve]['date']), file=sys.stderr) |
133 | |
134 | nvd = convert_to_nvd(cves, lambda cve: cves[cve]['subject']) |
135 | @@ -347,7 +351,7 @@ class RHEL8OVALHandler(xml.sax.handler.ContentHandler): |
136 | |
137 | def startElement(self, name, attrs): |
138 | if name == 'oval:timestamp': |
139 | - if opt.verbose: |
140 | + if args.verbose: |
141 | print("Parsing RHEL8 OVAL schema", file=sys.stderr) |
142 | self._curr_chars_collect = True |
143 | self._curr_chars = "" |
144 | @@ -448,11 +452,11 @@ def read_locate_cves_output(f): |
145 | print("Skipping malformed CVE: '%s' from '%s'" % (cve, f), file=sys.stderr) |
146 | cve = None |
147 | elif cve in cves: |
148 | - if opt.verbose: |
149 | + if args.verbose: |
150 | print("Skipping duplicate '%s' from '%s'" % (cve, f), file=sys.stderr) |
151 | cve = None |
152 | else: |
153 | - if opt.verbose: |
154 | + if args.verbose: |
155 | print("Adding '%s'" % cve, file=sys.stderr) |
156 | cves[cve] = dict() |
157 | continue |
158 | @@ -600,7 +604,7 @@ class CVEHandler(xml.sax.handler.ContentHandler): |
159 | # Append to timestamp file list |
160 | with open('%s/check-cves.log' % (destdir), 'a') as f: |
161 | f.write('%s UTC - %s added, %s ignored, %s skipped, %s total - files: %s\n' % |
162 | - (timestamp, self.num_added, self.num_ignored, self.num_skipped, self.num_added + self.num_ignored, [os.path.basename(x) for x in args])) |
163 | + (timestamp, self.num_added, self.num_ignored, self.num_skipped, self.num_added + self.num_ignored, [os.path.basename(x) for x in args.uris])) |
164 | |
165 | def printReport(self): |
166 | print('\n============================ Triage summary =============================') |
167 | @@ -732,7 +736,7 @@ class CVEHandler(xml.sax.handler.ContentHandler): |
168 | |
169 | def startElement(self, name, attrs): |
170 | if name == "item": |
171 | - if opt.verbose: |
172 | + if args.verbose: |
173 | print("Parsing Mitre XML schema", file=sys.stderr) |
174 | self.curr_cve = attrs['name'] |
175 | self.curr_refs = [] |
176 | @@ -772,7 +776,7 @@ class CVEHandler(xml.sax.handler.ContentHandler): |
177 | return |
178 | |
179 | limit = cve_limit |
180 | - if not opt.refresh and not opt.score_refresh: |
181 | + if not args.refresh and not args.score_refresh: |
182 | limit = 2005 |
183 | if int(self.curr_cve.split("-")[1]) < limit: |
184 | return |
185 | @@ -1398,12 +1402,12 @@ CVEKnownList += [cve for cve in os.listdir(destdir + "/retired/") if cve.startsw |
186 | (ActiveList, EmbargoList) = cve_lib.get_cve_list() |
187 | CVEKnownList += [cve for cve in ActiveList if cve not in EmbargoList] |
188 | |
189 | -if not opt.refresh and not opt.mistriaged and not opt.score_refresh: |
190 | +if not args.refresh and not args.mistriaged and not args.score_refresh: |
191 | CVEIgnoreList += CVEKnownList |
192 | |
193 | -if opt.known: |
194 | +if args.known: |
195 | cvelist = CVEIgnoreList |
196 | - if opt.skip_nfu: |
197 | + if args.skip_nfu: |
198 | cvelist = CVEKnownList |
199 | for cve in sorted(cvelist): |
200 | print(cve) |
201 | @@ -1416,9 +1420,9 @@ parser.setContentHandler(handler) |
202 | # if has specified to triage only specific CVEs, check these are not |
203 | # ignored |
204 | specific_cves = None |
205 | -if opt.cve: |
206 | +if args.cve: |
207 | specific_cves = set() |
208 | - for cve in opt.cve.split(","): |
209 | + for cve in args.cve.split(","): |
210 | # ignore empty CVE |
211 | if cve.strip() == "": |
212 | continue |
213 | @@ -1429,28 +1433,26 @@ if opt.cve: |
214 | specific_cves.add(cve) |
215 | |
216 | untriaged_json = "" |
217 | -if opt.untriaged: |
218 | - untriaged_json = read_locate_cves_output(opt.untriaged) |
219 | - args.append(untriaged_json) |
220 | +if args.untriaged: |
221 | + untriaged_json = read_locate_cves_output(args.untriaged) |
222 | + args.uris.append(untriaged_json) |
223 | |
224 | -if opt.mbox: |
225 | - untriaged_json = read_mbox_file(opt.mbox) |
226 | - args.append(untriaged_json) |
227 | +if args.mbox: |
228 | + untriaged_json = read_mbox_file(args.mbox) |
229 | + args.uris.append(untriaged_json) |
230 | |
231 | rhel8oval_import_json = "" |
232 | -if opt.rhel8oval: |
233 | - untriaged_json = read_rhel8oval_file(opt.rhel8oval) |
234 | - args.append(untriaged_json) |
235 | +if args.rhel8oval: |
236 | + untriaged_json = read_rhel8oval_file(args.rhel8oval) |
237 | + args.uris.append(untriaged_json) |
238 | |
239 | debian_import_json = "" |
240 | -if (opt.import_missing_debian or opt.mistriaged) and handler.debian is not None: |
241 | +if (args.import_missing_debian or args.mistriaged) and handler.debian is not None: |
242 | debian_import_json = import_debian(handler) |
243 | - args.append(debian_import_json) |
244 | + args.uris.append(debian_import_json) |
245 | |
246 | -if len(args) == 0: |
247 | - args.append("https://cve.mitre.org/cve/downloads/allitems.xml") |
248 | |
249 | -for uri in args: |
250 | +for uri in args.uris: |
251 | print('Loading %s ...' % (uri), file=sys.stderr) |
252 | if '://' in uri: |
253 | readable = urllib.request.urlopen(uri) |
254 | @@ -1487,7 +1489,7 @@ def refresh_cves(cve_refresh_list, full_refresh=True): |
255 | public = handler.cve_data[cve]['public'] |
256 | cvsss = handler.cve_data[cve]['cvss'] |
257 | except: |
258 | - if opt.verbose: |
259 | + if args.verbose: |
260 | print('%s not listed in XML' % (cve), file=sys.stderr) |
261 | |
262 | # Find the on-disk CVE file |
263 | @@ -1552,17 +1554,17 @@ def refresh_cves(cve_refresh_list, full_refresh=True): |
264 | print("Refreshed %s" % (cvefile), file=sys.stderr) |
265 | |
266 | |
267 | -if opt.refresh or opt.score_refresh: |
268 | - if opt.cve and specific_cves is not set(): |
269 | +if args.refresh or args.score_refresh: |
270 | + if args.cve and specific_cves is not set(): |
271 | cve_refresh_list = specific_cves |
272 | else: |
273 | cve_refresh_list = CVEKnownList |
274 | |
275 | - # with OptParse opt.refresh and opt.score_refresh will each |
276 | + # with OptParse args.refresh and args.score_refresh will each |
277 | # either be True or None. We want full_refresh to be False when |
278 | - # opt.score_refresh is True. If both are true, then we'll do a full |
279 | + # args.score_refresh is True. If both are true, then we'll do a full |
280 | # refresh since it's a superset of the score only refresh. |
281 | - full_refresh = opt.refresh or not opt.score_refresh |
282 | + full_refresh = args.refresh or not args.score_refresh |
283 | refresh_cves(cve_refresh_list, full_refresh=full_refresh) |
284 | sys.exit(0) |
285 | |
286 | @@ -1575,13 +1577,13 @@ if experimental: |
287 | handler.display_command_file_usage(fout, '# ') |
288 | |
289 | for cve in new_cves: |
290 | - if opt.cve and cve not in specific_cves: |
291 | + if args.cve and cve not in specific_cves: |
292 | # ignore this cve |
293 | continue |
294 | # if this got marked as mistriaged, probablistically choose it for |
295 | # processing |
296 | if mistriaged_hint in handler.cve_data[cve]['desc']: |
297 | - if opt.mistriaged == 0: |
298 | + if args.mistriaged == 0: |
299 | # ignore this one |
300 | continue |
301 | else: |
302 | @@ -1596,11 +1598,11 @@ for cve in new_cves: |
303 | if rand > prob: |
304 | continue |
305 | # selected! |
306 | - opt.mistriaged = opt.mistriaged - 1 |
307 | + args.mistriaged = args.mistriaged - 1 |
308 | |
309 | count += 1 |
310 | |
311 | - if opt.report: |
312 | + if args.report: |
313 | print(cve) |
314 | continue |
315 | |
316 | @@ -1653,6 +1655,6 @@ if experimental: |
317 | fout.seek(0) |
318 | handler.process_command_file(fout) |
319 | |
320 | -if not opt.report: |
321 | +if not args.report: |
322 | handler.updateTimestamp() |
323 | handler.printReport() |
argparse adds features, such as `choices`, I plan to use later