aboutsummaryrefslogtreecommitdiffstats
path: root/bin/mitre/srtool_mitre.py
diff options
context:
space:
mode:
Diffstat (limited to 'bin/mitre/srtool_mitre.py')
-rwxr-xr-xbin/mitre/srtool_mitre.py146
1 files changed, 103 insertions, 43 deletions
diff --git a/bin/mitre/srtool_mitre.py b/bin/mitre/srtool_mitre.py
index 3c6af89d..cdf6ff6e 100755
--- a/bin/mitre/srtool_mitre.py
+++ b/bin/mitre/srtool_mitre.py
@@ -31,8 +31,7 @@ import sys
import xml.etree.ElementTree as ET
import argparse
import shutil
-import sqlite3
-from datetime import datetime, timedelta
+from datetime import datetime, timedelta, date
import pytz
from urllib.request import urlopen
@@ -41,10 +40,13 @@ from urllib.request import urlopen
dir_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.insert(0, dir_path)
from common.srt_schema import ORM
+from common.srtool_progress import *
+from common.srtool_sql import *
# Setup:
srtDbName = 'srt.sqlite'
srtErrorLog = 'srt_errors.txt'
+COMMIT_DELAY = 64
mitre_cvrf_url = 'https://cve.mitre.org/data/downloads'
mitre_cvrf_xml = 'data/allitems-cvrf-year-2018.xml'
@@ -56,6 +58,8 @@ mitre_cache_dir = 'data/cache/mitre'
# Debugging support
verbose = False
+cmd_skip = 0
+cmd_count = 0
# Development support
overrides = {}
@@ -88,18 +92,17 @@ def srt_error_log(msg):
f1.close()
-# Newly discovered or updated CVEs default to NEW for triage
-# Inited CVEs default to HISTORICAL, unless they are within the courtesy CVE_INIT_NEW_DELTA
+# Newly discovered CVEs default to NEW_RESERVED if reserved, else NEW for triage
init_new_date = None
def get_cve_default_status(is_init,publishedDate,description):
global init_new_date
if None == init_new_date:
# Precalculate and cache the relative 'new' date for efficiency
- conn = sqlite3.connect(srtDbName)
+ conn = SQL_CONNECT()
cur = conn.cursor()
sql = '''SELECT * FROM orm_srtsetting WHERE name=?'''
- CVE_INIT_NEW_DELTA = cur.execute(sql, ('CVE_INIT_NEW_DELTA',)).fetchone()
+ CVE_INIT_NEW_DELTA = SQL_EXECUTE(cur, sql, ('CVE_INIT_NEW_DELTA',)).fetchone()
if CVE_INIT_NEW_DELTA is None:
cve_init_new_delta = 30
else:
@@ -110,18 +113,10 @@ def get_cve_default_status(is_init,publishedDate,description):
#print("\nPreset new data = %s" % init_new_date.strftime("%Y-%m-%d"))
init_new_date = init_new_date.strftime("%Y-%m-%d")
- if is_init:
- # Note: the NIST 'published date' is in the format "2017-05-11", so do a simple string compare
- #print("INIT status: %s versus %s" % (init_new_date,publishedDate))
- if not publishedDate or (publishedDate > init_new_date):
- # Is this reserved by Mitre? Is '** RESERVED **' within the first 20 char positions?
- reserved_pos = description.find('** RESERVED **')
- if (0 <= reserved_pos) and (20 > reserved_pos):
- return ORM.STATUS_NEW_RESERVED
- else:
- return ORM.STATUS_NEW
- else:
- return ORM.STATUS_HISTORICAL
+ # Is this reserved by Mitre? Is '** RESERVED **' within the first 20 char positions?
+ reserved_pos = description.find('** RESERVED **')
+ if (0 <= reserved_pos) and (20 > reserved_pos):
+ return ORM.STATUS_NEW_RESERVED
else:
return ORM.STATUS_NEW
@@ -173,6 +168,11 @@ def fetch_cve(cve_name,cvrf_xml_file):
datasource_xml = os.path.join(srtool_basepath,cvrf_xml_file)
cache_file = os.path.join(srtool_basepath,mitre_cache_dir,"%s.txt" % cve_name)
+ # Insure that the original data file exists
+ if not os.path.isfile(datasource_xml):
+ print("description=There is no loaded Mitre data.")
+ return
+
# Insure the cache dir exists
cache_dir = os.path.join(srtool_basepath,mitre_cache_dir)
if not os.path.isdir(cache_dir):
@@ -268,36 +268,52 @@ def append_cve_database(is_init,file_xml):
tree = ET.parse(file_xml)
root = tree.getroot()
- # Max count for development cycle
- cmd_count = 20 if get_override('SRTDBG_MINIMAL_DB') else 0
-
- conn = sqlite3.connect(srtDbName)
+ conn = SQL_CONNECT()
cur = conn.cursor()
cur_write = conn.cursor()
cur_ds = conn.cursor()
datasource_id = 0
+ srtool_today = date.today()
+ version_date = ''
+
+ # Progress expected max
+ if cmd_count:
+ progress_set_max(cmd_count)
+ else:
+ progress_set_max(len(root))
i = 0
for child in root:
i += 1
+
+ # Extract document date record
+ # <DocumentTracking>
+ # <Version>2020.01.14.22</Version>
+ if 'DocumentTracking' in child.tag:
+ for child_d in child:
+ if 'Version' in child_d.tag:
+ version_date = datetime.strptime(child_d.text, '%Y.%m.%d.%H')
+
+ # Find the Vulnerability records
if not 'Vulnerability' in child.tag:
continue
+
summary = _extract_text(child)
cve_name = summary['CVE']
+ progress_show(cve_name,force_newline=True)
# Progress indicator support
- if 0 == i % 10:
- print('%04d: %20s \r' % (i,cve_name), end='')
- if (0 == i % 200):
- conn.commit()
+ if 0 == (i % COMMIT_DELAY):
+ SQL_COMMIT(conn)
print('')
+ sys.stdout.flush()
if cmd_count and (i > cmd_count):
break
# Find the datasource matching these CVE prefixes
if 0 == datasource_id:
sql = "SELECT * FROM orm_datasource WHERE data = ? AND source = ?"
- cur_ds.execute(sql, ('cve','mitre',))
+ SQL_EXECUTE(cur_ds, sql, ('cve','mitre',))
for ds in cur_ds:
if ds[ORM.DATASOURCE_CVE_FILTER] and cve_name.startswith(ds[ORM.DATASOURCE_CVE_FILTER]):
datasource_id = ds[ORM.DATASOURCE_ID]
@@ -309,28 +325,45 @@ def append_cve_database(is_init,file_xml):
# Define the CVE (if not already there - e.g. not defined by NIST)
sql = ''' SELECT * FROM orm_cve WHERE name = ?'''
- cve = cur_write.execute(sql, (cve_name,)).fetchone()
+ cve = SQL_EXECUTE(cur_write, sql, (cve_name,)).fetchone()
if cve:
cve_id = cve[ORM.CVE_ID]
- print("MITRE:FOUND %20s\r" % cve_name, end='')
+ if not progress_status()[PROGRESS_STATUS_ENABLE]:
+ print("MITRE:FOUND %20s" % cve_name, end='\r')
else:
# Get the default CVE status
status = get_cve_default_status(is_init,summary['Published'],summary['Description'])
- sql = ''' INSERT into orm_cve (name, name_sort, priority, status, comments, comments_private, cve_data_type, cve_data_format, cve_data_version, public, publish_state, publish_date, description, publishedDate, lastModifiedDate, recommend, recommend_list, cvssV3_baseScore, cvssV3_baseSeverity, cvssV2_baseScore, cvssV2_severity, srt_updated, packages)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'''
- cur.execute(sql, (cve_name, get_name_sort(cve_name), ORM.PRIORITY_UNDEFINED, status, '', '', 'CVE', 'MITRE', '', 1, ORM.PUBLISH_UNPUBLISHED, '', summary['Description'], summary['Published'], summary['Modified'],0, '', '', '', '', '', datetime.now(),''))
- cve_id = cur.lastrowid
- print("MITRE:ADDED %20s\r" % cve_name)
+ # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
+ sql = ''' INSERT INTO orm_cve (name, name_sort, priority, status, comments, comments_private, tags, cve_data_type, cve_data_format, cve_data_version, public, publish_state, publish_date, acknowledge_date, description, "publishedDate", "lastModifiedDate", recommend, recommend_list, "cvssV3_baseScore", "cvssV3_baseSeverity", "cvssV2_baseScore", "cvssV2_severity", srt_updated, srt_created, packages)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'''
+ SQL_EXECUTE(cur, sql, (cve_name, get_name_sort(cve_name), ORM.PRIORITY_UNDEFINED, status, '', '', '', 'CVE', 'MITRE', '', True, ORM.PUBLISH_UNPUBLISHED, '', None, summary['Description'], summary['Published'], summary['Modified'],0, '', '', '', '', '', datetime.now(), datetime.now(),''))
+ # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
+ cve_id = SQL_GET_LAST_ROW_INSERTED_ID(cur)
+ if not progress_status()[PROGRESS_STATUS_ENABLE]:
+ print("MITRE:ADDED %20s" % cve_name, end='\r')
+
+ # Also create CVE history entry
+ update_comment = "%s {%s}" % (ORM.UPDATE_CREATE_STR % ORM.UPDATE_SOURCE_CVE,'Created from MITRE')
+ sql = '''INSERT INTO orm_cvehistory (cve_id, comment, date, author) VALUES (?,?,?,?)'''
+ SQL_EXECUTE(cur, sql, (cve_id,update_comment,srtool_today,ORM.USER_SRTOOL_NAME,) )
# Add this data source to the CVE
sql = '''SELECT * FROM orm_cvesource WHERE cve_id=? AND datasource_id=? '''
- if not cur_ds.execute(sql, (cve_id,datasource_id)).fetchone():
- sql = ''' INSERT into orm_cvesource (cve_id, datasource_id) VALUES (?, ?)'''
- cur_ds.execute(sql, (cve_id,datasource_id))
+ if not SQL_EXECUTE(cur_ds, sql, (cve_id,datasource_id)).fetchone():
+ sql = ''' INSERT INTO orm_cvesource (cve_id, datasource_id) VALUES (?, ?)'''
+ SQL_EXECUTE(cur_ds, sql, (cve_id,datasource_id))
+
+ #update datasource's lastModifiedDate after successsfuly updating it
+ if datasource_id:
+ print("\nVersion Date=%s" % str(version_date))
+ sql = """UPDATE orm_datasource SET "lastModifiedDate" = ? WHERE id='%s'""" % datasource_id
+ SQL_EXECUTE(cur, sql, (str(version_date),))
- conn.commit()
+ SQL_COMMIT(conn)
print("\nTotal = %5d\n" % i)
+ # End progress
+ progress_done('Done')
#################################
# test dump
@@ -391,8 +424,9 @@ def dump(file_xml):
print("OTHER TOP TAG=%s" % child.tag)
i += 1
- if (0 == (i % 20)):
- print("%5d\r" % i,end = '')
+ if not progress_status()[PROGRESS_STATUS_ENABLE]:
+ if (0 == (i % 20)):
+ print("%5d" % i,end = '\r')
print("\nTotal = %5d\n" % i)
#################################
@@ -401,25 +435,46 @@ def dump(file_xml):
def main(argv):
global verbose
+ global cmd_skip
+ global cmd_count
# setup
parser = argparse.ArgumentParser(description='srtool_mitre.py: manage Mitre CVE data')
- parser.add_argument('--initialize', '-I', action='store_const', const='init_mitre', dest='command', help='Download the Mitre source CVE file')
+ parser.add_argument('--initialize', '-I', action='store_const', const='init_mitre', dest='command', help='Download the Mitre source CVE file, add CVEs')
parser.add_argument('--update', '-u', action='store_const', const='update_mitre', dest='command', help='Update the Mitre source CVE file')
parser.add_argument('--source', dest='source', help='Local CVE source file')
parser.add_argument('--url-file', dest='url_file', help='CVE URL extension')
+ parser.add_argument('--download-only', action='store_const', const='download_mitre', dest='command', help='Download the Mitre source CVE file only')
parser.add_argument('--cve-detail', '-d', dest='cve_detail', help='Fetch CVE detail')
parser.add_argument('--file', dest='cve_file', help='Local CVE source file')
+
+ parser.add_argument('--progress', '-P', action='store_true', dest='do_progress', help='Progress output')
parser.add_argument('--force', '-f', action='store_true', dest='force_update', help='Force update')
+ parser.add_argument('--update-skip-history', '-H', action='store_true', dest='update_skip_history', help='Skip history updates')
parser.add_argument('--verbose', '-v', action='store_true', dest='is_verbose', help='Enable verbose debugging output')
+ parser.add_argument('--skip', dest='skip', help='Debugging: skip record count')
+ parser.add_argument('--count', dest='count', help='Debugging: short run record count')
+ parser.add_argument('--debug-sql', action='store_true', dest='debug_sql', help='Debug SQL writes')
parser.add_argument('--dump', '-D', action='store_const', const='dump', dest='command', help='test dump data')
parser.add_argument('--dump2', '-2', action='store_const', const='dump2', dest='command', help='test dump data')
args = parser.parse_args()
+ # fetch any environment overrides
+ set_override('SRTDBG_MINIMAL_DB')
+
if args.is_verbose:
verbose = True
+ if None != args.skip:
+ cmd_skip = int(args.skip)
+ if None != args.count:
+ cmd_count = int(args.count)
+ elif get_override('SRTDBG_MINIMAL_DB'):
+ cmd_count = 20
+ if args.debug_sql:
+ SQL_DEBUG(True,'MTR')
+ progress_set_on(args.do_progress)
if 'dump' == args.command:
dump(mitre_cvrf_xml)
@@ -438,8 +493,6 @@ def main(argv):
fetch_cve(args.cve_detail,args.cve_file)
return
- # fetch any environment overrides
- set_override('SRTDBG_MINIMAL_DB')
# Required parameters to continue
if not args.source:
@@ -449,15 +502,22 @@ def main(argv):
print("ERROR: missing --url_file parameter")
exit(1)
+ # Currently no different between initialize and update actions
if 'init_mitre' == args.command:
init_mitre_file(args.source,args.url_file,args.cve_file,args.force_update)
append_cve_database(True,args.cve_file)
elif 'update_mitre' == args.command:
init_mitre_file(args.source,args.url_file,args.cve_file,args.force_update)
append_cve_database(False,args.cve_file)
+ elif 'download_mitre' == args.command:
+ init_mitre_file(args.source,args.url_file,args.cve_file,args.force_update)
else:
print("Command not found")
+ # Dump the SQL transaction data
+ if args.debug_sql:
+ SQL_DUMP()
+
if __name__ == '__main__':
srtool_basepath = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0]))))
main(sys.argv[1:])