aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Reyna <David.Reyna@windriver.com>2020-01-26 16:07:39 -0800
committerDavid Reyna <David.Reyna@windriver.com>2020-01-26 16:07:39 -0800
commit6ff000a1f67d22a68a7d3662636334c72f3fe81f (patch)
treebaa5bf90b8f0cee1b034e2b47d6b6f3e390f4df3
parent0895afc7175d7a817dc5d5231cc54d711c0443a1 (diff)
downloadsrtool-6ff000a1f67d22a68a7d3662636334c72f3fe81f.zip
srtool-6ff000a1f67d22a68a7d3662636334c72f3fe81f.tar.gz
srtool-6ff000a1f67d22a68a7d3662636334c72f3fe81f.tar.bz2
srtool: fix NIST modified pre-emption, bad cvesource mappings
1.Fix NIST modified pre-emption * When a CVE appears in the 'Modified' source * Remove the CVE link to the normal source * Add the CVE link to the modified source * When a CVE disappears from the 'Modified' source * Remove the CVE link from the modified source * Restore the CVE link to the normal source * If forcing a normal source update, first gather the CVEs in the current "Modified" source, and ignore them when scanning the normal source. This is to avoid regressive updates. * Add tracing to help validate this workflow. 2. Fix sql_cve_query() to always return a valid cve_id even when there are no updates, to avoid adding cvesource mappings to '-1'. This addresses one of the issues in '--find-bad-links'. 3. In the NIST details web page, force the display of impact and exploit scores to two decimal places, to normalize the current NIST feeds that are outputting 8+ decimal places. Signed-off-by: David Reyna <David.Reyna@windriver.com>
-rwxr-xr-xbin/common/srtool_utils.py83
-rwxr-xr-xbin/dev_tools/start.sh3
-rwxr-xr-xbin/nist/srtool_nist.py200
-rwxr-xr-xlib/srtgui/templates/cve-nist-local.html8
-rwxr-xr-xlib/srtgui/templates/cve-nist.html8
5 files changed, 257 insertions, 45 deletions
diff --git a/bin/common/srtool_utils.py b/bin/common/srtool_utils.py
index 573a86d..0ac6471 100755
--- a/bin/common/srtool_utils.py
+++ b/bin/common/srtool_utils.py
@@ -1106,6 +1106,9 @@ def fix_severity(datasource_list):
DATA_MAP_V2_Severity = 3
+ # Allow "MOD" as shorthand for the modification datasource
+ datasource_list = datasource_list.replace('MOD','NIST Modified Data')
+
#
# Gather the NIST data source list
#
@@ -1149,6 +1152,9 @@ def fix_severity(datasource_list):
#
try:
+ if not os.path.isfile(nist_file):
+ print("ERROR: no such file '%s'" % nist_file)
+ exit(1)
f = open(nist_file, 'r')
source_dct = json.load(f)
for item in source_dct["CVE_Items"]:
@@ -1197,15 +1203,19 @@ def fix_severity(datasource_list):
cur_cve.execute('SELECT * FROM orm_cve WHERE name = "%s"' % cve_name)
cve = cur_cve.fetchone()
if not cve:
- print("WARNING: MISSING CVE in orm_cvesource '%d:%d' : %s" % (cvesource[ORM.CVESOURCE_CVE_ID],cvesource[ORM.CVESOURCE_DATASOURCE_ID],ds[ORM.DATASOURCE_DESCRIPTION]))
+ print("WARNING: MISSING CVE in orm_cvesource : %s" % cve_name)
continue
cve_name = cve[ORM.CVE_NAME]
if cve_name in nist_data_map:
fix_count += 1
if (nist_data_map[cve_name][DATA_MAP_V3_Score] != cve[ORM.CVE_CVSSV3_BASESCORE]) or (nist_data_map[cve_name][DATA_MAP_V3_Severity] != cve[ORM.CVE_CVSSV3_BASESEVERITY]) or \
(nist_data_map[cve_name][DATA_MAP_V2_Score] != cve[ORM.CVE_CVSSV2_BASESCORE]) or (nist_data_map[cve_name][DATA_MAP_V2_Severity] != cve[ORM.CVE_CVSSV2_SEVERITY ]):
- print("CHANGE: %s V3(%s to %s,%s to %s)V2(%s to %s,%s to %s)" % (cve_name,cve[ORM.CVE_CVSSV3_BASESCORE],nist_data_map[cve_name][DATA_MAP_V3_Score],cve[ORM.CVE_CVSSV3_BASESEVERITY],nist_data_map[cve_name][DATA_MAP_V3_Severity],
- cve[ORM.CVE_CVSSV2_BASESCORE],nist_data_map[cve_name][DATA_MAP_V2_Score],cve[ORM.CVE_CVSSV2_SEVERITY ],nist_data_map[cve_name][DATA_MAP_V2_Severity]))
+ print("CHANGE: %s V3(%s to %s,%s to %s)V2(%s to %s,%s to %s) (%s,%s)" % (
+ cve_name,
+ cve[ORM.CVE_CVSSV3_BASESCORE],nist_data_map[cve_name][DATA_MAP_V3_Score],cve[ORM.CVE_CVSSV3_BASESEVERITY],nist_data_map[cve_name][DATA_MAP_V3_Severity],
+ cve[ORM.CVE_CVSSV2_BASESCORE],nist_data_map[cve_name][DATA_MAP_V2_Score],cve[ORM.CVE_CVSSV2_SEVERITY ],nist_data_map[cve_name][DATA_MAP_V2_Severity],
+ ORM.get_orm_string(cve[ORM.CVE_STATUS],ORM.STATUS_STR),cve[ORM.CVE_COMMENTS],
+ ))
if force:
sql = ''' UPDATE orm_cve
@@ -1259,6 +1269,56 @@ def fix_severity(datasource_list):
print("CVE COUNT=%d, fix_count=%d" % (cve_count,fix_count))
if force: conn.commit()
+#
+# Trim the V3/V2 scores to one decimal place, in line with NIST public pages
+#
+
+def fix_trim_cve_scores():
+ conn = sqlite3.connect(srtDbName)
+ cur = conn.cursor()
+ cur_wr = conn.cursor()
+
+ def fixscore(score):
+ if not score:
+ return ''
+ return '%02.1f' % float(score)
+
+ cve_count = 0
+ fix_count = 0
+
+ cur.execute('SELECT * FROM orm_cve')
+ for i,cve in enumerate(cur):
+ if 0 == i % 100:
+ print("%4d) C=%-30s\r" % (i,cve[ORM.CVE_NAME]), end='')
+
+ new_v3score = fixscore(cve[ORM.CVE_CVSSV3_BASESCORE])
+ new_v2score = fixscore(cve[ORM.CVE_CVSSV2_BASESCORE])
+
+ if (new_v3score != cve[ORM.CVE_CVSSV3_BASESCORE]) or (new_v2score != cve[ORM.CVE_CVSSV2_BASESCORE]):
+ fix_count += 1
+ if verbose:
+ print("CHANGE:%s:%s to %s,%s to %s" % (cve[ORM.CVE_NAME],cve[ORM.CVE_CVSSV3_BASESCORE],new_v3score,cve[ORM.CVE_CVSSV2_BASESCORE],new_v2score))
+
+ if force:
+ sql = ''' UPDATE orm_cve
+ SET cvssV3_baseScore = ?, cvssV2_baseScore = ?
+ WHERE id = ?'''
+ cur_wr.execute(sql, (new_v3score,new_v2score,cve[ORM.CVE_ID],))
+
+ # Development/debug support
+ cve_count += 1
+ if cmd_skip and (cve_count < cmd_skip): continue
+ if cmd_count and ((cve_count - cmd_skip) > cmd_count): break
+
+ # Progress indicator support
+ if (0 == cve_count % 1000):
+ print('%05d: %-20s\r' % (cve_count,cve[ORM.CVE_NAME]), end='')
+ if force: conn.commit()
+ print('')
+ pass
+
+ if force: conn.commit()
+ print("CVE COUNT=%d, fix_count=%d" % (cve_count,fix_count))
# Sample code that does a CVE lookup data fetch and CVE update
#def example_datasource_lookup(cve,nist_ds,cvesource,cur):
@@ -1405,6 +1465,13 @@ def find_bad_links():
print('\n=== CVE Source Check ===\n')
#
+ # Find the data source mapping
+ cur.execute('SELECT * FROM orm_datasource;')
+ datasource_map = {}
+ for datasource in cur:
+ # DataSource Map is [cve_file,ds_desc,ds_lastmodifieddate,ds_lastupdateddate]
+ datasource_map[datasource[ORM.DATASOURCE_ID]] = datasource[ORM.DATASOURCE_DESCRIPTION]
+
cur.execute('SELECT * FROM orm_cvesource')
is_change = False
for i,cs in enumerate(cur):
@@ -1417,7 +1484,7 @@ def find_bad_links():
if (1 > srcid): error = True
if error:
- print("ERROR: [%4d] CVE=%6d,SRC=%6d" % (cs[ORM.CVESOURCE_ID],cveid,srcid))
+ print("ERROR: [%4d] CVE=%6d,SRC=%6d (%s)" % (cs[ORM.CVESOURCE_ID],cveid,srcid,datasource_map[srcid]))
if force:
sql = 'DELETE FROM orm_cvesource WHERE id=?'
cur_del.execute(sql, (cs[ORM.CVESOURCE_ID],))
@@ -1561,15 +1628,16 @@ def main(argv):
parser.add_argument('--fix-remove-bulk-cve-history', action='store_const', const='fix_remove_bulk_cve_history', dest='command', help='foo')
parser.add_argument('--fix-bad-mitre-init', action='store_const', const='fix_bad_mitre_init', dest='command', help='foo')
parser.add_argument('--fix-bad-new', action='store_const', const='fix_bad_new', dest='command', help='foo')
+ parser.add_argument('--find-empty-status', action='store_const', const='find_empty_status', dest='command', help='foo')
parser.add_argument('--fix-severity', dest='fix_severity', help='Fix bad score/severity values, broken cve source links')
- parser.add_argument('--find-empty-status', action='store_const', const='find_empty_status', dest='command', help='foo')
+ parser.add_argument('--fix-trim-cve-scores', action='store_const', const='fix_trim_cve_scores', dest='command', help='Trim V3/V2 scores to one decimal place standard')
parser.add_argument('--find-multiple-defects', action='store_const', const='find_multiple_defects', dest='command', help='foo')
parser.add_argument('--find-duplicate-names', action='store_const', const='find_duplicate_names', dest='command', help='foo')
parser.add_argument('--fix-defects-to-products', action='store_const', const='fix_defects_to_products', dest='command', help='foo')
- parser.add_argument('--find-bad-links', action='store_const', const='find_bad_links', dest='command', help='Find bad links, e.g. "orm_cvesource" (with "-f" to fix)')
+ parser.add_argument('--find-bad-links', action='store_const', const='find_bad_links', dest='command', help='Find bad links, e.g. "orm_cvesource" (add "-f" to fix)')
parser.add_argument('--database', '-D', dest='database', help='Selected database file')
@@ -1637,8 +1705,11 @@ def main(argv):
fix_bad_mitre_init()
elif 'fix_bad_new' == args.command:
fix_bad_new()
+
elif args.fix_severity:
fix_severity(args.fix_severity)
+ elif 'fix_trim_cve_scores' == args.command:
+ fix_trim_cve_scores()
elif 'find_multiple_defects' == args.command:
find_multiple_defects()
diff --git a/bin/dev_tools/start.sh b/bin/dev_tools/start.sh
index 6d6515b..f73dc7b 100755
--- a/bin/dev_tools/start.sh
+++ b/bin/dev_tools/start.sh
@@ -6,5 +6,6 @@ if [ -z "$SRT_PORT" ] ; then
SRT_PORT=9000
fi
-./bin/srt start webport=0.0.0.0:$SRT_PORT
+# Accept parameters (like 'noautoupdate')
+./bin/srt start webport=0.0.0.0:$SRT_PORT $1
diff --git a/bin/nist/srtool_nist.py b/bin/nist/srtool_nist.py
index 021836b..9efd3d1 100755
--- a/bin/nist/srtool_nist.py
+++ b/bin/nist/srtool_nist.py
@@ -64,6 +64,8 @@ update_skip_history = False
cmd_skip = 0
cmd_count = 0
+nist_datasources = {}
+
nist_cve_url_base = 'https://nvd.nist.gov/feeds/json/cve/1.1'
nist_meta_url_base = 'https://nvd.nist.gov/feeds/json/cve/1.1'
nist_cache_dir = 'data/cache/nist'
@@ -105,6 +107,7 @@ def _log(msg):
f1.write("|" + msg + "|\n" )
f1.close()
+# Compute a sortable CVE name
def get_name_sort(cve_name):
try:
a = cve_name.split('-')
@@ -143,25 +146,26 @@ def nist_scan_configuration_or(cpe_or_node, name, and_enum):
cpe_list = '[or]|'
found = 0
if 'cpe' in cpe_or_node:
- if verbose: print("NOTE:NIST_SCAN_CONFIGURATION_OR:cpe")
+ #if verbose: print("NOTE:NIST_SCAN_CONFIGURATION_OR:cpe")
cpe_list += do_nist_scan_configuration_or(cpe_or_node, name, and_enum,'cpe')
found += 1
if 'cpe_match' in cpe_or_node:
- if verbose: print("NOTE:NIST_SCAN_CONFIGURATION_OR:cpe_match")
+ #if verbose: print("NOTE:NIST_SCAN_CONFIGURATION_OR:cpe_match")
cpe_list += do_nist_scan_configuration_or(cpe_or_node, name, and_enum,'cpe_match')
found += 1
cpe_list += '[/or]|'
if verbose and (not found):
- print("WARNING:NIST_SCAN_CONFIGURATION_OR:NO CPE|CPE_MATCH:%s" % cpe_or_node)
- srt_error_log("WARNING:NIST_SCAN_CONFIGURATION_OR:NO CPE|CPE_MATCH:%s" % cpe_or_node)
+ print("WARNING:NIST_SCAN_CONFIGURATION_OR:NO CPE|CPE_MATCH:%s (%s)" % (cpe_or_node,name))
+ srt_error_log("WARNING:NIST_SCAN_CONFIGURATION_OR:NO CPE|CPE_MATCH:%s (%s)" % (cpe_or_node,name))
return cpe_list
def fixscore(score):
if not score:
return ''
- return '%02.2f' % float(score)
+ return '%02.1f' % float(score)
+# Parse NIST JSON record to a summary dict
def CVE_ItemToSummary(CVE_Item,header_only=False):
summary = {}
@@ -536,12 +540,55 @@ def sql_cve_query(action, conn, summary, log):
###
else:
+ # CVE found but is already up to date
+ cve_id = cve_current[ORM.CVE_ID]
is_change = False
if log: log.write("\tSKIPPED '%s'\n" % summary['name'])
cur.close()
return (cve_id, is_change)
#######################################################################
+# prescan_modified()
+# Gather all the CVEs in the "Modified" NIST data source
+#
+
+def prescan_modified(cve_filter):
+
+ modify_datasource = None
+ cve_skip_list = []
+
+ for id in nist_datasources:
+ if nist_datasources[id][ORM.DATASOURCE_DESCRIPTION] == 'NIST Modified Data':
+ modify_datasource = nist_datasources[id]
+ break
+ if not modify_datasource:
+ print("ERROR: 'NIST Modified Data' not found")
+ return cve_skip_list
+
+ nist_file = os.path.join(srtool_basepath,get_file_from_lookup(modify_datasource[ORM.DATASOURCE_LOOKUP]))
+ try:
+ if not os.path.isfile(nist_file):
+ print("ERROR: no such file '%s'" % nist_file)
+ exit(1)
+ f = open(nist_file, 'r')
+ source_dct = json.load(f)
+ for item in source_dct["CVE_Items"]:
+ if not 'cve' in item:
+ continue
+ if not 'CVE_data_meta' in item['cve']:
+ continue
+ if not 'ID' in item['cve']['CVE_data_meta']:
+ continue
+ cve_name = item['cve']['CVE_data_meta']['ID']
+ if cve_name.startswith(cve_filter):
+ cve_skip_list.append(cve_name)
+ if verbose: print("MODSKIP:%s:1ADDMOD" % cve_name)
+ except Exception as e:
+ print("ERROR:%s" % e)
+
+ return(cve_skip_list)
+
+#######################################################################
# nist_json: parses JSON, creates CVE object, and updates database as necessary. Commits to database on success
#
# Will EITHER create new record in orm_cve if cve does not exist OR overwrite
@@ -556,15 +603,25 @@ def nist_json(action, summary_json_url, datasource, datasource_file, log, date_n
conn = sqlite3.connect(srtDbName)
cur = conn.cursor()
+ # Special handling around the NIST Modified Source
+ is_modified_source = ("PREVIEW-SOURCE" in datasource[ORM.DATASOURCE_ATTRIBUTES])
+
# If this is a volatile preview source:
# (a) Fetch the existing CveSource matches into a list
# (b) Remove found matches from that list
# (c) Delete remaining obsolete CveSource entries
preview_dict = {}
- if "PREVIEW-SOURCE" in datasource[ORM.DATASOURCE_ATTRIBUTES]:
+ cve_skip_list = []
+ if is_modified_source:
sql = '''SELECT * FROM orm_cvesource WHERE datasource_id=? '''
for d2c in cur.execute(sql, (datasource[ORM.DATASOURCE_ID],)):
preview_dict[d2c[ORM.CVESOURCE_CVE_ID]] = d2c[ORM.CVESOURCE_ID]
+ if verbose: print("MODCHK:%8d:1ADDPREV" % d2c[ORM.CVESOURCE_CVE_ID])
+ else:
+ # If normal source but "force_update" flag is set, pre-fetch the CVes
+ # that are in the "Modified" source so that they can be skipped.
+ if force_update:
+ cve_skip_list = prescan_modified(datasource[ORM.DATASOURCE_CVE_FILTER])
# If we have already cached a current version of the NIST file, read from it directly
@@ -603,8 +660,20 @@ def nist_json(action, summary_json_url, datasource, datasource_file, log, date_n
# Translate a CVE_Item JSON node
summary = CVE_ItemToSummary(CVE_Item)
+ # Skip this CVE (Modified preemption)?
+ if not is_modified_source:
+ if summary['name'] in cve_skip_list:
+ if verbose: print("MODSKIP:%s:2SKIPMOD" % summary['name'])
+ continue
+ else:
+ if verbose: print("MODSKIP:%s:3PROCESS" % summary['name'])
+ pass
+
# Indicate progress
print('[%4d]%30s\r' % ((i * 100)/ total, summary['name']), end='', flush=True)
+ if verbose:
+ # Remove this progress from the verbose lines (allows sorting by cve_id)
+ print('')
#if cve exists in cache, delete it
cve_path = os.path.join(cache_path, '%s.json' % summary['name'])
@@ -616,7 +685,9 @@ def nist_json(action, summary_json_url, datasource, datasource_file, log, date_n
cve_id, is_change = sql_cve_query(action, conn, summary, log)
# Remove this found CVE from the preview check list, if present
- preview_dict.pop(cve_id,None)
+ if is_modified_source:
+ preview_dict.pop(cve_id,None)
+ if verbose: print("MODCHK:%8d:2POP" % cve_id)
# If CVE updates, must check and update associated records (CWEs, references, and CVE2CWE)
#sql_cwe_query, and sql_cve2cwe_query require valid CVE record primary key at some point during their execution, therefore must always be after call to sql_cve_query
@@ -633,8 +704,23 @@ def nist_json(action, summary_json_url, datasource, datasource_file, log, date_n
sql = '''SELECT * FROM orm_cvesource WHERE cve_id=? AND datasource_id=? '''
exists = cur.execute(sql, (cve_id,datasource[ORM.DATASOURCE_ID])).fetchone()
if exists is None:
+ # If volatile source, first remove all existing (potentially obsolete) NIST datasources to CVE
+ if is_modified_source:
+ if verbose: print("MODCHK:%8d:3aREM_OLD_CVESOURCE %s" % (cve_id,summary['name']))
+ sql = '''SELECT * FROM orm_cvesource WHERE cve_id=?'''
+ for cve2ds in cur.execute(sql, (cve_id, )):
+ if cve2ds[ORM.CVESOURCE_DATASOURCE_ID] in nist_datasources:
+ sql = 'DELETE FROM orm_cvesource WHERE id=?'
+ cur.execute(sql, (cve2ds[ORM.CVESOURCE_ID],))
+ if verbose: print("MODCHK:%8d:3bREM_FROM_CVESOURCE DS:%d" % (cve_id,cve2ds[ORM.CVESOURCE_DATASOURCE_ID]))
+
+ # Now, add found NIST datasource to CVE
sql = ''' INSERT into orm_cvesource (cve_id, datasource_id) VALUES (?, ?)'''
cur.execute(sql, (cve_id,datasource[ORM.DATASOURCE_ID]))
+ if verbose: print("MODCHK:%8d:4ADD_TO_CVESOURCE" % cve_id)
+ else:
+ if verbose: print("MODCHK:%8d:4NO_CHANGE_CVESOURCE" % cve_id)
+ pass
# Safety commit as we go
if 199 == (i % 200):
@@ -651,9 +737,27 @@ def nist_json(action, summary_json_url, datasource, datasource_file, log, date_n
log.write("total number of CVEs checked: %s\n" % total)
# Now delete any un-matched obsolete CveSource entries
- for old_cve_id in preview_dict.keys():
- sql = 'DELETE FROM orm_cvesource WHERE id=?'
- cur.execute(sql, (preview_dict[old_cve_id],))
+ if is_modified_source:
+ if verbose: print("MODCHK:%8d:5REMOVE DEAD LINKS" % 0)
+ for cve_id in preview_dict.keys():
+ # First, remove volatile and obsolete CveSource reference
+ sql = 'DELETE FROM orm_cvesource WHERE id=?'
+ cur.execute(sql, (preview_dict[cve_id],))
+ if verbose: print("MODCHK:%8d:6REMOVE DEAD LINK" % cve_id)
+ # Second, reattach to normal CveSource reference
+ cve = cur.execute('SELECT * FROM orm_cve WHERE id = "%s"' % cve_id).fetchone()
+ if cve:
+ for ds_id in nist_datasources:
+ datasource_cve_filter = nist_datasources[ds_id][ORM.DATASOURCE_CVE_FILTER]
+ if datasource_cve_filter and cve[ORM.CVE_NAME].startswith(datasource_cve_filter):
+ sql = ''' INSERT into orm_cvesource (cve_id, datasource_id) VALUES (?, ?)'''
+ cur.execute(sql, (cve_id,ds_id))
+ if verbose: print("MODCHK:%8d:7MOVE TO NORMAL %d" % (cve_id,ds_id))
+ break
+ else:
+ msg = "ERROR: missing CVE record '%d' when reattaching obsolete CveSource reference" % cve_id
+ print(msg)
+ log.write(msg)
conn.commit()
cur.close()
@@ -669,6 +773,7 @@ def nist_json(action, summary_json_url, datasource, datasource_file, log, date_n
# tracks history in update_log.txt
def update_nist(action,datasource_description, url_file, url_meta, cve_file):
+ global nist_datasources
nist_cve_url = '%s/%s' % (nist_cve_url_base,url_file)
nist_meta_url = '%s/%s' % (nist_meta_url_base,url_meta)
@@ -692,6 +797,13 @@ def update_nist(action,datasource_description, url_file, url_meta, cve_file):
conn = sqlite3.connect(srtDbName)
c = conn.cursor()
+ # Prefetch the NIST data sources to assist MODIFIED <-> NORMAL transitions
+ sql = "SELECT * FROM orm_datasource WHERE source = 'nist'"
+ c.execute(sql)
+ nist_datasources = {}
+ for ds in c:
+ nist_datasources[ds[ORM.DATASOURCE_ID]] = ds
+
sql = "SELECT * FROM orm_datasource WHERE description='%s'" % datasource_description
c.execute(sql)
for ds in c:
@@ -715,6 +827,7 @@ def update_nist(action,datasource_description, url_file, url_meta, cve_file):
if (date_new > date_past) or force_update:
pre_update_time = datetime.now() #used for logging purposes only
+ if verbose: print("NIST: EXECUTING ACTION %s" % action)
nist_json(action,nist_cve_url, ds, nist_file, log, date_new)
log.write("began %s: %s\n" % ( action, str(pre_update_time) ))
log.write("finished %s: %s\n" % ( action, str(datetime.now()) ))
@@ -726,11 +839,11 @@ def update_nist(action,datasource_description, url_file, url_meta, cve_file):
c.execute(sql, (str(date_new),))
conn.commit()
else:
+ if verbose: print("NIST: NO %s NEEDED" % action)
log.write("No %s needed\n" % action)
log.write("Checked: %s\n" % datetime.now())
log.write("=============================================================================\n")
log.write("\n")
- print("NO %s NEEDED" % action)
# Reset datasource's lastModifiedDate as today
sql = "UPDATE orm_datasource SET lastModifiedDate = ? WHERE id='%s'" % ds[ORM.DATASOURCE_ID]
@@ -829,6 +942,7 @@ def fetch_cve(cve_name,cve_source_file):
print('%s=%s' % (key,summary[key]))
def cve_summary(cve_name):
+ cve_name = cve_name.upper()
DSMAP_FILE = 0
DSMAP_DESC = 1
@@ -838,8 +952,8 @@ def cve_summary(cve_name):
conn = sqlite3.connect(srtDbName)
cur_ds = conn.cursor()
cur_cve = conn.cursor()
- base_id = []
- modified_id = []
+ base_id = -1
+ modified_id = -1
def description_summary(description):
desc_sum = 0
@@ -849,18 +963,34 @@ def cve_summary(cve_name):
description = "%-37s..." % description[:37]
return("%-40s [sum=%d]" % (description,desc_sum))
- def show_summary(key,cve_name,datasource_map):
- summary = do_fetch_cve(cve_name,datasource_map[DSMAP_FILE],False)
- if summary:
- print(" %s: %s in %s (%s,%s)" % (key,summary['name'],datasource_map[DSMAP_FILE],datasource_map[DSMAP_MOD],datasource_map[DSMAP_UPDATE]))
- print(" description :%s" % description_summary(summary['description']))
- print(" cvssV3_baseScore :%s" % summary['cvssV3_baseScore'])
- print(" cvssV3_baseSeverity:%s" % summary['cvssV3_baseSeverity'])
- print(" cvssV2_baseScore :%s" % summary['cvssV2_baseScore'])
- print(" cvssV2_severity :%s" % summary['cvssV2_severity'])
- print(" lastModifiedDate :%s" % summary['lastModifiedDate'])
+ def show_summary(key,cve_name,datasource_map,datasource_id):
+ if datasource_id in datasource_map:
+ data_map = datasource_map[datasource_id]
+ summary = do_fetch_cve(cve_name,data_map[DSMAP_FILE],False)
+ if summary:
+ print(" %s: %s in %s (%s,%s)" % (key,summary['name'],data_map[DSMAP_FILE],data_map[DSMAP_MOD],data_map[DSMAP_UPDATE]))
+ print(" description :%s" % description_summary(summary['description']))
+ print(" cvssV3_baseScore :%s" % summary['cvssV3_baseScore'])
+ print(" cvssV3_baseSeverity:%s" % summary['cvssV3_baseSeverity'])
+ print(" cvssV2_baseScore :%s" % summary['cvssV2_baseScore'])
+ print(" cvssV2_severity :%s" % summary['cvssV2_severity'])
+ print(" lastModifiedDate :%s" % summary['lastModifiedDate'])
+ else:
+ print(" %s: There is no CVE record for %s in %s" % (key,cve_name,data_map[DSMAP_FILE]))
else:
- print(" %s: There is no CVE record for %s in %s" % (key,cve_name,datasource_map[DSMAP_FILE]))
+ print(" %s: There is no matching datasource" % cve_name)
+
+ # Support CVE record IDs in addition to CVE names
+ cve = None
+ if cve_name[0].isdigit():
+ cve = cur_cve.execute('SELECT * FROM orm_cve WHERE id = %s' % cve_name).fetchone()
+ if not cve:
+ print("CVE Summary:")
+ print(" CVE : There is no CVE record for this ID %s in orm_cve" % (cve_name))
+ return
+ cve_name = cve[ORM.CVE_NAME]
+ else:
+ cve = cur_cve.execute('SELECT * FROM orm_cve WHERE name = "%s"' % cve_name).fetchone()
cur_ds.execute('SELECT * FROM orm_datasource;')
datasource_map = {}
@@ -878,19 +1008,20 @@ def cve_summary(cve_name):
# Return the NIST results
print("NIST Summary:")
- show_summary("BASE",cve_name,datasource_map[base_id])
- show_summary("MOD ",cve_name,datasource_map[modified_id])
- cve = cur_cve.execute('SELECT * FROM orm_cve WHERE name = "%s"' % cve_name).fetchone()
+ show_summary("BASE",cve_name,datasource_map,base_id)
+ show_summary("MOD ",cve_name,datasource_map,modified_id)
if cve:
cur_ds.execute('SELECT * FROM orm_cvesource WHERE cve_id = %d' % cve[ORM.CVE_ID])
# Return the CVE record's current values
print("CVE Summary:")
- print(" CVE : %s" % (cve[ORM.CVE_NAME]))
+ print(" CVE [%s]: %s " % (cve[ORM.CVE_ID],cve[ORM.CVE_NAME],))
print(" description :%s" % description_summary(cve[ORM.CVE_DESCRIPTION]))
print(" cvssV3_baseScore :%s" % cve[ORM.CVE_CVSSV3_BASESCORE])
print(" cvssV3_baseSeverity:%s" % cve[ORM.CVE_CVSSV3_BASESEVERITY])
print(" cvssV2_baseScore :%s" % cve[ORM.CVE_CVSSV2_BASESCORE])
print(" cvssV2_severity :%s" % cve[ORM.CVE_CVSSV2_SEVERITY])
+ print(" public_notes :%s" % cve[ORM.CVE_COMMENTS])
+ print(" status :%s" % ORM.get_orm_string(cve[ORM.CVE_STATUS],ORM.STATUS_STR))
print(" lastModifiedDate :%s" % cve[ORM.CVE_LASTMODIFIEDDATE])
# Return the DataSource mapping results
print("DataSource Summary:")
@@ -1083,7 +1214,7 @@ def main(argv):
parser.add_argument('--update-existing-cves', '-L', dest='update_existing_cves', help='Update list of existing CVEs to database')
parser.add_argument('--cve-detail', '-d', dest='cve_detail', help='Lookup CVE data')
- parser.add_argument('--cve-summary', '-S', dest='cve_summary', help='Quick summary of CVE data')
+ parser.add_argument('--cve-summary', '-S', dest='cve_summary', help='Quick summary of CVE data [[cvename|cve_id]*|ask]')
parser.add_argument('--source', dest='source', help='Local CVE source file')
parser.add_argument('--url-file', dest='url_file', help='CVE URL extension')
@@ -1119,7 +1250,16 @@ def main(argv):
update_existing_cves(ACTION_UPDATE_CVE,args.update_existing_cves)
return
elif args.cve_summary:
- cve_summary(args.cve_summary)
+ if 'ask' == args.cve_summary.lower():
+ print("Next CVE [name|id]: ",end='')
+ cve = input()
+ while cve:
+ cve_summary(cve)
+ print("Next CVE [name|id]: ",end='')
+ cve = input()
+ else:
+ for cve in args.cve_summary.split(','):
+ cve_summary(args.cve_summary)
return
# Required parameters to continue
diff --git a/lib/srtgui/templates/cve-nist-local.html b/lib/srtgui/templates/cve-nist-local.html
index 31236a4..9c4c454 100755
--- a/lib/srtgui/templates/cve-nist-local.html
+++ b/lib/srtgui/templates/cve-nist-local.html
@@ -61,10 +61,10 @@
<dd class="localblue">{{details.cvssV3_vectorString}}</dd>
<dt>Impact Score:</dt>
- <dd class="localblue">{{details.cvssV3_impactScore}}</dd>
+ <dd class="localblue">{{details.cvssV3_impactScore|floatformat:2}}</dd>
<dt>Exploitability Score:</dt>
- <dd class="localblue">{{details.cvssV3_exploitabilityScore}}</dd>
+ <dd class="localblue">{{details.cvssV3_exploitabilityScore|floatformat:2}}</dd>
</dl>
<h3>CVSS Version 3 Metrics:</h3>
<dl class="dl-horizontal">
@@ -103,10 +103,10 @@
<dd class="localblue">{{details.cvssV2_vectorString}}</dd>
<dt>Impact Subscore:</dt>
- <dd class="localblue">{{details.cvssV2_impactScore}}</dd>
+ <dd class="localblue">{{details.cvssV2_impactScore|floatformat:2}}</dd>
<dt>Exploitability Subscore:</dt>
- <dd class="localblue">{{details.cvssV2_exploitabilityScore}}</dd>
+ <dd class="localblue">{{details.cvssV2_exploitabilityScore|floatformat:2}}</dd>
</dl>
<h3>CVSS Version 2 Metrics:</h3>
diff --git a/lib/srtgui/templates/cve-nist.html b/lib/srtgui/templates/cve-nist.html
index ead11ca..9792865 100755
--- a/lib/srtgui/templates/cve-nist.html
+++ b/lib/srtgui/templates/cve-nist.html
@@ -64,10 +64,10 @@
<dd {{cve_html|get_dict_value:'cvssV3_vectorString'}}>{{details.cvssV3_vectorString}}</dd>
<dt>Impact Score:</dt>
- <dd {{cve_html|get_dict_value:'cvssV3_impactScore'}}>{{details.cvssV3_impactScore}}</dd>
+ <dd {{cve_html|get_dict_value:'cvssV3_impactScore'}}>{{details.cvssV3_impactScore|floatformat:2}}</dd>
<dt>Exploitability Score:</dt>
- <dd {{cve_html|get_dict_value:'cvssV3_exploitabilityScore'}}>{{details.cvssV3_exploitabilityScore}}</dd>
+ <dd {{cve_html|get_dict_value:'cvssV3_exploitabilityScore'}}>{{details.cvssV3_exploitabilityScore|floatformat:2}}</dd>
</dl>
<h3>CVSS Version 3 Metrics:</h3>
<dl class="dl-horizontal">
@@ -107,10 +107,10 @@
<dd {{cve_html|get_dict_value:'cvssV2_vectorString'}}>{{details.cvssV2_vectorString}}</dd>
<dt>Impact Subscore:</dt>
- <dd {{cve_html|get_dict_value:'cvssV2_impactScore'}}>{{details.cvssV2_impactScore}}</dd>
+ <dd {{cve_html|get_dict_value:'cvssV2_impactScore'}}>{{details.cvssV2_impactScore|floatformat:2}}</dd>
<dt>Exploitability Subscore:</dt>
- <dd {{cve_html|get_dict_value:'cvssV2_exploitabilityScore'}}>{{details.cvssV2_exploitabilityScore}}</dd>
+ <dd {{cve_html|get_dict_value:'cvssV2_exploitabilityScore'}}>{{details.cvssV2_exploitabilityScore|floatformat:2}}</dd>
</dl>
<h3>CVSS Version 2 Metrics:</h3>