aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xbin/common/srtool_common.py22
-rwxr-xr-xbin/common/srtool_utils.py250
-rwxr-xr-xbin/mitre/srtool_mitre.py41
-rw-r--r--bin/nist/datasource.json4
-rw-r--r--lib/srtgui/tables.py7
-rw-r--r--lib/srtgui/templates/publish.html361
-rw-r--r--lib/srtgui/templates/publish_diff_snapshot.html327
-rw-r--r--lib/srtgui/urls.py6
-rw-r--r--lib/srtgui/views.py184
-rwxr-xr-xlib/yp/reports.py381
-rwxr-xr-xlib/yp/urls.py4
-rwxr-xr-xlib/yp/views.py52
12 files changed, 1276 insertions, 363 deletions
diff --git a/bin/common/srtool_common.py b/bin/common/srtool_common.py
index d9fbd341..2a92333a 100755
--- a/bin/common/srtool_common.py
+++ b/bin/common/srtool_common.py
@@ -40,8 +40,10 @@ from datetime import datetime
# Load the srt.sqlite schema index file
# Since it is generated from this script
# it may not exist on the first pass
+dir_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
+sys.path.insert(0, dir_path)
try:
- from srt_schema import ORM
+ from common.srt_schema import ORM
except:
# Do a pass so that '--generate-schema-header' can fix it
print("Warning: srt_schema not yet created or bad format")
@@ -857,24 +859,29 @@ def update_cve_status_tree(cve_list,update_skip_history):
# CREATE TABLE "orm_notifycategories" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "category" varchar(50) NULL);
# ...
-def gen_schema_header():
+
+def gen_schema_header(database_dir,schema_dir):
+
+ database_file = os.path.join(database_dir, 'srt.sqlite')
+ schema_file = os.path.join(schema_dir, 'srt_schema.py')
+
create_re = re.compile(r"CREATE TABLE[A-Z ]* \"(\w+)\" \((.+)\);")
try:
- cmd = ('sqlite3', os.path.join(srtool_basepath, 'srt.sqlite'), '.schema')
+ cmd = ('sqlite3', database_file, '.schema')
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
print("ERROR(%d): %s" % (e.returncode, e.output))
return
# Fetch USER_SRTOOL_ID
- conn = sqlite3.connect(srtDbName)
+ conn = sqlite3.connect(database_file)
cur = conn.cursor()
USER_SRTOOL_NAME = 'SRTool'
user = cur.execute("SELECT * FROM users_srtuser where username = '%s'" % USER_SRTOOL_NAME).fetchone()
USER_SRTOOL_ID = user[0] # Hardcoded 'ORM.USERS_SRTUSER_ID'
conn.close()
- with open(os.path.join(srtool_basepath,'bin/common/srt_schema.py'), 'w') as fd:
+ with open(schema_file, 'w') as fd:
fd.write("# SRTool database table schema indexes\n")
fd.write("# Generated by: './bin/common/srtool_common.py --generate-schema-header'\n")
fd.write("# Should be run after any schema changes to sync commandline tools\n")
@@ -1089,6 +1096,7 @@ def main(argv):
parser.add_argument('--init-notify-categories', '-n', action='store_const', const='init_notify_categories', dest='command', help='Initialize notify categories')
parser.add_argument('--score-new-cves', '-s', dest='score_new_cves', help='Score CVEs for triage [NEW|CVE-1234]')
parser.add_argument('--generate-schema-header', '-g', action='store_const', const='gen_schema_header', dest='command', help='Generate database schema header')
+ parser.add_argument('--generate-schema-header-dir', dest='gen_schema_header_dir', help='Generate database schema header for a give database directory')
parser.add_argument('--update-cve-status-tree', '-S', dest='update_cve_status_tree', help="Update CVEs and their children's cumulative status")
@@ -1126,7 +1134,9 @@ def main(argv):
elif args.score_new_cves:
score_new_cves(args.score_new_cves)
elif 'gen_schema_header' == args.command:
- gen_schema_header()
+ gen_schema_header(srtool_basepath,os.path.join(srtool_basepath,'bin/common'))
+ elif args.gen_schema_header_dir:
+ gen_schema_header(args.gen_schema_header_dir,args.gen_schema_header_dir)
elif args.update_cve_status_tree:
update_cve_status_tree(args.update_cve_status_tree, update_skip_history)
diff --git a/bin/common/srtool_utils.py b/bin/common/srtool_utils.py
index ac65d42d..e3f574f6 100755
--- a/bin/common/srtool_utils.py
+++ b/bin/common/srtool_utils.py
@@ -28,6 +28,7 @@ import sqlite3
from datetime import datetime, date
import time
import re
+import subprocess
# load the srt.sqlite schema indexes
dir_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
@@ -57,6 +58,32 @@ def _log(msg):
f1.write("|" + msg + "|\n" )
f1.close()
+# Sub Process calls
+def execute_process(*args):
+ cmd_list = []
+ for arg in args:
+ if isinstance(arg, (list, tuple)):
+ # Flatten all the way down
+ for a in arg:
+ cmd_list.append(a)
+ else:
+ cmd_list.append(arg)
+
+ # Python < 3.5 compatible
+ if sys.version_info < (3,5):
+ process = subprocess.Popen(cmd_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ try:
+ stdout, stderr = process.communicate(input)
+ except:
+ process.kill()
+ process.wait()
+ raise
+ retcode = process.poll()
+ return retcode, stdout, stderr
+ else:
+ result = subprocess.run(cmd_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ return result.returncode,result.stdout,result.stderr
+
#################################
# reset sources
#
@@ -873,6 +900,180 @@ def fix_defects_to_products():
conn.commit()
#################################
+# fix_bad_mitre_init
+#
+
+#
+def fix_bad_mitre_init():
+ conn = sqlite3.connect(srtDbName)
+ cur = conn.cursor()
+ cur_ds = conn.cursor()
+ cur_cve = conn.cursor()
+ cur_del = conn.cursor()
+
+ new_count = 0
+ mitre_count = 0
+ cve_name = ''
+
+ nist_source_list = []
+ # Find NIST data sources
+ cur.execute('SELECT * FROM orm_datasource WHERE source = "nist"')
+ for i,ds in enumerate(cur):
+ nist_source_list.append(ds[ORM.DATASOURCE_ID])
+ print('NIST DataSource List=[%s]' % nist_source_list)
+
+ mitre_source_list = []
+ # Find MITRE data sources
+ cur.execute('SELECT * FROM orm_datasource WHERE source = "mitre"')
+ for i,ds in enumerate(cur):
+ mitre_source_list.append(ds[ORM.DATASOURCE_ID])
+ print('MITRE DataSource List=[%s]' % mitre_source_list)
+
+ # Find all bad MITRE reserved CVEs
+ cur.execute('SELECT * FROM orm_cve WHERE description = "" AND status = %d' % ORM.STATUS_NEW)
+# cur.execute('SELECT * FROM orm_cve WHERE description = ""')
+ for i,cve in enumerate(cur):
+ new_count += 1
+
+ cur_ds.execute('SELECT * FROM orm_cvesource WHERE cve_id = %d' % cve[ORM.CVE_ID])
+ is_mitre = False
+ is_nist = False
+ for cvesource in cur_ds:
+ if cvesource[ORM.CVESOURCE_DATASOURCE_ID] in mitre_source_list:
+ is_mitre = True
+ if cvesource[ORM.CVESOURCE_DATASOURCE_ID] in nist_source_list:
+ is_nist = True
+
+ if is_mitre and not is_nist:
+ mitre_count += 1
+ cve_name = cve[ORM.CVE_NAME]
+
+ if force:
+ sql = ''' UPDATE orm_cve
+ SET status = ?
+ WHERE id = ?'''
+ cur_cve.execute(sql, (ORM.STATUS_NEW_RESERVED,cve[ORM.CVE_ID],))
+ conn.commit()
+
+ # Progress indicator support
+ if 19 == i % 20:
+ print('%05d: %-20s\r' % (i,cve[ORM.CVE_NAME]), end='')
+ pass
+ if (0 == i % 200):
+# conn.commit()
+ #print('')
+ pass
+ # Development/debug support
+ if cmd_skip and (i < cmd_skip): continue
+ if cmd_count and ((i - cmd_skip) > cmd_count): break
+
+ print("3CVE NEW_COUNT=%d, mitre=%d, name=%s, database=%s" % (new_count,mitre_count,cve_name,srtDbName))
+# conn.commit()
+
+
+
+
+#
+def foo_fix_bad_mitre_init():
+ conn = sqlite3.connect(srtDbName)
+ cur = conn.cursor()
+ cur_ds = conn.cursor()
+ cur_cve = conn.cursor()
+ cur_del = conn.cursor()
+
+ fix_count = 0
+ reserved_count = 0
+ mitre_count = 0
+ nosource_count = 0
+
+ mitre_source_list = []
+ mitre_lookup = {}
+
+ # Find MITRE data sources
+ cur.execute('SELECT * FROM orm_datasource WHERE source = "mitre"')
+ for i,ds in enumerate(cur):
+ mitre_source_list.append(ds[ORM.DATASOURCE_ID])
+ mitre_lookup[ds[ORM.DATASOURCE_ID]] = ds[ORM.DATASOURCE_LOOKUP]
+ print('MITRE DataSource List=[%s]' % mitre_source_list)
+
+ # Find all bad MITRE reserved CVEs
+ cur.execute('SELECT * FROM orm_cve WHERE description = ""')
+ for i,cve in enumerate(cur):
+ fix_count += 1
+
+# reserved_pos = cve[ORM.CVE_DESCRIPTION].find('** RESERVED **')
+# if (0 <= reserved_pos) and (20 > reserved_pos):
+# reserved_count += 1
+
+ if ORM.STATUS_NEW == cve[ORM.CVE_STATUS]:
+ reserved_count += 1
+
+ cur_ds.execute('SELECT * FROM orm_cvesource WHERE cve_id = %d' % cve[ORM.CVE_ID])
+ is_mitre = False
+ mitre_ds = 0
+ ds_list = []
+ ds_count = 0
+ for cvesource in cur_ds:
+ ds_count += 1
+ if cvesource[ORM.CVESOURCE_DATASOURCE_ID] in mitre_source_list:
+ is_mitre = True
+ mitre_ds = cvesource[ORM.CVESOURCE_DATASOURCE_ID]
+ break
+ ds_list.append(cvesource[ORM.CVESOURCE_DATASOURCE_ID])
+
+ if True:
+ print('%05d: %-20s, SourceList=%s' % (i,cve[ORM.CVE_NAME],ds_list))
+
+ if False:
+ if is_mitre:
+ mitre_count += 1
+
+ lookup_command = mitre_lookup[ cvesource[ORM.CVESOURCE_DATASOURCE_ID] ].replace('%command%','--cve-detail=%s' % cve[ORM.CVE_NAME])
+ result_returncode,result_stdout,result_stderr = execute_process(lookup_command.split(' '))
+ if 0 != result_returncode:
+ print("ERROR_LOOKUP:%s" % lookup_command)
+ return(1)
+ description = ''
+ for line in result_stdout.decode("utf-8").splitlines():
+ try:
+ name = line[:line.index('=')]
+ value = line[line.index('=')+1:].replace("[EOL]","\n")
+ except:
+ continue
+ if name == 'description':
+ description = value
+ break
+ if description:
+ # print("%s='%s'" % (cve[ORM.CVE_NAME],description))
+ sql = ''' UPDATE orm_cve
+ SET description = ?
+ WHERE id = ?'''
+ cur_ds.execute(sql, (description,cve[ORM.CVE_ID],))
+ # conn.commit()
+ # return(0)
+
+ elif 0 == ds_count:
+ nosource_count += 1
+ else:
+ print('%05d: %-20s, SourceList=%s' % (i,cve[ORM.CVE_NAME],ds_list))
+
+ # Progress indicator support
+ if 19 == i % 100:
+ print('%05d: %-20s\r' % (i,cve[ORM.CVE_NAME]), end='')
+ pass
+ if (0 == i % 200):
+# conn.commit()
+ #print('')
+ pass
+ # Development/debug support
+ if cmd_skip and (i < cmd_skip): continue
+ if cmd_count and ((i - cmd_skip) > cmd_count): break
+
+ print("CVE RESERVED COUNT=%d of %d, mitre=%d, no_source=%d" % (reserved_count,fix_count,mitre_count,nosource_count))
+# conn.commit()
+
+
+#################################
# find_multiple_defects
#
@@ -1047,6 +1248,40 @@ def find_bad_links():
conn.close()
+#################################
+# find_empty_status
+#
+
+def find_empty_status():
+
+ conn = sqlite3.connect(srtDbName)
+ cur = conn.cursor()
+ cur_del = conn.cursor()
+
+ #
+ print('\n=== CVE Empty Status Check ===\n')
+ #
+
+ cur.execute('SELECT * FROM orm_cve')
+ empty_count = 0
+ date_count = 0
+ other_count = 0
+ total = 0
+ for i,cve in enumerate(cur):
+ total += 1
+ if not cve[ORM.CVE_STATUS]:
+ empty_count += 1
+ elif '-' in cve[ORM.CVE_STATUS]:
+ date_count += 1
+ else:
+ try:
+ value = int(cve[ORM.CVE_STATUS])
+ except:
+ other_count += 1
+
+
+ print("STATUS: Empty=%d, Date=%d, OtherBad=%d, total=%d of %d" % (empty_count,date_count,other_count,empty_count+date_count+other_count,total))
+
#################################
# main loop
@@ -1057,6 +1292,7 @@ def main(argv):
global cmd_skip
global cmd_count
global force
+ global srtDbName
# setup
parser = argparse.ArgumentParser(description='srtool.py: manage the SRTool database')
@@ -1074,6 +1310,9 @@ def main(argv):
parser.add_argument('--fix-missing-create-dates', action='store_const', const='fix_missing_create_dates', dest='command', help='Reset CVE srt_create dates to NIST release dates')
parser.add_argument('--fix-public-reserved', action='store_const', const='fix_public_reserved', dest='command', help='Reset CVE NEW_RESERVED if now public')
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('--find-empty-status', action='store_const', const='find_empty_status', dest='command', help='foo')
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')
@@ -1081,6 +1320,8 @@ def main(argv):
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('--database', '-D', dest='database', help='Selected database file')
+
parser.add_argument('--force', '-f', action='store_true', dest='force', help='Force the 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='verbose', help='Debugging: verbose output')
@@ -1098,6 +1339,10 @@ def main(argv):
cmd_count = int(args.count)
force = args.force
+ # Test for example the backup databases
+ if args.database:
+ srtDbName = args.database
+
if args.sources:
if args.sources.startswith('s'):
sources("set")
@@ -1137,7 +1382,8 @@ def main(argv):
fix_remove_bulk_cve_history()
elif 'fix_defects_to_products' == args.command:
fix_defects_to_products()
-
+ elif 'fix_bad_mitre_init' == args.command:
+ fix_bad_mitre_init()
elif 'find_multiple_defects' == args.command:
find_multiple_defects()
@@ -1146,6 +1392,8 @@ def main(argv):
elif 'find_bad_links' == args.command:
find_bad_links()
+ elif 'find_empty_status' == args.command:
+ find_empty_status()
else:
print("Command not found")
diff --git a/bin/mitre/srtool_mitre.py b/bin/mitre/srtool_mitre.py
index 3928e51e..75789b7a 100755
--- a/bin/mitre/srtool_mitre.py
+++ b/bin/mitre/srtool_mitre.py
@@ -56,6 +56,8 @@ mitre_cache_dir = 'data/cache/mitre'
# Debugging support
verbose = False
+cmd_skip = 0
+cmd_count = 0
# Development support
overrides = {}
@@ -88,8 +90,7 @@ 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
@@ -110,19 +111,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:
- if True:
- 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
@@ -269,9 +261,6 @@ 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)
cur = conn.cursor()
cur_write = conn.cursor()
@@ -319,11 +308,11 @@ def append_cve_database(is_init,file_xml):
# Get the default CVE status
status = get_cve_default_status(is_init,summary['Published'],summary['Description'])
- # 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
+ # 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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'''
- 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(), 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
+ 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(), 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 = cur.lastrowid
print("MITRE:ADDED %20s\r" % cve_name)
@@ -410,6 +399,8 @@ def dump(file_xml):
def main(argv):
global verbose
+ global cmd_skip
+ global cmd_count
# setup
@@ -425,6 +416,8 @@ def main(argv):
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('--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')
@@ -432,6 +425,12 @@ def main(argv):
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 'dump' == args.command:
dump(mitre_cvrf_xml)
diff --git a/bin/nist/datasource.json b/bin/nist/datasource.json
index de52a6b4..28633b61 100644
--- a/bin/nist/datasource.json
+++ b/bin/nist/datasource.json
@@ -23,8 +23,8 @@
"attributes" : "PREVIEW-SOURCE",
"cve_filter" : "",
"init" : "",
- "update" : "bin/nist/srtool_nist.py --update_nist_incremental --source='NIST Modified Data' --file=data/nvdcve-1.0-modified.json --url-file=nvdcve-1.0-modified.json.gz --url-meta=nvdcve-1.0-modified.meta",
- "lookup" : "bin/nist/srtool_nist.py --file=data/nvdcve-1.0-modified.json %command%",
+ "update" : "bin/nist/srtool_nist.py --update_nist_incremental --source='NIST Modified Data' --file=data/nvdcve-1.1-modified.json --url-file=nvdcve-1.1-modified.json.gz --url-meta=nvdcve-1.1-modified.meta",
+ "lookup" : "bin/nist/srtool_nist.py --file=data/nvdcve-1.1-modified.json %command%",
"update_frequency" : "2",
"_comment_" : "Update at 7:00 am",
"update_time" : "{\"hour\":\"7\"}"
diff --git a/lib/srtgui/tables.py b/lib/srtgui/tables.py
index e80ee71e..e0a6fb42 100644
--- a/lib/srtgui/tables.py
+++ b/lib/srtgui/tables.py
@@ -1739,6 +1739,13 @@ class SourcesTable(ToasterTable):
def setup_columns(self, *args, **kwargs):
+ self.add_column(title="ID",
+ hideable=True,
+ hidden=True,
+ orderable=True,
+ field_name="id",
+ )
+
self.add_column(title="Key",
hideable=False,
orderable=True,
diff --git a/lib/srtgui/templates/publish.html b/lib/srtgui/templates/publish.html
index cf0f2294..826e1953 100644
--- a/lib/srtgui/templates/publish.html
+++ b/lib/srtgui/templates/publish.html
@@ -4,324 +4,49 @@
{% load projecttags %}
{% load humanize %}
-{% block title %} Publish Requests {% endblock %}
-
+{% block title %} Publish Reports {% endblock %}
{% block pagecontent %}
-<div class="row">
- <!-- Breadcrumbs -->
- <div class="col-md-12">
- <ul class="breadcrumb" id="breadcrumb">
- <li><a href="{% url 'landing' %}">Home</a></li><span class="divider">&rarr;</span>
- <li><a href="{% url 'manage' %}">Management</a></li><span class="divider">&rarr;</span>
- <li>Publish Report Management</li>
- </ul>
- </div>
-</div>
-
-<h2>Publish Report Management</h2>
-<ul>
- <li>The SRTool supports exporting new and updated CVEs to external publishing tools</li>
-</ul>
-
-<hr>
-
-<h2>Publish Via Database Snapshots</h2>
-<h3> On Demand</h3>
-<ul>
- <li>This extracts the changes from a 'base' database backup snapshot to more recent 'top' snapshot</li>
- <li>The 'start' and 'stop' dates can extract a subset of those changes. Normally they are set to the 'base' and 'top' dates</li>
-</ul>
-
-<div style="padding-left:30px;">
- <div>
- <label> Start Snapshot: </label>
- <select id="snap_date_base">
- {% for snap in snapshot_list %}
- <option value="{{snap.date}}" {% if snap_start_index == snap.index %}selected{% endif %}>
- ({{snap.mode}}) {{snap.date}} {{snap.time}} | {{snap.day}}
- </option>
- {% endfor %}
- </select>
- </div>
- <div>
- <label> Stop Snapshot: </label>
- <select id="snap_date_top">
- {% for snap in snapshot_list %}
- <option value="{{snap.date}}" {% if snap_stop_index == snap.index %}selected{% endif %}>
- ({{snap.mode}}) {{snap.date}} {{snap.time}} | {{snap.day}}
- </option>
- {% endfor %}
- </select>
- </div>
- <div>
- Start Date: <input type="text" id="snap_date_start" value="{{snap_date_start}}">&nbsp;&nbsp;
- Stop Date: <input type="text" id="snap_date_stop" value="{{snap_date_stop}}">&nbsp;&nbsp;
- <I>(Format: yyyy-mm-dd)</I>
- </div>
-<br>
-</div>
-
-<div>
- <span style="padding-left:30px;"><button id="export-snapshot" class="btn btn-default" type="button">Generate</button></span>
- <!--<button type="submit" name="action" value="export-snapshot">Export</button> -->
- <span id="export-snapshot-text">Generate the publish table on-demand (using snapshots)</span>
- <span id="generating-report" hidden style="color:red"><I>... Generating the report - this will take a few minutes ...</I></span>
-</div>
-<br>
-
-<form method="POST"> {% csrf_token %}
-<h3>Automatic (Under Development)</h3>
-<div style="padding-left: 25px;">
- <label> Frequency: </label>
- <select id="snap_frequency">
- {% for snap in snapshot_frequency_list %}
- <option value="{{snap}}" {% if snap == snap_frequency_select %}selected{% endif %}>
- {{snap}}
- </option>
- {% endfor %}
- </select>
- <span style="padding-left:30px;"><button id="export-snapshot" class="btn btn-default" type="button" disabled>Save</button></span>
- <!--<button type="submit" name="action" value="export-snapshot">Export</button> -->
- Save the automatic publishing frequency
-</div>
-</form>
-
-<h3>Generated Reports</h3>
-<div style="padding-left: 25px;">
- <table class="table table-striped table-condensed" data-testid="vuln-hyperlinks-table">
- <thead>
- <tr>
- <th>Name</th>
- <th>Size</th>
- <th>Date</th>
- <th>Manage</th>
- </tr>
- </thead>
- {% if generated_report_list %}
- {% for report in generated_report_list %}
- <tr>
- <td>{{report.name}}</td>
- <td>{{report.size}}</td>
- <td>{{report.date}}</td>
- <td>
- <span id="attachment_entry_'+{{report.name}}+'" class="js-config-var-name"></span>
- <form id="downloadbanner-{{forloop.counter}}" enctype="multipart/form-data" method="post" >{% csrf_token %}
- <input type="hidden" id="action" name="action" value="download">
- <input type="hidden" id="report_id" name="report_name" value={{report.name}}>
- <span class="glyphicon glyphicon-download-alt submit-downloadreport" id="report_download_'+{{report.name}}+'" x-data="{{forloop.counter}}"></span>
- {% if request.user.is_creator %}
- <span class="glyphicon glyphicon-trash trash-report" id="report_trash_'+{{report.name}}+'" x-data="{{report.name}}"></span>
- {% endif %}
- </form>
- </td>
- </tr>
- {% endfor %}
- {% else %}
- <tr>
- <td>No report files found</td>
- </tr>
- {% endif %}
- </table>
- (last report = {{snap_last_calc}})
-</div>
-
-<hr>
-
-<form method="POST"> {% csrf_token %}
-<h2>Publish Via History Tables (Under development)</h2>
-<ul>
- <li>These tools can be used to (a) gather the candidate CVEs, (b) review and edit the list if needed, (c) generate the report when ready</li>
- <li>The user can explicitly include and exclude CVEs from the "New" list and the "Updated" list, in case the automatic caltulations need adjustment</li>
- <li>These mark-ups are inserted into the respective CVE's history at a mid-point date of the period, so they are both persistent and period-specific</li>
- <li>The user can clear the markups from the given period and start over, but this will not affect any other period</li>
-</ul>
-<h3> Publish Preparation</h3>
-<ul>
- <div>
- Start Date: <input type="text" name="date_start" value="{{date_start}}">&nbsp;&nbsp;
- Stop Date: <input type="text" name="date_stop" value="{{date_stop}}">
- </div>
- <br>
- <div>
- Product filter:
- <select name="product-filter" id="select-product-filter">
- <option value="0">WR Linux Suported Products</option>
- </select>
- </div>
- <br>
- <div>
- <button type="submit" name="action" value="recalculate">Recalculate publish table</button>
- Gather the items for this period to be published from SRTool, with user changes (last done {{last_calc}})
- </div>
- <br>
- <div>
- <button type="submit" name="action" value="reset">Reset user edits, Recalculate</button>
- Remove the user changes for this period, recalculate the table
- </div>
-</ul>
-<h3> Publish Preview and Modifications</h3>
-<ul>
- <div>
- <button type="submit" name="action" value="view">View the publish table</button>
- View the publish table, prune entries
- </div>
- <br>
- <div>
- <button type="submit" name="action" value="add-cve">Add via CVEs</button>
- Add recent CVEs to the table
- </div>
- <br>
- <div>
- <button type="submit" name="action" value="add-defect">Add via defects</button>
- Add CVEs of recent defects to the table
- </div>
- <br>
-</ul>
-<h3> Publish the Report</h3>
-<ul>
- <div>
- <button type="submit" name="action" value="export">Export</button>
- Export the publish table (using history)
- </div>
- <br>
-</ul>
-</form>
-
-<script>
- var selected_newcomment=false;
-
- $(document).ready(function() {
-
- function onCommitAjaxSuccess(data, textstatus) {
- document.getElementById("export-snapshot").disabled = false;
- /* document.getElementById("download-snapshot").disabled = false;*/
- document.getElementById("export-snapshot-text").innerText = "Generate the publish table on-demand (using snapshots)";
- document.getElementById("generating-report").style.display = "block";
- if (window.console && window.console.log) {
- console.log("XHR returned:", data, "(" + textstatus + ")");
- } else {
- alert("NO CONSOLE:\n");
- return;
- }
- if (data.error != "ok") {
- alert("error on request:\n" + data.error);
- return;
- }
- // reload the page with the updated tables
- location.reload(true);
- }
-
- function onCommitAjaxError(jqXHR, textstatus, error) {
- console.log("ERROR:"+error+"|"+textstatus);
- alert("XHR errored1:\n" + error + "\n(" + textstatus + ")");
- document.getElementById("export-snapshot").disabled = false;
- document.getElementById("export-snapshot-text").innerText = "Generate the publish table on-demand (using snapshots)";
- /* document.getElementById("download-snapshot").disabled = false; */
- document.getElementById("generating-report").style.display = "block";
- }
-
- /* ensure cookie exists {% csrf_token %} */
- function postCommitAjaxRequest(reqdata) {
- var ajax = $.ajax({
- type:"POST",
- data: reqdata,
- url:"{% url 'xhr_publish' %}",
- headers: { 'X-CSRFToken': $.cookie("csrftoken")},
- success: onCommitAjaxSuccess,
- error: onCommitAjaxError,
- });
- }
-
- $("#snap_date_base").change(function(){
- snap_date_base = $("#snap_date_base").val();
- snap_date_top = $("#snap_date_top").val();
- if (snap_date_base > snap_date_top) {
- $("#snap_date_base").val(snap_date_top);
- $("#snap_date_top").val(snap_date_base);
- $("#snap_date_start").val(snap_date_top);
- $("#snap_date_stop").val(snap_date_base);
- } else {
- snap_date_start = $("#snap_date_start").val();
- snap_date_stop = $("#snap_date_stop").val();
- $("#snap_date_start").val(snap_date_base);
- if (snap_date_stop < snap_date_base) {
- $("#snap_date_stop").val(snap_date_top);
- }
- }
- });
-
- $("#snap_date_top").change(function(){
- snap_date_base = $("#snap_date_base").val();
- snap_date_top = $("#snap_date_top").val();
- if (snap_date_base > snap_date_top) {
- $("#snap_date_base").val(snap_date_top);
- $("#snap_date_top").val(snap_date_base);
- $("#snap_date_start").val(snap_date_top);
- $("#snap_date_stop").val(snap_date_base);
- } else {
- snap_date_start = $("#snap_date_start").val();
- snap_date_stop = $("#snap_date_stop").val();
- if (snap_date_start > snap_date_top) {
- $("#snap_date_start").val(snap_date_base);
- }
- $("#snap_date_stop").val(snap_date_top);
- }
- });
-
- $('#export-snapshot').click(function(){
- snap_date_base = $("#snap_date_base").val();
- snap_date_top = $("#snap_date_top").val();
- snap_date_start = $("#snap_date_start").val();
- snap_date_stop = $("#snap_date_stop").val();
- if (snap_date_start > snap_date_stop) {
- alert("Error: the start date is after the stop date");
- return;
- }
- if (snap_date_start < snap_date_base) {
- alert("Error: the start date is before the snapshot base date");
- return;
- }
- if (snap_date_stop > snap_date_top) {
- alert("Error: the stop date is after the snapshot top date");
- return;
- }
- var result = confirm("Generate the report? This will take several minutes.");
- if (result){
- document.getElementById("export-snapshot").disabled = true;
- document.getElementById("export-snapshot-text").innerText = "... Generating the report - this will take a few minutes ...";
-
- /* document.getElementById("download-snapshot").disabled = true; */
- document.getElementById("generating-report").style.display = "none";
- postCommitAjaxRequest({
- "action" : 'export-snapshot',
- "snap_date_base" : snap_date_base,
- "snap_date_top" : snap_date_top,
- "snap_date_start" : snap_date_start,
- "snap_date_stop" : snap_date_stop
- });
- }
- });
-
-
- /* Manage report files */
-
- $('.submit-downloadreport').click(function() {
- $("#downloadbanner-"+this.getAttribute("x-data")).submit();
- });
-
- $('.trash-report').click(function() {
- var result = confirm("Are you sure?");
- if (result){
- postCommitAjaxRequest({
- "action" : 'submit-trashreport',
- "report_name" : $(this).attr('x-data'),
- });
- }
- });
-
-
-
- });
-</script>
+ <div class="row">
+ <div class="col-md-7" style="padding-left: 50px;">
+ <h1>Management</h1>
+ </div>
+ </div>
+ <div class="row">
+ <div class="jumbotron well-transparent">
+
+ <div class="col-md-6">
+ <div>
+ <table class="table table-striped table-condensed" data-testid="landing-hyperlinks-table">
+ <thead>
+ <tr>
+ <th>Action</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+
+ <tr>
+ <td><a class="btn btn-info btn-lg" href="{% url 'report' 'publish-summary' %}">Summary of CVEs</a></td>
+ <td>Summary across CVEs and Products</td>
+ </tr>
+
+ <tr>
+ <td><a class="btn btn-info btn-lg" href="{% url 'publish_diff_snapshot' %}">Difference Snapshots [UNDER DEVELOPMENT]</a></td>
+ <td>Difference Report via Snapshots</td>
+ </tr>
+
+ <tr>
+ <td><a class="btn btn-info btn-lg" href="{% url 'publish_diff_history' %}">Difference History [UNDER DEVELOPMENT]</a></td>
+ <td>Difference Report via History</td>
+ </tr>
+
+ </table>
+ </div>
+
+ </div>
+
+ </div>
+
+ </div>
+ </div>
{% endblock %}
diff --git a/lib/srtgui/templates/publish_diff_snapshot.html b/lib/srtgui/templates/publish_diff_snapshot.html
new file mode 100644
index 00000000..cf0f2294
--- /dev/null
+++ b/lib/srtgui/templates/publish_diff_snapshot.html
@@ -0,0 +1,327 @@
+{% extends "base.html" %}
+
+{% load static %}
+{% load projecttags %}
+{% load humanize %}
+
+{% block title %} Publish Requests {% endblock %}
+
+{% block pagecontent %}
+<div class="row">
+ <!-- Breadcrumbs -->
+ <div class="col-md-12">
+ <ul class="breadcrumb" id="breadcrumb">
+ <li><a href="{% url 'landing' %}">Home</a></li><span class="divider">&rarr;</span>
+ <li><a href="{% url 'manage' %}">Management</a></li><span class="divider">&rarr;</span>
+ <li>Publish Report Management</li>
+ </ul>
+ </div>
+</div>
+
+<h2>Publish Report Management</h2>
+<ul>
+ <li>The SRTool supports exporting new and updated CVEs to external publishing tools</li>
+</ul>
+
+<hr>
+
+<h2>Publish Via Database Snapshots</h2>
+<h3> On Demand</h3>
+<ul>
+ <li>This extracts the changes from a 'base' database backup snapshot to more recent 'top' snapshot</li>
+ <li>The 'start' and 'stop' dates can extract a subset of those changes. Normally they are set to the 'base' and 'top' dates</li>
+</ul>
+
+<div style="padding-left:30px;">
+ <div>
+ <label> Start Snapshot: </label>
+ <select id="snap_date_base">
+ {% for snap in snapshot_list %}
+ <option value="{{snap.date}}" {% if snap_start_index == snap.index %}selected{% endif %}>
+ ({{snap.mode}}) {{snap.date}} {{snap.time}} | {{snap.day}}
+ </option>
+ {% endfor %}
+ </select>
+ </div>
+ <div>
+ <label> Stop Snapshot: </label>
+ <select id="snap_date_top">
+ {% for snap in snapshot_list %}
+ <option value="{{snap.date}}" {% if snap_stop_index == snap.index %}selected{% endif %}>
+ ({{snap.mode}}) {{snap.date}} {{snap.time}} | {{snap.day}}
+ </option>
+ {% endfor %}
+ </select>
+ </div>
+ <div>
+ Start Date: <input type="text" id="snap_date_start" value="{{snap_date_start}}">&nbsp;&nbsp;
+ Stop Date: <input type="text" id="snap_date_stop" value="{{snap_date_stop}}">&nbsp;&nbsp;
+ <I>(Format: yyyy-mm-dd)</I>
+ </div>
+<br>
+</div>
+
+<div>
+ <span style="padding-left:30px;"><button id="export-snapshot" class="btn btn-default" type="button">Generate</button></span>
+ <!--<button type="submit" name="action" value="export-snapshot">Export</button> -->
+ <span id="export-snapshot-text">Generate the publish table on-demand (using snapshots)</span>
+ <span id="generating-report" hidden style="color:red"><I>... Generating the report - this will take a few minutes ...</I></span>
+</div>
+<br>
+
+<form method="POST"> {% csrf_token %}
+<h3>Automatic (Under Development)</h3>
+<div style="padding-left: 25px;">
+ <label> Frequency: </label>
+ <select id="snap_frequency">
+ {% for snap in snapshot_frequency_list %}
+ <option value="{{snap}}" {% if snap == snap_frequency_select %}selected{% endif %}>
+ {{snap}}
+ </option>
+ {% endfor %}
+ </select>
+ <span style="padding-left:30px;"><button id="export-snapshot" class="btn btn-default" type="button" disabled>Save</button></span>
+ <!--<button type="submit" name="action" value="export-snapshot">Export</button> -->
+ Save the automatic publishing frequency
+</div>
+</form>
+
+<h3>Generated Reports</h3>
+<div style="padding-left: 25px;">
+ <table class="table table-striped table-condensed" data-testid="vuln-hyperlinks-table">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Size</th>
+ <th>Date</th>
+ <th>Manage</th>
+ </tr>
+ </thead>
+ {% if generated_report_list %}
+ {% for report in generated_report_list %}
+ <tr>
+ <td>{{report.name}}</td>
+ <td>{{report.size}}</td>
+ <td>{{report.date}}</td>
+ <td>
+ <span id="attachment_entry_'+{{report.name}}+'" class="js-config-var-name"></span>
+ <form id="downloadbanner-{{forloop.counter}}" enctype="multipart/form-data" method="post" >{% csrf_token %}
+ <input type="hidden" id="action" name="action" value="download">
+ <input type="hidden" id="report_id" name="report_name" value={{report.name}}>
+ <span class="glyphicon glyphicon-download-alt submit-downloadreport" id="report_download_'+{{report.name}}+'" x-data="{{forloop.counter}}"></span>
+ {% if request.user.is_creator %}
+ <span class="glyphicon glyphicon-trash trash-report" id="report_trash_'+{{report.name}}+'" x-data="{{report.name}}"></span>
+ {% endif %}
+ </form>
+ </td>
+ </tr>
+ {% endfor %}
+ {% else %}
+ <tr>
+ <td>No report files found</td>
+ </tr>
+ {% endif %}
+ </table>
+ (last report = {{snap_last_calc}})
+</div>
+
+<hr>
+
+<form method="POST"> {% csrf_token %}
+<h2>Publish Via History Tables (Under development)</h2>
+<ul>
+ <li>These tools can be used to (a) gather the candidate CVEs, (b) review and edit the list if needed, (c) generate the report when ready</li>
+ <li>The user can explicitly include and exclude CVEs from the "New" list and the "Updated" list, in case the automatic caltulations need adjustment</li>
+ <li>These mark-ups are inserted into the respective CVE's history at a mid-point date of the period, so they are both persistent and period-specific</li>
+ <li>The user can clear the markups from the given period and start over, but this will not affect any other period</li>
+</ul>
+<h3> Publish Preparation</h3>
+<ul>
+ <div>
+ Start Date: <input type="text" name="date_start" value="{{date_start}}">&nbsp;&nbsp;
+ Stop Date: <input type="text" name="date_stop" value="{{date_stop}}">
+ </div>
+ <br>
+ <div>
+ Product filter:
+ <select name="product-filter" id="select-product-filter">
+ <option value="0">WR Linux Suported Products</option>
+ </select>
+ </div>
+ <br>
+ <div>
+ <button type="submit" name="action" value="recalculate">Recalculate publish table</button>
+ Gather the items for this period to be published from SRTool, with user changes (last done {{last_calc}})
+ </div>
+ <br>
+ <div>
+ <button type="submit" name="action" value="reset">Reset user edits, Recalculate</button>
+ Remove the user changes for this period, recalculate the table
+ </div>
+</ul>
+<h3> Publish Preview and Modifications</h3>
+<ul>
+ <div>
+ <button type="submit" name="action" value="view">View the publish table</button>
+ View the publish table, prune entries
+ </div>
+ <br>
+ <div>
+ <button type="submit" name="action" value="add-cve">Add via CVEs</button>
+ Add recent CVEs to the table
+ </div>
+ <br>
+ <div>
+ <button type="submit" name="action" value="add-defect">Add via defects</button>
+ Add CVEs of recent defects to the table
+ </div>
+ <br>
+</ul>
+<h3> Publish the Report</h3>
+<ul>
+ <div>
+ <button type="submit" name="action" value="export">Export</button>
+ Export the publish table (using history)
+ </div>
+ <br>
+</ul>
+</form>
+
+<script>
+ var selected_newcomment=false;
+
+ $(document).ready(function() {
+
+ function onCommitAjaxSuccess(data, textstatus) {
+ document.getElementById("export-snapshot").disabled = false;
+ /* document.getElementById("download-snapshot").disabled = false;*/
+ document.getElementById("export-snapshot-text").innerText = "Generate the publish table on-demand (using snapshots)";
+ document.getElementById("generating-report").style.display = "block";
+ if (window.console && window.console.log) {
+ console.log("XHR returned:", data, "(" + textstatus + ")");
+ } else {
+ alert("NO CONSOLE:\n");
+ return;
+ }
+ if (data.error != "ok") {
+ alert("error on request:\n" + data.error);
+ return;
+ }
+ // reload the page with the updated tables
+ location.reload(true);
+ }
+
+ function onCommitAjaxError(jqXHR, textstatus, error) {
+ console.log("ERROR:"+error+"|"+textstatus);
+ alert("XHR errored1:\n" + error + "\n(" + textstatus + ")");
+ document.getElementById("export-snapshot").disabled = false;
+ document.getElementById("export-snapshot-text").innerText = "Generate the publish table on-demand (using snapshots)";
+ /* document.getElementById("download-snapshot").disabled = false; */
+ document.getElementById("generating-report").style.display = "block";
+ }
+
+ /* ensure cookie exists {% csrf_token %} */
+ function postCommitAjaxRequest(reqdata) {
+ var ajax = $.ajax({
+ type:"POST",
+ data: reqdata,
+ url:"{% url 'xhr_publish' %}",
+ headers: { 'X-CSRFToken': $.cookie("csrftoken")},
+ success: onCommitAjaxSuccess,
+ error: onCommitAjaxError,
+ });
+ }
+
+ $("#snap_date_base").change(function(){
+ snap_date_base = $("#snap_date_base").val();
+ snap_date_top = $("#snap_date_top").val();
+ if (snap_date_base > snap_date_top) {
+ $("#snap_date_base").val(snap_date_top);
+ $("#snap_date_top").val(snap_date_base);
+ $("#snap_date_start").val(snap_date_top);
+ $("#snap_date_stop").val(snap_date_base);
+ } else {
+ snap_date_start = $("#snap_date_start").val();
+ snap_date_stop = $("#snap_date_stop").val();
+ $("#snap_date_start").val(snap_date_base);
+ if (snap_date_stop < snap_date_base) {
+ $("#snap_date_stop").val(snap_date_top);
+ }
+ }
+ });
+
+ $("#snap_date_top").change(function(){
+ snap_date_base = $("#snap_date_base").val();
+ snap_date_top = $("#snap_date_top").val();
+ if (snap_date_base > snap_date_top) {
+ $("#snap_date_base").val(snap_date_top);
+ $("#snap_date_top").val(snap_date_base);
+ $("#snap_date_start").val(snap_date_top);
+ $("#snap_date_stop").val(snap_date_base);
+ } else {
+ snap_date_start = $("#snap_date_start").val();
+ snap_date_stop = $("#snap_date_stop").val();
+ if (snap_date_start > snap_date_top) {
+ $("#snap_date_start").val(snap_date_base);
+ }
+ $("#snap_date_stop").val(snap_date_top);
+ }
+ });
+
+ $('#export-snapshot').click(function(){
+ snap_date_base = $("#snap_date_base").val();
+ snap_date_top = $("#snap_date_top").val();
+ snap_date_start = $("#snap_date_start").val();
+ snap_date_stop = $("#snap_date_stop").val();
+ if (snap_date_start > snap_date_stop) {
+ alert("Error: the start date is after the stop date");
+ return;
+ }
+ if (snap_date_start < snap_date_base) {
+ alert("Error: the start date is before the snapshot base date");
+ return;
+ }
+ if (snap_date_stop > snap_date_top) {
+ alert("Error: the stop date is after the snapshot top date");
+ return;
+ }
+ var result = confirm("Generate the report? This will take several minutes.");
+ if (result){
+ document.getElementById("export-snapshot").disabled = true;
+ document.getElementById("export-snapshot-text").innerText = "... Generating the report - this will take a few minutes ...";
+
+ /* document.getElementById("download-snapshot").disabled = true; */
+ document.getElementById("generating-report").style.display = "none";
+ postCommitAjaxRequest({
+ "action" : 'export-snapshot',
+ "snap_date_base" : snap_date_base,
+ "snap_date_top" : snap_date_top,
+ "snap_date_start" : snap_date_start,
+ "snap_date_stop" : snap_date_stop
+ });
+ }
+ });
+
+
+ /* Manage report files */
+
+ $('.submit-downloadreport').click(function() {
+ $("#downloadbanner-"+this.getAttribute("x-data")).submit();
+ });
+
+ $('.trash-report').click(function() {
+ var result = confirm("Are you sure?");
+ if (result){
+ postCommitAjaxRequest({
+ "action" : 'submit-trashreport',
+ "report_name" : $(this).attr('x-data'),
+ });
+ }
+ });
+
+
+
+ });
+</script>
+
+{% endblock %}
diff --git a/lib/srtgui/urls.py b/lib/srtgui/urls.py
index a4947c51..c0df1c89 100644
--- a/lib/srtgui/urls.py
+++ b/lib/srtgui/urls.py
@@ -141,13 +141,17 @@ urlpatterns = [
name='manage_notifications'),
url(r'^triage_cves/$', views.triage_cves, name='triage_cves'),
url(r'^create_vulnerability/$', views.create_vulnerability, name='create_vulnerability'),
- url(r'^publish/$', views.publish, name='publish'),
url(r'^manage_report/$', views.manage_report, name='manage_report'),
url(r'^sources/$',
tables.SourcesTable.as_view(template_name="sources-toastertable.html"),
name='sources'),
url(r'^users/$', views.users, name='users'),
+ url(r'^publish/$', views.publish, name='publish'),
+ url(r'^publish_summary/$', views.publish_summary, name='publish_summary'),
+ url(r'^publish_diff_snapshot/$', views.publish_diff_snapshot, name='publish_diff_snapshot'),
+ url(r'^publish_diff_history/$', views.publish_diff_history, name='publish_diff_history'),
+
url(r'^maintenance/$', views.maintenance, name='maintenance'),
url(r'^history_cve/$',
tables.HistoryCveTable.as_view(template_name="history-cve-toastertable.html"),
diff --git a/lib/srtgui/views.py b/lib/srtgui/views.py
index 79bf7b17..7f819fd1 100644
--- a/lib/srtgui/views.py
+++ b/lib/srtgui/views.py
@@ -994,6 +994,26 @@ def publish(request):
# does this user have permission to see this record?
if not UserSafe.is_creator(request.user):
return redirect(landing)
+
+ context = {
+ }
+ return render(request, 'publish.html', context)
+
+def publish_summary(request):
+ # does this user have permission to see this record?
+ if not UserSafe.is_creator(request.user):
+ return redirect(landing)
+
+ context = {
+ }
+ return render(request, 'management.html', context)
+
+def publish_diff_snapshot(request):
+ # does this user have permission to see this record?
+ if not UserSafe.is_creator(request.user):
+ return redirect(landing)
+
+ main_app = SrtSetting.get_setting('SRT_MAIN_APP','yp')
if request.method == "GET":
# Prepare available snapshots
@@ -1037,9 +1057,145 @@ def publish(request):
]
# List of available reports
generated_report_list = []
- for entry in os.scandir('data/wr'):
- if entry.name.startswith('cve-svns-srtool'):
- generated_report_list.append(ReportFile(entry.name,entry.stat().st_size,datetime.fromtimestamp(entry.stat().st_mtime)))
+ if os.path.isdir('data/publish'):
+ for entry in os.scandir('data/publish'):
+ if entry.name.startswith('cve-svns-srtool'):
+ generated_report_list.append(ReportFile(entry.name,entry.stat().st_size,datetime.fromtimestamp(entry.stat().st_mtime)))
+# generated_report_list.sort()
+ generated_report_list = sorted(generated_report_list,key=lambda x: x.name)
+
+ # Prepare History data
+ last_calc = SrtSetting.get_setting('publish_last_calc','06/08/2019')
+ date_start = SrtSetting.get_setting('publish_date_start','06/08/2019')
+ date_stop = SrtSetting.get_setting('publish_date_stop','06/21/2019')
+
+ context = {
+ 'date_start' : date_start,
+ 'date_stop' : date_stop,
+ 'last_calc' : last_calc,
+
+ 'snap_date_start' : snap_date_start,
+ 'snap_date_stop' : snap_date_stop,
+ 'snap_date_base' : snap_date_base,
+ 'snap_date_top' : snap_date_top,
+ 'snapshot_list' : snapshot_list,
+ 'snap_start_index' : '%02d' % snap_start_index,
+ 'snap_stop_index' : '%02d' % snap_stop_index,
+ 'snap_last_calc' : snap_last_calc,
+ 'generated_report_list' : generated_report_list,
+
+ 'snapshot_frequency_list' : snapshot_frequency_list,
+ 'snap_frequency_select' : snap_frequency_select,
+ }
+ return render(request, 'publish_diff_snapshot.html', context)
+ elif request.method == "POST":
+ action = request.POST['action']
+
+ if request.POST["action"] == "download":
+ report_name = request.POST['report_name']
+ file_path = 'data/publish/%s' % (report_name)
+ if file_path:
+ fsock = open(file_path, "rb")
+ content_type = MimeTypeFinder.get_mimetype(file_path)
+ response = HttpResponse(fsock, content_type = content_type)
+ disposition = 'attachment; filename="{}"'.format(file_path)
+ response['Content-Disposition'] = 'attachment; filename="{}"'.format(file_path)
+ _log("EXPORT_POST_Q{%s} %s || %s " % (response, response['Content-Disposition'], disposition))
+ return response
+ else:
+ return render(request, "unavailable_artifact.html", context={})
+
+ # Dates (make as no timezone)
+ msg = ''
+ try:
+ msg = 'Start:%s' % request.POST.get('date_start', '')
+ date_start = datetime.strptime(request.POST.get('date_start', ''), '%m/%d/%Y')
+ msg = 'Stop:%s' % request.POST.get('date_stop', '')
+ date_stop = datetime.strptime(request.POST.get('date_stop', ''), '%m/%d/%Y')
+ if date_stop < date_start:
+# return 'Error:stop date is before start date'
+ _log('Error:stop date is before start date')
+ pass
+ except Exception as e:
+# return 'Error:bad format for dates (must be mm/dd/yyyy) (%s)(%s)' % (msg,e),''
+ _log('Error:bad format for dates (must be mm/dd/yyyy) (%s)(%s)' % (msg,e))
+ pass
+ SrtSetting.set_setting('publish_date_start',date_start.strftime('%m/%d/%Y'))
+ SrtSetting.set_setting('publish_date_stop',date_stop.strftime('%m/%d/%Y'))
+ if 'recalculate' == action:
+ # Calculate
+ publishCalculate(date_start,date_stop)
+ return redirect('publish')
+ if 'view' == action:
+ # Go to publish list page
+ return redirect('publish-list')
+ if 'add-cve' == action:
+ # Go to publish list page
+ return redirect('publish-cve')
+ if 'add-defect' == action:
+ # Go to publish list page
+ return redirect('publish-defect')
+ if 'reset' == action:
+ publishReset(date_start,date_stop)
+ publishCalculate(date_start,date_stop)
+ return redirect('publish')
+ if 'export' == action:
+ return redirect('/%s/report/publish' % main_app)
+ return redirect('publish')
+
+def publish_diff_history(request):
+ # does this user have permission to see this record?
+ if not UserSafe.is_creator(request.user):
+ return redirect(landing)
+
+ main_app = SrtSetting.get_setting('SRT_MAIN_APP','yp')
+ if request.method == "GET":
+
+ # Prepare available snapshots
+ snapshot_list = []
+ snap_start_index = 0
+ snap_stop_index = 0
+ snap_date_base = SrtSetting.get_setting('publish_snap_date_base','2019-06-08')
+ snap_date_top = SrtSetting.get_setting('publish_snap_date_top','2019-06-16')
+ snap_date_start = SrtSetting.get_setting('publish_snap_date_start','2019-06-08')
+ snap_date_stop = SrtSetting.get_setting('publish_snap_date_stop','2019-06-16')
+ snap_last_calc = SrtSetting.get_setting('publish_snap_last_calc','')
+ backup_returncode,backup_stdout,backup_result = execute_process('bin/common/srtool_backup.py','--list-backups-db')
+ for i,line in enumerate(backup_stdout.decode("utf-8").splitlines()):
+ # Week|backup_2019_19|2019-05-18|12:51:51|Saturday, May 18 2019
+ backup_mode,backup_dir,backup_date,backup_time,backup_day = line.split('|')
+ if 'Now' != backup_mode:
+ snap = Snap(i,backup_mode,backup_dir,backup_date,backup_time,backup_day)
+ snapshot_list.append(snap)
+ if snap_date_base == snap.date:
+ snap_start_index = i
+ if snap_date_start < snap.date:
+ snap_date_start = snap.date
+ if snap_date_stop < snap.date:
+ snap_date_stop = snap.date
+ if snap_date_top == snap.date:
+ snap_stop_index = i
+ if snap_date_stop > snap.date:
+ snap_date_stop = snap.date
+ if not snap_stop_index:
+ snap_stop_index = i
+ if snap_date_stop < snap.date:
+ snap_date_stop = snap.date
+ # Report automation
+ snap_frequency_select = SrtSetting.get_setting('publish_snap_frequency','Off')
+ snapshot_frequency_list = [
+ 'Off',
+ 'Monthly',
+ 'Bi-monthly',
+ 'Weekly',
+ 'Daily',
+ ]
+ # List of available reports
+ generated_report_list = []
+ if os.path.isdir('data/publish'):
+ for entry in os.scandir('data/publish'):
+ if entry.name.startswith('cve-svns-srtool'):
+ generated_report_list.append(ReportFile(entry.name,entry.stat().st_size,datetime.fromtimestamp(entry.stat().st_mtime)))
# generated_report_list.sort()
generated_report_list = sorted(generated_report_list,key=lambda x: x.name)
@@ -1072,7 +1228,7 @@ def publish(request):
if request.POST["action"] == "download":
report_name = request.POST['report_name']
- file_path = 'data/wr/%s' % report_name
+ file_path = 'data/publish/%s' % (report_name)
if file_path:
fsock = open(file_path, "rb")
content_type = MimeTypeFinder.get_mimetype(file_path)
@@ -1119,10 +1275,12 @@ def publish(request):
publishCalculate(date_start,date_stop)
return redirect('publish')
if 'export' == action:
- return redirect('/wr/report/publish')
+ return redirect('/%s/report/publish' % main_app)
return redirect('publish')
+
+
def manage_report(request):
# does this user have permission to see this record?
if not UserSafe.is_creator(request.user):
@@ -2146,6 +2304,8 @@ def xhr_investigation_commit(request):
def xhr_publish(request):
_log("xhr_publish(%s)" % request.POST)
+ main_app = SrtSetting.get_setting('SRT_MAIN_APP','yp')
+
def remove_mark(mark,line):
pos1 = line.find(mark)
if -1 == pos1:
@@ -2185,18 +2345,18 @@ def xhr_publish(request):
if (not top_dir) and (snap_date_top == backup_date) and ('Now' != backup_mode):
top_dir = 'backups/%s' % backup_dir
- _log('Publish:./bin/wr/srtool_publish.py --srt2update ' + base_dir)
- report_returncode,report_stdout,report_error = execute_process('./bin/wr/srtool_publish.py','--srt2update',base_dir)
+ _log('Publish:./bin/%s/srtool_publish.py --srt2update %s' % (main_app,base_dir))
+ report_returncode,report_stdout,report_error = execute_process('./bin/%s/srtool_publish.py' % main_app,'--srt2update',base_dir)
if 0 != report_returncode:
return_data = {"error": "Error: base dir prep:%s:%s" % (report_error,report_stdout),}
return HttpResponse(json.dumps( return_data ), content_type = "application/json")
- _log('Publish:./bin/wr/srtool_publish.py --srt2update ' + top_dir)
- report_returncode,report_stdout,report_error = execute_process('./bin/wr/srtool_publish.py','--srt2update',top_dir)
+ _log('Publish:./bin/%s/srtool_publish.py --srt2update %s' % (main_app,top_dir))
+ report_returncode,report_stdout,report_error = execute_process('./bin/%s/srtool_publish.py' % main_app,'--srt2update',top_dir)
if 0 != report_returncode:
return_data = {"error": "Error: top dir prep:%s:%s" % (report_error,report_stdout),}
return HttpResponse(json.dumps( return_data ), content_type = "application/json")
- _log('Publish:./bin/wr/srtool_publish.py --validate-update-svns --previous '+base_dir+' --current '+top_dir+' --start '+snap_date_start+' --stop '+snap_date_stop)
- report_returncode,report_stdout,report_error = execute_process('./bin/wr/srtool_publish.py',
+ _log('Publish:./bin/'+main_app+'/srtool_publish.py --validate-update-svns --previous '+base_dir+' --current '+top_dir+' --start '+snap_date_start+' --stop '+snap_date_stop)
+ report_returncode,report_stdout,report_error = execute_process('./bin/%s/srtool_publish.py' % main_app,
'--validate-update-svns','--previous',base_dir,'--current',top_dir,
'--start',snap_date_start,'--stop',snap_date_stop)
if 0 != report_returncode:
@@ -2212,7 +2372,7 @@ def xhr_publish(request):
_log('Publish:Done!')
elif 'submit-trashreport' == action:
report_name = request.POST['report_name']
- os.remove('data/wr/%s' % report_name)
+ os.remove('data/%s/%s' % (main_app,report_name))
else:
srtool_today_time = datetime.today()
srtool_today = datetime.today().strftime("%Y-%m-%d")
diff --git a/lib/yp/reports.py b/lib/yp/reports.py
new file mode 100755
index 00000000..dca99d10
--- /dev/null
+++ b/lib/yp/reports.py
@@ -0,0 +1,381 @@
+#
+# Security Response Tool Implementation
+#
+# Copyright (C) 2017-2020 Wind River Systems
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+# Please run flake8 on this file before sending patches
+
+import os
+import re
+import logging
+import json
+from collections import Counter
+from datetime import datetime, date
+import csv
+
+from orm.models import Cve, Vulnerability, Investigation, Defect, Product
+from orm.models import SRTool, PublishSet
+from srtgui.api import execute_process, readCveDetails, writeCveDetails, summaryCveDetails
+
+from srtgui.reports import Report, ReportManager, ProductsReport, ManagementReport, DefectsReport, PublishListReport
+
+
+from django.db.models import Q, F
+from django.db import Error
+from srtgui.templatetags.projecttags import filtered_filesizeformat
+
+logger = logging.getLogger("srt")
+
+SRT_BASE_DIR = os.environ['SRT_BASE_DIR']
+SRT_REPORT_DIR = '%s/reports' % SRT_BASE_DIR
+
+# quick development/debugging support
+from srtgui.api import _log
+
+def _log_args(msg, *args, **kwargs):
+ s = '%s:(' % msg
+ if args:
+ for a in args:
+ s += '%s,' % a
+ s += '),('
+ if kwargs:
+ for key, value in kwargs.items():
+ s += '(%s=%s),' % (key,value)
+ s += ')'
+ _log(s)
+
+###############################################################################
+#
+# YPPublishListReport: Yocto Project Management reports
+#
+
+class YPPublishListReport(PublishListReport):
+ """Report for the Publish Cve Page"""
+
+ def __init__(self, parent_page, *args, **kwargs):
+ _log_args("REPORT_YPPUBLISHLIST_INIT(%s)" % parent_page, *args, **kwargs)
+ super(YPPublishListReport, self).__init__(parent_page, *args, **kwargs)
+
+ def get_context_data(self, *args, **kwargs):
+ _log_args("REPORT_YPPUBLISHLIST_CONTEXT", *args, **kwargs)
+ context = super(YPPublishListReport, self).get_context_data(*args, **kwargs)
+
+ # Add a custom extension report type
+ context['report_type_list'] = '\
+ <option value="yp_summary">YP Summary Report</option> \
+ '
+
+ context['report_custom_list'] = ''
+ # Add scope
+ context['report_custom_list'] += '\
+ <input type="checkbox" id="new" name="new" checked>&nbsp;New CVEs</input> <br>\
+ <input type="checkbox" id="investigate" name="investigate" checked>&nbsp;Investigate CVEs</input> <br>\
+ <input type="checkbox" id="vulnerable" name="vulnerable" checked>&nbsp;Vulnerable CVEs</input> <br>\
+ <input type="checkbox" id="not-vulnerable" name="not-vulnerable" checked>&nbsp;Not Vulnerable CVEs</input> <br>\
+ <input type="checkbox" id="new-reserved" name="new-reserved" >&nbsp;New-Reserved CVEs</input> <br>\
+ <input type="checkbox" id="historical" name="historical" >&nbsp;Historical CVEs</input> <br>\
+ '
+ # Add extra
+ context['report_custom_list'] += '<br>'
+ context['report_custom_list'] += '\
+ <input type="checkbox" id="truncate" name="truncate" checked>&nbsp;Truncate fields (for simple text reports)</input> <BR>\
+ '
+
+ return context
+
+ def get_product_status_matrix(self,product_list,cve):
+ # Preset the default product status labels
+ status_table = {}
+ product_top_order = 99
+ product_top_defect = []
+ # Default all product status to the CVE's status
+ for product in product_list:
+ status_table[product.key] = SRTool.status_text(SRTool.NOT_VULNERABLE)
+ # Set the specific status for the child investigations
+ for cv in cve.cve_to_vulnerability.all():
+ #status_text = cv.vulnerability.get_status_text
+ for investigation in cv.vulnerability.vulnerability_investigation.all():
+# product_key = investigation.product.key
+ release_version_list = []
+ # Gather release versions, find the highest product's respective defect
+ for id in investigation.investigation_to_defect.all():
+ # Find defect(s) for higest ordered product
+ if product_top_order > investigation.product.order:
+ product_top_order = investigation.product.order
+ product_top_defect = []
+ if product_top_order == investigation.product.order:
+ product_top_defect.append(id.defect.name)
+ # Gather the status or release version
+ if id.defect.release_version:
+ release_version_list.append(id.defect.release_version)
+ release_version = '/'.join(release_version_list)
+ # Set investigation status, unless there are release versions
+ status_table[investigation.product.key] = investigation.get_status_text
+ if release_version:
+ status_table[investigation.product.key] = release_version
+ return status_table,product_top_defect
+
+ def exec_report(self, *args, **kwargs):
+ _log_args("REPORT_YPPUBLISHLIST_EXEC", *args, **kwargs)
+ super(YPPublishListReport, self).exec_report(*args, **kwargs)
+
+ request_POST = self.request.POST
+ format = request_POST.get('format', '')
+ report_type = request_POST.get('report_type', '')
+ csv_separator = request_POST.get('csv_separator', 'semi')
+ truncate = ('on' == request_POST.get('truncate', 'off'))
+ status_list = []
+ if ('on' == request_POST.get('new', 'off')): status_list.append(Cve.NEW)
+ if ('on' == request_POST.get('investigate', 'off')): status_list.append(Cve.INVESTIGATE)
+ if ('on' == request_POST.get('vulnerable', 'off')): status_list.append(Cve.VULNERABLE)
+ if ('on' == request_POST.get('not-vulnerable', 'off')): status_list.append(Cve.NOT_VULNERABLE)
+ if ('on' == request_POST.get('new-reserved', 'off')): status_list.append(Cve.NEW_RESERVED)
+ if ('on' == request_POST.get('historical', 'off')): status_list.append(Cve.HISTORICAL)
+
+ # Default to the regular report output if not our custom extension
+ if not report_type in ('yp_summary'):
+ return(super(YPPublishListReport, self).exec_report(*args, **kwargs))
+
+ if 'csv' == format:
+ separator = ';'
+ if csv_separator == 'comma': separator = ','
+ if csv_separator == 'tab': separator = '\t'
+ report_name = '%s/cve-svns-srtool-%s.csv' % (SRT_REPORT_DIR,datetime.today().strftime('%Y_%m_%d'))
+ else:
+ separator = ","
+ report_name = '%s/cve-svns-srtool-%s.txt' % (SRT_REPORT_DIR,datetime.today().strftime('%Y_%m_%d'))
+
+ # Get the desired product list
+ product_list = Product.objects.order_by('-order')
+
+ if 'yp_summary' == report_type:
+ with open(report_name, 'w', newline='') as csvfile:
+ writer = None
+
+ # Assemble the header
+ text_format = '%-18s,%16s,%-8s,%-8s,%-15s,%-15s,%-30s,%-25s,%15s,%15s,%20s,'
+ header = [
+ 'CVE Number',
+ 'Status',
+ 'CVSSv2_Severity',
+ 'CVSSv2_Score',
+ 'CVSSv3_Severity',
+ 'CVSSv3_Score',
+ 'CVE Description',
+ 'YP Comments',
+ 'Created Date',
+ 'Modified Date',
+ 'YP Acknowledged Date',
+ ]
+ # Assemble the product column namess
+ for product in product_list:
+ product_title = product.key
+ header.append(product_title)
+ min_len = max(16,len(product_title)+1)
+ str_format = "%s%ds," % ('%',min_len)
+ text_format += str_format
+# # Add Top Defect
+# header.append('Top Defect')
+# text_format += '%s'
+
+ # Print the header
+ if 'csv' == format:
+ writer = csv.writer(csvfile, delimiter=separator, quotechar='"', quoting=csv.QUOTE_MINIMAL)
+ writer.writerow(header)
+ else:
+ writer = csvfile
+ print(text_format % tuple(header), file=csvfile)
+
+ for i,cve in enumerate(Cve.objects.filter(status__in=status_list).order_by('name_sort')):
+ # Compute the product columns
+ status_table,product_top_defect = self.get_product_status_matrix(product_list,cve)
+ # Assemble the row data
+ if cve.description:
+ if truncate:
+ description = cve.description[:26] + '...'
+ else:
+ description = cve.description
+ else:
+ description = ''
+
+ # Use publish date if acknowledge date not available
+ try:
+ acknowledge_date = cve.acknowledge_date
+ if not acknowledge_date:
+ acknowledge_date = datetime.strptime(cve.publishedDate, '%Y-%m-%d')
+ acknowledge_date = acknowledge_date.strftime('%m/%d/%Y')
+ except:
+ acknowledge_date = ''
+ _log("NO ACK:%s,%s" % (cve.acknowledge_date,cve.publishedDate))
+
+ row = [
+ cve.name,
+ cve.get_status_text,
+ cve.cvssV2_severity,
+ cve.cvssV2_baseScore,
+ cve.cvssV3_baseSeverity,
+ cve.cvssV3_baseScore,
+ description,
+ cve.get_public_comments[:20] if truncate else cve.get_public_comments,
+ cve.srt_created.strftime('%Y/%m/%d') if cve.srt_created else '',
+ cve.srt_updated.strftime('%Y/%m/%d') if cve.srt_updated else '',
+ acknowledge_date,
+ ]
+ # Append the product columns
+ for product in product_list:
+ # Show inactive status as normal status
+ row.append(status_table[product.key].replace('(','').replace(')',''))
+# row.append('/'.join(product_top_defect))
+ # Print the row
+ if 'csv' == format:
+ writer.writerow(row)
+ else:
+ print(text_format % tuple(row), file=writer)
+
+
+ return report_name,os.path.basename(report_name)
+
+###############################################################################
+#
+# EXAMPLE: simple custom extention to the Products report
+#
+class YPProductsReport(ProductsReport):
+ """Report for the Products Page"""
+
+ def __init__(self, parent_page, *args, **kwargs):
+ _log_args("YP_REPORT_PRODUCTS_INIT(%s)" % parent_page, *args, **kwargs)
+ super(YPProductsReport, self).__init__(parent_page, *args, **kwargs)
+
+ def get_context_data(self, *args, **kwargs):
+ _log_args("YP_REPORT_PRODUCTS_CONTEXT", *args, **kwargs)
+
+ # Fetch the default report context definition
+ context = super(YPProductsReport, self).get_context_data(*args, **kwargs)
+
+ # Add a custom extension report type
+ context['report_type_list'] += '\
+ <option value="wr_summary">YP Products Table</option> \
+ '
+
+ # Done!
+ return context
+
+ def exec_report(self, *args, **kwargs):
+ _log_args("YP_REPORT_PRODUCTS_EXEC", *args, **kwargs)
+
+ request_POST = self.request.POST
+
+ records = request_POST.get('records', '')
+ format = request_POST.get('format', '')
+ title = request_POST.get('title', '')
+ report_type = request_POST.get('report_type', '')
+ record_list = request_POST.get('record_list', '')
+
+ # Default to the regular report output if not our custom extension
+ if 'wr_summary' != report_type:
+ return(super(YPProductsReport, self).exec_report(*args, **kwargs))
+
+ # CUSTOM: prepend "wr" to the generated file name
+ report_name = '%s/wr_products_%s_%s.%s' % (SRT_REPORT_DIR,report_type,datetime.today().strftime('%Y%m%d_%H%M'),format)
+ with open(report_name, 'w') as file:
+
+ if 'csv' == format:
+ separator = "\t"
+ else:
+ separator = ","
+
+ if ('wr_summary' == report_type):
+ if 'csv' == format:
+ # CUSTOM: prepend "YP" to the generated header
+ file.write("YP Name\tVersion\tProfile\tCPE\tSRT SPE\tInvestigations\tDefects\n")
+ if 'txt' == format:
+ # CUSTOM: prepend "YP" to the generated title
+ file.write("Report : YP Products Table\n")
+ file.write("\n")
+ # CUSTOM: prepend "YP" to the generated header
+ file.write("YP Name,Version,Profile,CPE,SRT SPE,Investigations,Defects\n")
+
+ for product in Product.objects.all():
+ # CUSTOM: prepend "YP" to the product name
+ file.write("YP %s%s" % (product.name,separator))
+ file.write("%s%s" % (product.version,separator))
+ file.write("%s%s" % (product.profile,separator))
+ file.write("%s%s" % (product.cpe,separator))
+ file.write("%s%s" % (product.defect_tags,separator))
+ file.write("%s%s" % (product.product_tags,separator))
+
+ for i,pi in enumerate(product.product_investigation.all()):
+ if i > 0:
+ file.write(" ")
+ file.write("%s" % (pi.name))
+ file.write("%s" % separator)
+ for i,pd in enumerate(product.product_defect.all()):
+ if i > 0:
+ file.write(" ")
+ file.write("%s" % (pd.name))
+ #file.write("%s" % separator)
+ file.write("\n")
+
+ return report_name,os.path.basename(report_name)
+
+###############################################################################
+#
+# Yocto Projects reports
+#
+# Available 'parent_page' values:
+# cve
+# vulnerability
+# investigation
+# defect
+# cves
+# select-cves
+# vulnerabilities
+# investigations
+# defects
+# products
+# select-publish
+# update-published
+# package-filters
+# cpes_srtool
+#
+
+class YPReportManager():
+ @staticmethod
+ def get_report_class(parent_page, *args, **kwargs):
+ _log("FOO:YPReportManager:'%s'" % parent_page)
+
+ if 'products' == parent_page:
+ # Extend the Products report
+ return YPProductsReport(parent_page, *args, **kwargs)
+
+ elif 'publish-summary' == parent_page:
+ return YPPublishListReport(parent_page, *args, **kwargs)
+
+ else:
+ # Return the default for all other reports
+ return ReportManager.get_report_class(parent_page, *args, **kwargs)
+
+ @staticmethod
+ def get_context_data(parent_page, *args, **kwargs):
+ _log_args("YP_REPORTMANAGER_CONTEXT", *args, **kwargs)
+ reporter = YPReportManager.get_report_class(parent_page, *args, **kwargs)
+ return reporter.get_context_data(*args, **kwargs)
+
+ @staticmethod
+ def exec_report(parent_page, *args, **kwargs):
+ _log_args("YP_REPORTMANAGER_EXEC", *args, **kwargs)
+ reporter = YPReportManager.get_report_class(parent_page, *args, **kwargs)
+ return reporter.exec_report(*args, **kwargs)
diff --git a/lib/yp/urls.py b/lib/yp/urls.py
index 586b87b6..494de9ae 100755
--- a/lib/yp/urls.py
+++ b/lib/yp/urls.py
@@ -5,4 +5,8 @@ urlpatterns = [
url(r'^hello/$', views.yp_hello, name='yp_hello'),
url(r'^$', views.yp_hello, name='yp_default'),
+
+ url(r'^report/(?P<page_name>\D+)$', views.report, name='report'),
+ url(r'^manage_report/$', views.manage_report, name='manage_report'),
+
]
diff --git a/lib/yp/views.py b/lib/yp/views.py
index 2d6d0043..3310e7e6 100755
--- a/lib/yp/views.py
+++ b/lib/yp/views.py
@@ -4,7 +4,7 @@
#
# Security Response Tool Implementation
#
-# Copyright (C) 2017-2018 Wind River Systems
+# Copyright (C) 2017-2020 Wind River Systems
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
@@ -21,9 +21,13 @@
#from django.urls import reverse_lazy
#from django.views import generic
-#from django.http import HttpResponse, HttpResponseNotFound, JsonResponse, HttpResponseRedirect
+from django.http import HttpResponse, HttpResponseNotFound, JsonResponse, HttpResponseRedirect
from django.shortcuts import render, redirect
+from users.models import SrtUser, UserSafe
+from srtgui.views import MimeTypeFinder
+from yp.reports import YPReportManager
+
#from orm.models import SrtSetting
# quick development/debugging support
@@ -33,3 +37,47 @@ def yp_hello(request):
context = {}
_log("Note:yp_hello")
return render(request, 'yp_hello.html', context)
+
+def report(request,page_name):
+ if request.method == "GET":
+ context = YPReportManager.get_context_data(page_name,request=request)
+ record_list = request.GET.get('record_list', '')
+ _log("EXPORT_GET!:%s|%s|" % (request,record_list))
+ context['record_list'] = record_list
+ return render(request, 'report.html', context)
+ elif request.method == "POST":
+ _log("EXPORT_POST!:%s|%s" % (request,request.FILES))
+ parent_page = request.POST.get('parent_page', '')
+ file_name,response_file_name = YPReportManager.exec_report(parent_page,request=request)
+
+ if file_name.startswith("Error"):
+ # Refresh the page with the error message
+ context = YPReportManager.get_context_data(page_name,request=request)
+ context['error_message'] = file_name
+ record_list = request.GET.get('record_list', '')
+ _log("EXPORT_GET_WITH_ERROR!:%s|%s|" % (request,record_list))
+ context['record_list'] = record_list
+ return render(request, 'report.html', context)
+ elif file_name and response_file_name:
+ fsock = open(file_name, "rb")
+ content_type = MimeTypeFinder.get_mimetype(file_name)
+
+ response = HttpResponse(fsock, content_type = content_type)
+
+ disposition = "attachment; filename=" + response_file_name
+ response["Content-Disposition"] = disposition
+
+ _log("EXPORT_POST_Q{%s|" % (response))
+ return response
+ else:
+ return render(request, "unavailable_artifact.html", {})
+
+ return redirect('/')
+ raise Exception("Invalid HTTP method for this page")
+
+def manage_report(request):
+ # does this user have permission to see this record?
+ if not UserSafe.is_creator(request.user):
+ return redirect('/')
+
+ return redirect(report,'management')