Merge ubuntu-cve-tracker:check-cves-argparse into ubuntu-cve-tracker:master

Proposed by Mark Esler
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)
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.

[0] https://docs.python.org/3/library/optparse.html

To post a comment you must log in.
Revision history for this message
Mark Esler (eslerm) wrote :

argparse adds features, such as `choices`, I plan to use later

Revision history for this message
Marc Deslauriers (mdeslaur) wrote :

LGTM, ack, thanks!

review: Approve
Revision history for this message
Mark Esler (eslerm) wrote :

Thanks Marc :)

Revision history for this message
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-missing-debian is invoked, it is invoked
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/scripts/check-cves b/scripts/check-cves
2index 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()

Subscribers

People subscribed via source and target branches