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
diff --git a/scripts/check-cves b/scripts/check-cves
index a4541ee..2e4ac9b 100755
--- a/scripts/check-cves
+++ b/scripts/check-cves
@@ -18,7 +18,7 @@
18from datetime import datetime, timezone, date as datetime_date18from datetime import datetime, timezone, date as datetime_date
19import json19import json
20import math20import math
21import optparse21import argparse
22import os22import os
23import os.path23import os.path
24import random24import random
@@ -44,24 +44,28 @@ from uct.config import read_uct_config
44# load settings, if any44# load settings, if any
45uct_config = read_uct_config()45uct_config = read_uct_config()
4646
47parser = optparse.OptionParser()47# fmt: off
48parser.add_option("-r", "--report", help="Just report CVEs that need checking", action="store_true")48parser = argparse.ArgumentParser(prog="check_cves", description="check_cves builds UCT with new CVE data")
49parser.add_option("-v", "--verbose", help="Report verbose XML details", action="store_true")49parser.add_argument('uris', nargs='+', default=["https://cve.mitre.org/cve/downloads/allitems.xml",])
50parser.add_option("-k", "--known", help="Only report CVEs already known", action="store_true")50parser.add_argument("-r", "--report", help="Just report CVEs that need checking", action="store_true")
51parser.add_option("-N", "--skip-nfu", help="Skip any CVEs marked as NFU (used with -k)", action="store_true")51parser.add_argument("-v", "--verbose", help="Report verbose details", action="store_true")
52parser.add_option("-R", "--refresh", help="Refresh CVE descriptions", action="store_true")52parser.add_argument("-d", "--debug", help="Report debugging information", action="store_true")
53parser.add_option("-S", "--score-refresh", help="Refresh CVSS scores values only", action="store_true")53parser.add_argument("-k", "--known", help="Only report CVEs already known", action="store_true")
54parser.add_option("", "--test", help="NO LONGER SUPPORTED, see test_uct_suggestions.py", action="help")54parser.add_argument("-N", "--skip-nfu", help="Skip any CVEs marked as NFU (used with -k)", action="store_true")
55parser.add_option("--untriaged", help="Process untriaged CVEs from output of locate_cves.py", metavar="FILE")55parser.add_argument("-R", "--refresh", help="Refresh CVE descriptions", action="store_true")
56parser.add_option("--mbox", help="Process untriaged CVEs from mbox file", metavar="FILE")56parser.add_argument("-S", "--score-refresh", help="Refresh CVSS scores values only", action="store_true")
57parser.add_option("--rhel8oval", help="Process untriaged RHEL8 CVEs", metavar="FILE")57# TODO: implement --refresh choices
58parser.add_option("--import-missing-debian", help="Process missing Debian CVEs", action="store_true")58# parser.add_argument("-R", "--refresh", choices=["all", "cvss", "description", "public_date", "urls"], help="Refresh CVE data")
59parser.add_option("--debug", help="Report debugging information", action="store_true")59parser.add_argument("--untriaged", help="Process untriaged CVEs from output of locate_cves.py", metavar="FILE")
60parser.add_option("--cve", help="Check only the listed comma-separated CVEs and ignore others", action="store")60parser.add_argument("--mbox", help="Process untriaged CVEs from mbox file", metavar="FILE")
61parser.add_option("--mistriaged", help="Process the specified number of possible mistriaged CVEs compared to Debian\n"61parser.add_argument("--import-missing-debian", help="Process missing Debian CVEs", action="store_true")
62 "Implies --import-missing-debian",62# TODO: deprecate --rhel8oval, upstream data stream has ended
63 action="store", type=int, default=0)63parser.add_argument("--rhel8oval", help="Process untriaged RHEL8 CVEs", metavar="FILE")
64(opt, args) = parser.parse_args()64parser.add_argument("--cve", help="Check only the listed comma-separated CVEs and ignore others", action="store")
65parser.add_argument("-e", "--experimental", help="Enable experimental mode", action="store_true")
66parser.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)
67args = parser.parse_args()
68# fmt: on
6569
66experimental = os.getenv('CHECK_CVES_EXPERIMENTAL', False)70experimental = os.getenv('CHECK_CVES_EXPERIMENTAL', False)
67breakfix = os.getenv('CHECK_CVES_BREAKFIX', False)71breakfix = os.getenv('CHECK_CVES_BREAKFIX', False)
@@ -132,7 +136,7 @@ def _spawn_editor(path):
132136
133def debug(msg):137def debug(msg):
134 global opt138 global opt
135 if opt.debug:139 if args.debug:
136 print(msg, file=sys.stderr)140 print(msg, file=sys.stderr)
137141
138142
@@ -245,11 +249,11 @@ def import_debian(handler):
245 return False249 return False
246250
247 # pull in CVEs from data/DSA/list251 # pull in CVEs from data/DSA/list
248 dsas = cve_lib.load_debian_dsas(uct_config['secure_testing_path'] + '/data/DSA/list', opt.verbose)252 dsas = cve_lib.load_debian_dsas(uct_config['secure_testing_path'] + '/data/DSA/list', args.verbose)
249 for dsa in dsas:253 for dsa in dsas:
250 for cve in dsas[dsa]['cves']:254 for cve in dsas[dsa]['cves']:
251 if not cve_lib.CVE_RE.match(cve):255 if not cve_lib.CVE_RE.match(cve):
252 if opt.verbose:256 if args.verbose:
253 print("Skipping %s, not well-formed?" % cve, file=sys.stderr)257 print("Skipping %s, not well-formed?" % cve, file=sys.stderr)
254 continue258 continue
255259
@@ -270,27 +274,27 @@ def import_debian(handler):
270 cves[cve]['subject'] = escape(dsas[dsa]['desc'])274 cves[cve]['subject'] = escape(dsas[dsa]['desc'])
271 cves[cve]['date'] = dsas[dsa]['date']275 cves[cve]['date'] = dsas[dsa]['date']
272276
273 if opt.verbose:277 if args.verbose:
274 print("Processing %s: %s (%s)" % (dsa, dsas[dsa]['desc'], cves[cve]['date']), file=sys.stderr)278 print("Processing %s: %s (%s)" % (dsa, dsas[dsa]['desc'], cves[cve]['date']), file=sys.stderr)
275279
276 # Now pull in CVEs from the data/CVE/list280 # Now pull in CVEs from the data/CVE/list
277 for cve in handler.debian:281 for cve in handler.debian:
278 if opt.verbose:282 if args.verbose:
279 print("[--- Processing %s ---]" % cve, file=sys.stderr)283 print("[--- Processing %s ---]" % cve, file=sys.stderr)
280284
281 if cve in cves:285 if cve in cves:
282 if opt.verbose:286 if args.verbose:
283 print("Skipping %s, already found in DSA" % cve, file=sys.stderr)287 print("Skipping %s, already found in DSA" % cve, file=sys.stderr)
284 continue288 continue
285289
286 if not cve_lib.CVE_RE.match(cve):290 if not cve_lib.CVE_RE.match(cve):
287 if opt.verbose:291 if args.verbose:
288 print("Skipping %s, not well-formed?" % cve, file=sys.stderr)292 print("Skipping %s, not well-formed?" % cve, file=sys.stderr)
289 continue293 continue
290294
291 year = int(re.split('-', cve)[1])295 year = int(re.split('-', cve)[1])
292 if year < cve_limit:296 if year < cve_limit:
293 if opt.verbose:297 if args.verbose:
294 print("Skipping %s, year %d predates %d" % (cve, year, cve_limit), file=sys.stderr)298 print("Skipping %s, year %d predates %d" % (cve, year, cve_limit), file=sys.stderr)
295 continue299 continue
296300
@@ -300,7 +304,7 @@ def import_debian(handler):
300 # add a note about how this was originally classified304 # add a note about how this was originally classified
301 handler.debian[cve]['desc'] = mistriaged_hint + handler.debian[cve]['desc']305 handler.debian[cve]['desc'] = mistriaged_hint + handler.debian[cve]['desc']
302 else:306 else:
303 if opt.verbose:307 if args.verbose:
304 print("Skipping %s, already known" % cve, file=sys.stderr)308 print("Skipping %s, already known" % cve, file=sys.stderr)
305 continue309 continue
306310
@@ -316,7 +320,7 @@ def import_debian(handler):
316 date = "%s-%s-%s" % (today.year, today.month, today.day)320 date = "%s-%s-%s" % (today.year, today.month, today.day)
317 cves[cve]['date'] = datetime.strptime(date, "%Y-%m-%d")321 cves[cve]['date'] = datetime.strptime(date, "%Y-%m-%d")
318322
319 if opt.verbose:323 if args.verbose:
320 print("Processing %s: %s (%s)" % (cve, handler.debian[cve]['desc'], cves[cve]['date']), file=sys.stderr)324 print("Processing %s: %s (%s)" % (cve, handler.debian[cve]['desc'], cves[cve]['date']), file=sys.stderr)
321325
322 nvd = convert_to_nvd(cves, lambda cve: cves[cve]['subject'])326 nvd = convert_to_nvd(cves, lambda cve: cves[cve]['subject'])
@@ -347,7 +351,7 @@ class RHEL8OVALHandler(xml.sax.handler.ContentHandler):
347351
348 def startElement(self, name, attrs):352 def startElement(self, name, attrs):
349 if name == 'oval:timestamp':353 if name == 'oval:timestamp':
350 if opt.verbose:354 if args.verbose:
351 print("Parsing RHEL8 OVAL schema", file=sys.stderr)355 print("Parsing RHEL8 OVAL schema", file=sys.stderr)
352 self._curr_chars_collect = True356 self._curr_chars_collect = True
353 self._curr_chars = ""357 self._curr_chars = ""
@@ -448,11 +452,11 @@ def read_locate_cves_output(f):
448 print("Skipping malformed CVE: '%s' from '%s'" % (cve, f), file=sys.stderr)452 print("Skipping malformed CVE: '%s' from '%s'" % (cve, f), file=sys.stderr)
449 cve = None453 cve = None
450 elif cve in cves:454 elif cve in cves:
451 if opt.verbose:455 if args.verbose:
452 print("Skipping duplicate '%s' from '%s'" % (cve, f), file=sys.stderr)456 print("Skipping duplicate '%s' from '%s'" % (cve, f), file=sys.stderr)
453 cve = None457 cve = None
454 else:458 else:
455 if opt.verbose:459 if args.verbose:
456 print("Adding '%s'" % cve, file=sys.stderr)460 print("Adding '%s'" % cve, file=sys.stderr)
457 cves[cve] = dict()461 cves[cve] = dict()
458 continue462 continue
@@ -600,7 +604,7 @@ class CVEHandler(xml.sax.handler.ContentHandler):
600 # Append to timestamp file list604 # Append to timestamp file list
601 with open('%s/check-cves.log' % (destdir), 'a') as f:605 with open('%s/check-cves.log' % (destdir), 'a') as f:
602 f.write('%s UTC - %s added, %s ignored, %s skipped, %s total - files: %s\n' %606 f.write('%s UTC - %s added, %s ignored, %s skipped, %s total - files: %s\n' %
603 (timestamp, self.num_added, self.num_ignored, self.num_skipped, self.num_added + self.num_ignored, [os.path.basename(x) for x in args]))607 (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]))
604608
605 def printReport(self):609 def printReport(self):
606 print('\n============================ Triage summary =============================')610 print('\n============================ Triage summary =============================')
@@ -732,7 +736,7 @@ class CVEHandler(xml.sax.handler.ContentHandler):
732736
733 def startElement(self, name, attrs):737 def startElement(self, name, attrs):
734 if name == "item":738 if name == "item":
735 if opt.verbose:739 if args.verbose:
736 print("Parsing Mitre XML schema", file=sys.stderr)740 print("Parsing Mitre XML schema", file=sys.stderr)
737 self.curr_cve = attrs['name']741 self.curr_cve = attrs['name']
738 self.curr_refs = []742 self.curr_refs = []
@@ -772,7 +776,7 @@ class CVEHandler(xml.sax.handler.ContentHandler):
772 return776 return
773777
774 limit = cve_limit778 limit = cve_limit
775 if not opt.refresh and not opt.score_refresh:779 if not args.refresh and not args.score_refresh:
776 limit = 2005780 limit = 2005
777 if int(self.curr_cve.split("-")[1]) < limit:781 if int(self.curr_cve.split("-")[1]) < limit:
778 return782 return
@@ -1398,12 +1402,12 @@ CVEKnownList += [cve for cve in os.listdir(destdir + "/retired/") if cve.startsw
1398(ActiveList, EmbargoList) = cve_lib.get_cve_list()1402(ActiveList, EmbargoList) = cve_lib.get_cve_list()
1399CVEKnownList += [cve for cve in ActiveList if cve not in EmbargoList]1403CVEKnownList += [cve for cve in ActiveList if cve not in EmbargoList]
14001404
1401if not opt.refresh and not opt.mistriaged and not opt.score_refresh:1405if not args.refresh and not args.mistriaged and not args.score_refresh:
1402 CVEIgnoreList += CVEKnownList1406 CVEIgnoreList += CVEKnownList
14031407
1404if opt.known:1408if args.known:
1405 cvelist = CVEIgnoreList1409 cvelist = CVEIgnoreList
1406 if opt.skip_nfu:1410 if args.skip_nfu:
1407 cvelist = CVEKnownList1411 cvelist = CVEKnownList
1408 for cve in sorted(cvelist):1412 for cve in sorted(cvelist):
1409 print(cve)1413 print(cve)
@@ -1416,9 +1420,9 @@ parser.setContentHandler(handler)
1416# if has specified to triage only specific CVEs, check these are not1420# if has specified to triage only specific CVEs, check these are not
1417# ignored1421# ignored
1418specific_cves = None1422specific_cves = None
1419if opt.cve:1423if args.cve:
1420 specific_cves = set()1424 specific_cves = set()
1421 for cve in opt.cve.split(","):1425 for cve in args.cve.split(","):
1422 # ignore empty CVE1426 # ignore empty CVE
1423 if cve.strip() == "":1427 if cve.strip() == "":
1424 continue1428 continue
@@ -1429,28 +1433,26 @@ if opt.cve:
1429 specific_cves.add(cve)1433 specific_cves.add(cve)
14301434
1431untriaged_json = ""1435untriaged_json = ""
1432if opt.untriaged:1436if args.untriaged:
1433 untriaged_json = read_locate_cves_output(opt.untriaged)1437 untriaged_json = read_locate_cves_output(args.untriaged)
1434 args.append(untriaged_json)1438 args.uris.append(untriaged_json)
14351439
1436if opt.mbox:1440if args.mbox:
1437 untriaged_json = read_mbox_file(opt.mbox)1441 untriaged_json = read_mbox_file(args.mbox)
1438 args.append(untriaged_json)1442 args.uris.append(untriaged_json)
14391443
1440rhel8oval_import_json = ""1444rhel8oval_import_json = ""
1441if opt.rhel8oval:1445if args.rhel8oval:
1442 untriaged_json = read_rhel8oval_file(opt.rhel8oval)1446 untriaged_json = read_rhel8oval_file(args.rhel8oval)
1443 args.append(untriaged_json)1447 args.uris.append(untriaged_json)
14441448
1445debian_import_json = ""1449debian_import_json = ""
1446if (opt.import_missing_debian or opt.mistriaged) and handler.debian is not None:1450if (args.import_missing_debian or args.mistriaged) and handler.debian is not None:
1447 debian_import_json = import_debian(handler)1451 debian_import_json = import_debian(handler)
1448 args.append(debian_import_json)1452 args.uris.append(debian_import_json)
14491453
1450if len(args) == 0:
1451 args.append("https://cve.mitre.org/cve/downloads/allitems.xml")
14521454
1453for uri in args:1455for uri in args.uris:
1454 print('Loading %s ...' % (uri), file=sys.stderr)1456 print('Loading %s ...' % (uri), file=sys.stderr)
1455 if '://' in uri:1457 if '://' in uri:
1456 readable = urllib.request.urlopen(uri)1458 readable = urllib.request.urlopen(uri)
@@ -1487,7 +1489,7 @@ def refresh_cves(cve_refresh_list, full_refresh=True):
1487 public = handler.cve_data[cve]['public']1489 public = handler.cve_data[cve]['public']
1488 cvsss = handler.cve_data[cve]['cvss']1490 cvsss = handler.cve_data[cve]['cvss']
1489 except:1491 except:
1490 if opt.verbose:1492 if args.verbose:
1491 print('%s not listed in XML' % (cve), file=sys.stderr)1493 print('%s not listed in XML' % (cve), file=sys.stderr)
14921494
1493 # Find the on-disk CVE file1495 # Find the on-disk CVE file
@@ -1552,17 +1554,17 @@ def refresh_cves(cve_refresh_list, full_refresh=True):
1552 print("Refreshed %s" % (cvefile), file=sys.stderr)1554 print("Refreshed %s" % (cvefile), file=sys.stderr)
15531555
15541556
1555if opt.refresh or opt.score_refresh:1557if args.refresh or args.score_refresh:
1556 if opt.cve and specific_cves is not set():1558 if args.cve and specific_cves is not set():
1557 cve_refresh_list = specific_cves1559 cve_refresh_list = specific_cves
1558 else:1560 else:
1559 cve_refresh_list = CVEKnownList1561 cve_refresh_list = CVEKnownList
15601562
1561 # with OptParse opt.refresh and opt.score_refresh will each1563 # with OptParse args.refresh and args.score_refresh will each
1562 # either be True or None. We want full_refresh to be False when1564 # either be True or None. We want full_refresh to be False when
1563 # opt.score_refresh is True. If both are true, then we'll do a full1565 # args.score_refresh is True. If both are true, then we'll do a full
1564 # refresh since it's a superset of the score only refresh.1566 # refresh since it's a superset of the score only refresh.
1565 full_refresh = opt.refresh or not opt.score_refresh1567 full_refresh = args.refresh or not args.score_refresh
1566 refresh_cves(cve_refresh_list, full_refresh=full_refresh)1568 refresh_cves(cve_refresh_list, full_refresh=full_refresh)
1567 sys.exit(0)1569 sys.exit(0)
15681570
@@ -1575,13 +1577,13 @@ if experimental:
1575 handler.display_command_file_usage(fout, '# ')1577 handler.display_command_file_usage(fout, '# ')
15761578
1577for cve in new_cves:1579for cve in new_cves:
1578 if opt.cve and cve not in specific_cves:1580 if args.cve and cve not in specific_cves:
1579 # ignore this cve1581 # ignore this cve
1580 continue1582 continue
1581 # if this got marked as mistriaged, probablistically choose it for1583 # if this got marked as mistriaged, probablistically choose it for
1582 # processing1584 # processing
1583 if mistriaged_hint in handler.cve_data[cve]['desc']:1585 if mistriaged_hint in handler.cve_data[cve]['desc']:
1584 if opt.mistriaged == 0:1586 if args.mistriaged == 0:
1585 # ignore this one1587 # ignore this one
1586 continue1588 continue
1587 else:1589 else:
@@ -1596,11 +1598,11 @@ for cve in new_cves:
1596 if rand > prob:1598 if rand > prob:
1597 continue1599 continue
1598 # selected!1600 # selected!
1599 opt.mistriaged = opt.mistriaged - 11601 args.mistriaged = args.mistriaged - 1
16001602
1601 count += 11603 count += 1
16021604
1603 if opt.report:1605 if args.report:
1604 print(cve)1606 print(cve)
1605 continue1607 continue
16061608
@@ -1653,6 +1655,6 @@ if experimental:
1653 fout.seek(0)1655 fout.seek(0)
1654 handler.process_command_file(fout)1656 handler.process_command_file(fout)
16551657
1656if not opt.report:1658if not args.report:
1657 handler.updateTimestamp()1659 handler.updateTimestamp()
1658handler.printReport()1660handler.printReport()

Subscribers

People subscribed via source and target branches