diff options
Diffstat (limited to 'lib')
42 files changed, 5716 insertions, 1574 deletions
diff --git a/lib/orm/fixtures/common.xml b/lib/orm/fixtures/common.xml index 5f095372..97bbd8e9 100644 --- a/lib/orm/fixtures/common.xml +++ b/lib/orm/fixtures/common.xml @@ -1,27 +1,107 @@ <?xml version="1.0" encoding="utf-8"?> <django-objects version="1.0"> - <!-- Set the common data sources (starts at 1) --> + +<!-- Set the common data settings (starts at 1) --> + + <object model="orm.srtsetting" pk="1"> + <field type="CharField" name="name">SRTOOL_FIXTURE_LIST_FALLBACK</field> + <field type="CharField" name="value">yp,nist</field> + </object> + + <object model="orm.srtsetting" pk="10"> + <field type="CharField" name="name">SRTOOL_DEFECT_UPDATE_FALLBACK</field> + <field type="CharField" name="value">bin/srtool_defect.py --update</field> + </object> + <object model="orm.srtsetting" pk="11"> + <field type="CharField" name="name">SRTOOL_DEFECT_ADD_FALLBACK</field> + <field type="CharField" name="value">bin/srtool_defect.py --add</field> + </object> + <object model="orm.srtsetting" pk="12"> + <field type="CharField" name="name">SRTOOL_DEFECT_DEL_FALLBACK</field> + <field type="CharField" name="value">bin/srtool_defect.py --del</field> + </object> + <object model="orm.srtsetting" pk="13"> + <field type="CharField" name="name">SRTOOL_DEFECT_NEW_FALLBACK</field> + <field type="CharField" name="value">bin/srtool_defect.py --new</field> + </object> + <object model="orm.srtsetting" pk="14"> + <field type="CharField" name="name">SRTOOL_DEFECT_SAMPLENAME_FALLBACK</field> + <field type="CharField" name="value">54321</field> + </object> + +<!-- Set the common data sources (starts at 1) --> + +<!-- Full production size keyword list <object model="orm.datasource" pk="1"> <field type="CharField" name="data">triage_keywords</field> <field type="CharField" name="source">common</field> <field type="CharField" name="type">csv</field> <field type="TextField" name="description">Table of keyword filters</field> - <field type="FilePathField" name="file_path">data/keyword_filters.csv</field> + <field type="FilePathField" name="file_path">data/keyword_filters_full.csv</field> <field type="TextField" name="url"></field> + <field type="CharField" name="lastModifiedDate">2018-03-01 01:01:01</field> + <field type="IntegerField" name="update_frequency">3</field> + <field type="CharField" name="update_time">02:00:00</field> </object> - - <!-- TEST DATA SOURCES --> +--> -<!-- - <object model="orm.datasource" pk="10"> - <field type="CharField" name="data">test</field> +<!-- Debug size keyword list --> + <object model="orm.datasource" pk="1"> + <field type="CharField" name="data">triage_keywords</field> <field type="CharField" name="source">common</field> <field type="CharField" name="type">csv</field> - <field type="TextField" name="description">TEST: CVE composite status charts</field> - <field type="FilePathField" name="file_path">data/test_data.csv</field> + <field type="TextField" name="description">Table of keyword filters</field> + <field type="FilePathField" name="file_path">data/keyword_filters.csv</field> <field type="TextField" name="url"></field> + <field type="CharField" name="lastModifiedDate">2018-03-01 01:01:01</field> + <field type="IntegerField" name="update_frequency">3</field> + <field type="CharField" name="update_time">02:00:00</field> + </object> +<!-- --> + + <object model="orm.datasource" pk="2"> + <field type="CharField" name="data">backup_weekly</field> + <field type="CharField" name="source">common</field> + <field type="CharField" name="type">script</field> + <field type="TextField" name="description">Weekly archive database backup</field> + <field type="FilePathField" name="file_path"></field> + <field type="TextField" name="url"></field> + <field type="CharField" name="lastModifiedDate">0001-01-01 01:01:01</field> + <field type="IntegerField" name="update_frequency">3</field> + <field type="CharField" name="update_time">02:00:00</field> + <field type="TextField" name="command">bin/srtool_utils.py --backup-db-json</field> + </object> + + <object model="orm.datasource" pk="3"> + <field type="CharField" name="data">backup_daily</field> + <field type="CharField" name="source">common</field> + <field type="CharField" name="type">script</field> + <field type="TextField" name="description">Daily database backup (wheel)</field> + <field type="FilePathField" name="file_path"></field> + <field type="TextField" name="url"></field> + <field type="CharField" name="lastModifiedDate">0001-01-01 01:01:01</field> + <field type="IntegerField" name="update_frequency">2</field> + <field type="CharField" name="update_time">02:00:00</field> + <field type="TextField" name="command">bin/srtool_utils.py --backup-db-json-daily</field> + </object> + +<!-- Built-in Users : first user is default log-on user --> + + <object model="orm.user" pk="1"> + <field type="TextField" name="name">Guest</field> + <field type="TextField" name="email"></field> + <field type="TextField" name="role">Guest</field> + <field type="IntegerField" name="access">0</field> + <field type="TextField" name="password"></field> + </object> + + <object model="orm.user" pk="2"> + <field type="TextField" name="name">SRTool</field> + <field type="TextField" name="email"></field> + <field type="TextField" name="role">SRTool automation scripts</field> + <field type="IntegerField" name="access">3</field> + <field type="TextField" name="password"></field> </object> ---> </django-objects> diff --git a/lib/orm/fixtures/nist.xml b/lib/orm/fixtures/nist.xml index 3b65f2e5..b392e57b 100644 --- a/lib/orm/fixtures/nist.xml +++ b/lib/orm/fixtures/nist.xml @@ -9,27 +9,43 @@ <field type="TextField" name="description">NIST Common Weakness Enumeration Data</field> <field type="FilePathField" name="file_path">data/nist-cwe-summary.html</field> <field type="TextField" name="url">https://nvd.nist.gov/vuln/categories</field> + <field type="CharField" name="lastModifiedDate">0001-01-01 01:01:01</field> + <field type="IntegerField" name="update_frequency">3</field> + <field type="CharField" name="update_time">02:00:00</field> + <field type="TextField" name="command"></field> </object> - <!-- NIST data feeds: https://nvd.nist.gov/vuln/data-feeds#JSON_FEED --> + <object model="orm.datasource" pk="23"> <field type="CharField" name="data">cve</field> <field type="CharField" name="source">nist</field> - <field type="CharField" name="type">json</field> + <field type="CharField" name="type">script</field> <field type="TextField" name="description">NIST JSON Data 2017</field> <field type="FilePathField" name="file_path">data/nvdcve-1.0-2017.json</field> <field type="TextField" name="url">https://static.nvd.nist.gov/feeds/json/cve/1.0/nvdcve-1.0-2017.json.gz</field> + <field type="TextField" name="meta_url">https://nvd.nist.gov/feeds/json/cve/1.0/nvdcve-1.0-2017.meta</field> + <field type="CharField" name="lastModifiedDate">0001-01-01 01:01:01</field> + <field type="IntegerField" name="update_frequency">3</field> + <field type="CharField" name="update_time">02:00:00</field> + <field type="TextField" name="command">bin/srtool_cve.py -n "NIST JSON Data 2017"</field> </object> + <object model="orm.datasource" pk="24"> <field type="CharField" name="data">cve</field> <field type="CharField" name="source">nist</field> - <field type="CharField" name="type">json</field> + <field type="CharField" name="type">script</field> <field type="TextField" name="description">NIST JSON Data 2018</field> <field type="FilePathField" name="file_path">data/nvdcve-1.0-2018.json</field> <field type="TextField" name="url">https://static.nvd.nist.gov/feeds/json/cve/1.0/nvdcve-1.0-2018.json.gz</field> + <field type="TextField" name="meta_url">https://nvd.nist.gov/feeds/json/cve/1.0/nvdcve-1.0-2018.meta</field> + <field type="CharField" name="lastModifiedDate">0001-01-01 01:01:01</field> + <field type="IntegerField" name="update_frequency">3</field> + <field type="CharField" name="update_time">02:00:00</field> + <field type="TextField" name="command">bin/srtool_cve.py -n "NIST JSON Data 2018"</field> </object> + <!-- <object model="orm.datasource" pk="25"> <field type="CharField" name="data">cve</field> @@ -44,10 +60,15 @@ <object model="orm.datasource" pk="26"> <field type="CharField" name="data">cve</field> <field type="CharField" name="source">nist</field> - <field type="CharField" name="type">json</field> - <field type="TextField" name="description">NIST JSON Modified Data 2017</field> + <field type="CharField" name="type">script</field> + <field type="TextField" name="description">NIST JSON Modified Data</field> <field type="FilePathField" name="file_path">data/nvdcve-1.0-modified.json</field> <field type="TextField" name="url">https://static.nvd.nist.gov/feeds/json/cve/1.0/nvdcve-1.0-modified.json.gz</field> + <field type="TextField" name="meta_url">https://nvd.nist.gov/feeds/json/cve/1.0/nvdcve-1.0-modified.meta</field> + <field type="CharField" name="lastModifiedDate">0001-01-01 01:01:01</field> + <field type="IntegerField" name="update_frequency">2</field> + <field type="CharField" name="update_time">02:00:00</field> + <field type="TextField" name="command">bin/srtool_cve.py -n "NIST JSON Modified Data"</field> </object> </django-objects> diff --git a/lib/orm/fixtures/samples.xml b/lib/orm/fixtures/samples.xml index 0d22770a..04b865b0 100644 --- a/lib/orm/fixtures/samples.xml +++ b/lib/orm/fixtures/samples.xml @@ -1,25 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <django-objects version="1.0"> - <!-- Set up test data for Products --> - - <object model="orm.product" pk="1"> - <field type="CharField" name="name">Yocto Project</field> - <field type="CharField" name="version">2.5 (Sumo)</field> - <field type="CharField" name="profile"></field> - </object> - <object model="orm.product" pk="2"> - <field type="CharField" name="name">Yocto Project</field> - <field type="CharField" name="version">2.4 (Rocko)</field> - <field type="CharField" name="profile"></field> - </object> - <object model="orm.product" pk="3"> - <field type="CharField" name="name">Yocto Project</field> - <field type="CharField" name="version">2.3 (Pyro)</field> - <field type="CharField" name="profile"></field> - </object> - - <object model="orm.cve" pk="1"> <field type="CharField" name="name">CVE-2017-0000</field> <field type="BooleanField" name="public">False</field> @@ -94,12 +75,6 @@ <field type="DateField" name="date">2017-12-14</field> <field type="TextField" name="author">Mark Hatle</field> </object> - <object model="orm.vulnerabilitycomments" pk="2"> - <field type="ForeignKey" name="vulnerability">1</field> - <field type="TextField" name="comment">ACTION: User 'Paul Gortmaker' 'Bruce Ashfield' added</field> - <field type="DateField" name="date">2017-12-14</field> - <field type="TextField" name="author">Mark Hatle</field> - </object> <object model="orm.vulnerabilitycomments" pk="3"> <field type="ForeignKey" name="vulnerability">1</field> <field type="TextField" name="comment">ACTION: Attachment 'PowerDNS Security Advisories 2018-02'</field> @@ -166,7 +141,7 @@ <field type="IntegerField" name="priority">3</field> <field type="IntegerField" name="status">5</field> <field type="IntegerField" name="resolution">1</field> - <field type="IntegerField" name="publishOLS">Reviewed - Publish</field> + <field type="IntegerField" name="publish">Reviewed - Publish</field> <field type="CharField" name="release_version">2.5.1</field> <field type="ForeignKey" name="product">1</field> </object> @@ -176,7 +151,7 @@ <field type="CharField" name="summary">(TEST) This is another defect</field> <field type="IntegerField" name="priority">2</field> <field type="IntegerField" name="status">0</field> - <field type="IntegerField" name="publishOLS">Not Reviewed</field> + <field type="IntegerField" name="publish">Not Reviewed</field> <field type="CharField" name="release_version"></field> <field type="ForeignKey" name="product">1</field> </object> @@ -206,112 +181,38 @@ <field type="ForeignKey" name="defect">2</field> </object> - <!-- Set up default users --> - - <object model="orm.user" pk="1"> - <field type="TextField" name="name">Guest</field> - <field type="TextField" name="email"></field> - <field type="TextField" name="role">Guest</field> - <field type="IntegerField" name="access">0</field> - <field type="TextField" name="password"></field> - </object> - - <object model="orm.user" pk="2"> - <field type="TextField" name="name">All</field> - <field type="TextField" name="email"></field> - <field type="TextField" name="role">internal all access placeholder</field> - <field type="IntegerField" name="access">0</field> - <field type="TextField" name="password"></field> - </object> - - <object model="orm.user" pk="3"> - <field type="TextField" name="name">Ross Burton</field> - <field type="TextField" name="email"></field> - <field type="TextField" name="role">Security Manager Yocto Project</field> - <field type="IntegerField" name="access">1</field> - <field type="TextField" name="password"></field> - </object> - - <object model="orm.user" pk="4"> - <field type="TextField" name="name">Richard Purtie</field> - <field type="TextField" name="email"></field> - <field type="TextField" name="role">Security Manager Yocto Project (backup)</field> - <field type="IntegerField" name="access">1</field> - <field type="TextField" name="password"></field> - </object> - - <object model="orm.user" pk="5"> - <field type="TextField" name="name">Mark Hatle</field> - <field type="TextField" name="email"></field> - <field type="TextField" name="role">Product Security Expert Wind River</field> - <field type="IntegerField" name="access">2</field> - <field type="TextField" name="password"></field> - </object> - - <object model="orm.user" pk="6"> - <field type="TextField" name="name">Jason Wessel</field> - <field type="TextField" name="email"></field> - <field type="TextField" name="role">Product Security Expert WRLinux (backup)</field> - <field type="IntegerField" name="access">2</field> - <field type="TextField" name="password"></field> - </object> - - <object model="orm.user" pk="7"> - <field type="TextField" name="name">Jefro Osier-Mixon</field> - <field type="TextField" name="email"></field> - <field type="TextField" name="role">Product Owner Yocto Project</field> - <field type="IntegerField" name="access">1</field> - <field type="TextField" name="password"></field> - </object> - - <object model="orm.user" pk="8"> - <field type="TextField" name="name">Stephen Jolley</field> - <field type="TextField" name="email"></field> - <field type="TextField" name="role">Product Owner Yocto Project (backup)</field> - <field type="IntegerField" name="access">1</field> - <field type="TextField" name="password"></field> - </object> - - <object model="orm.user" pk="9"> - <field type="TextField" name="name">David Reyna</field> - <field type="TextField" name="email">david.reyna@windriver.com</field> - <field type="TextField" name="role">Developer Wind River</field> - <field type="IntegerField" name="access">1</field> - <field type="TextField" name="password"></field> - </object> - <!-- Set up default users to investigations --> <object model="orm.investigationaccess" pk="1"> <field type="ForeignKey" name="investigation">1</field> - <field type="ForeignKey" name="user">1</field> + <field type="ForeignKey" name="user">10</field> </object> <object model="orm.investigationaccess" pk="2"> <field type="ForeignKey" name="investigation">1</field> - <field type="ForeignKey" name="user">2</field> + <field type="ForeignKey" name="user">11</field> </object> <object model="orm.investigationaccess" pk="3"> <field type="ForeignKey" name="investigation">1</field> - <field type="ForeignKey" name="user">3</field> + <field type="ForeignKey" name="user">12</field> </object> <object model="orm.vulnerabilityaccess" pk="4"> <field type="ForeignKey" name="vulnerability">1</field> - <field type="ForeignKey" name="user">2</field> + <field type="ForeignKey" name="user">10</field> </object> <object model="orm.vulnerabilityaccess" pk="5"> <field type="ForeignKey" name="vulnerability">1</field> - <field type="ForeignKey" name="user">4</field> + <field type="ForeignKey" name="user">11</field> </object> <object model="orm.investigationnotification" pk="1"> <field type="ForeignKey" name="investigation">1</field> - <field type="ForeignKey" name="user">5</field> + <field type="ForeignKey" name="user">12</field> </object> <object model="orm.vulnerabilitynotification" pk="2"> <field type="ForeignKey" name="vulnerability">1</field> - <field type="ForeignKey" name="user">6</field> + <field type="ForeignKey" name="user">10</field> </object> </django-objects> diff --git a/lib/orm/fixtures/yp.xml b/lib/orm/fixtures/yp.xml new file mode 100644 index 00000000..38e0f875 --- /dev/null +++ b/lib/orm/fixtures/yp.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="utf-8"?> +<django-objects version="1.0"> + + <!-- Set up test data for Products --> + + <object model="orm.product" pk="1"> + <field type="CharField" name="name">Yocto Project</field> + <field type="CharField" name="version">2.5 (Sumo)</field> + <field type="CharField" name="profile"></field> + </object> + <object model="orm.product" pk="2"> + <field type="CharField" name="name">Yocto Project</field> + <field type="CharField" name="version">2.4 (Rocko)</field> + <field type="CharField" name="profile"></field> + </object> + <object model="orm.product" pk="3"> + <field type="CharField" name="name">Yocto Project</field> + <field type="CharField" name="version">2.3 (Pyro)</field> + <field type="CharField" name="profile"></field> + </object> + + <!-- Set up default users --> + + <object model="orm.user" pk="10"> + <field type="TextField" name="name">Ross Burton</field> + <field type="TextField" name="email"></field> + <field type="TextField" name="role">Security Manager Yocto Project</field> + <field type="IntegerField" name="access">3</field> + <field type="TextField" name="password"></field> + </object> + + <object model="orm.user" pk="11"> + <field type="TextField" name="name">Richard Purtie</field> + <field type="TextField" name="email"></field> + <field type="TextField" name="role">Security Manager Yocto Project (backup)</field> + <field type="IntegerField" name="access">3</field> + <field type="TextField" name="password"></field> + </object> + + <object model="orm.user" pk="12"> + <field type="TextField" name="name">Mark Hatle</field> + <field type="TextField" name="email"></field> + <field type="TextField" name="role">Product Security Expert Wind River</field> + <field type="IntegerField" name="access">2</field> + <field type="TextField" name="password"></field> + </object> + + <object model="orm.user" pk="13"> + <field type="TextField" name="name">Stephen Jolley</field> + <field type="TextField" name="email"></field> + <field type="TextField" name="role">Product Owner Yocto Project</field> + <field type="IntegerField" name="access">1</field> + <field type="TextField" name="password"></field> + </object> + + <object model="orm.user" pk="14"> + <field type="TextField" name="name">David Reyna</field> + <field type="TextField" name="email">david.reyna@windriver.com</field> + <field type="TextField" name="role">Developer Wind River</field> + <field type="IntegerField" name="access">1</field> + <field type="TextField" name="password"></field> + </object> + +</django-objects> + diff --git a/lib/orm/management/commands/checksettings.py b/lib/orm/management/commands/checksettings.py index 65e9ab8a..e65d16ac 100644 --- a/lib/orm/management/commands/checksettings.py +++ b/lib/orm/management/commands/checksettings.py @@ -29,7 +29,7 @@ class Command(BaseCommand): def _verify_srt_source(self): ds_loaded = {} - + needs_import = False if 0 == DataSource.objects.all().count(): needs_import = True @@ -46,7 +46,7 @@ class Command(BaseCommand): print("Loading default settings") call_command("loaddata", "settings") - # Import the Common fixture if it's present + # Import the common fixture with warnings.catch_warnings(): warnings.filterwarnings( action="ignore", @@ -54,41 +54,57 @@ class Command(BaseCommand): print("Importing Common settings if present") try: call_command("loaddata", "common") - except: - print("NOTE: optional fixture 'common' not found") - - # Import the Mitre fixture if it's present - with warnings.catch_warnings(): - warnings.filterwarnings( - action="ignore", - message="^.*No fixture named.*$") - print("Importing Mitre settings if present") - try: - call_command("loaddata", "mitre") - except: - print("NOTE: optional fixture 'mitre' not found") + except Exception as e: + print("NOTE: optional fixture 'common' not found (%s)" % e) - # Import the NIST fixture if it's present + # Import the 'custom' fixture to allow local custom overrides with warnings.catch_warnings(): warnings.filterwarnings( action="ignore", message="^.*No fixture named.*$") - print("Importing NIST settings if present") - try: - call_command("loaddata", "nist") - except: - print("NOTE: optional fixture 'nist' not found") - - # Import the Sample_Test fixture if it's present - with warnings.catch_warnings(): - warnings.filterwarnings( - action="ignore", - message="^.*No fixture named.*$") - print("Importing Sample Test settings if present") + print("Importing Common settings if present") try: - call_command("loaddata", "samples") - except: - print("NOTE: optional fixture 'samples' not found") + call_command("loaddata", "custom") + except Exception as e: + print("NOTE: optional fixture 'custom' not found (%s)" % e) + + # Promote fallback values to missing configure defines + for setting in SrtSetting.objects.all(): + if '_fallback' in setting.name: + name = setting.name.replace('_fallback','') + s,create = SrtSetting.objects.get_or_create(name=key) + if create: + s.value = setting.value + s.save + + # Import the requested source fixture list + try: + fixture_list = SrtSetting.objects.get(name='SRTOOL_FIXTURE_LIST').value + except: + fixture_list = 'yp,nist' + for fixture in fixture_list.split(','): + fixture = fixture.strip() + with warnings.catch_warnings(): + warnings.filterwarnings( + action="ignore", + message="^.*No fixture named.*$") + print("Importing %s fixture if present" % fixture) + try: + call_command("loaddata", fixture) + except Exception as e: + print("NOTE: optional fixture '%s' not found (%s)" % (fixture,e)) + + # Import the Sample_Test fixture if it's requested and present + if self._test_settings_get('SRTDBG_SAMPLES'): + with warnings.catch_warnings(): + warnings.filterwarnings( + action="ignore", + message="^.*No fixture named.*$") + print("Importing Sample Test settings if present") + try: + call_command("loaddata", "samples") + except Exception as e: + print("NOTE: optional fixture 'samples' not found (%s)" % e) # restore data source loaded flags for source in DataSource.objects.all(): @@ -109,8 +125,20 @@ class Command(BaseCommand): traceback.print_exc() return 0 - - def _test_settings(self,key): + + def _test_settings_get(self,key): + try: + key_record = SrtSetting.objects.get(name=key) + if 'yes' == key_record.value: + return True + elif 'no' == key_record.value: + return False + else: + return key_record.value + except: + return False + + def _test_settings_set(self,key): key_record,create = SrtSetting.objects.get_or_create(name=key) if key in os.environ.keys(): key_record.value = 'yes' if os.environ[key].startswith('1') else 'no' @@ -127,12 +155,16 @@ class Command(BaseCommand): SrtSetting.objects.get_or_create(name='DEFAULT_RELEASE', value='') # TEST: set up the test flags based on ENVIRONMENT values - # * export TEST_SKIP_NIST_IMPORT=1: we already have NIST data in the DB - # * export TEST_SKIP_CPE_IMPORT=1: we do not need to (re-)scan the CPEs for vulnerable CVEs - # * export TEST_MINIMAL_DB=1: only load the minimal database items for fast GUI tests - self._test_settings('TEST_SKIP_NIST_IMPORT') - self._test_settings('TEST_SKIP_CPE_IMPORT') - self._test_settings('TEST_MINIMAL_DB') + # * export SRTDBG_SKIP_CVE_IMPORT=1: we do not need to (re-)scan the CVE data + # * export SRTDBG_SKIP_CPE_IMPORT=1: we do not need to (re-)scan the CPE data + # * export SRTDBG_SKIP_DEFECT_IMPORT=1: we do not need to (re-)scan the CPE data + # * export SRTDBG_MINIMAL_DB=1: only load the minimal database items for fast GUI tests + # * export SRTDBG_SAMPLES=1: load sample fixture (if any) database items for fast GUI tests + self._test_settings_set('SRTDBG_SKIP_CVE_IMPORT') + self._test_settings_set('SRTDBG_SKIP_CPE_IMPORT') + self._test_settings_set('SRTDBG_SKIP_DEFECT_IMPORT') + self._test_settings_set('SRTDBG_MINIMAL_DB') + self._test_settings_set('SRTDBG_SAMPLES') # TEMP: set up default user info current_user = SrtSetting.objects.get_or_create(name='current_user')[0] diff --git a/lib/orm/management/commands/lsupdates.py b/lib/orm/management/commands/lsupdates.py index 32548ddb..d72d732a 100644 --- a/lib/orm/management/commands/lsupdates.py +++ b/lib/orm/management/commands/lsupdates.py @@ -33,22 +33,21 @@ from orm.models import Keywords import os import sys import re +import subprocess +import time +from datetime import datetime, date import json import xml.etree.ElementTree as ET import csv import logging import threading -import time -logger = logging.getLogger("srt") import urllib +logger = logging.getLogger("srt") -def _log(msg): - f1=open('/tmp/srt.log', 'a') - f1.write("|" + msg + "|\n" ) - f1.close() - +# quick development/debugging support +from srtgui.api import _log # === Debugging limited database loading support === debug_cve_list = [] # empty list for any @@ -112,6 +111,27 @@ class Command(BaseCommand): sys.stdout.flush() + # Execute a shell script to import data, relative the SRT base + def execute_script(self,command): + SRT_BASE_DIR = os.environ.get('SRT_BASE_DIR') + CWD = os.getcwd() + os.chdir(SRT_BASE_DIR) + script = os.path.join(SRT_BASE_DIR,command) + print("====vvv Executing script '%s' vvv====" % script) + os.system(script) + os.chdir(CWD) + print("====^^^ Script Done ^^^====") + + # Mark database as loaded + def source_loaded(self,id,update_modified=True): + # Re-fetch record in case external script updates + updated_source=DataSource.objects.get(id=id) + updated_source.loaded = True + if update_modified: + updated_source.update_time = datetime.today().strftime('%Y-%m-%d %H:%M:%S') + updated_source.lastModifiedDate = updated_source.update_time + updated_source.save() + def nist_scan_configuration_or(self, cve, cpe_or_node, name, and_enum): cpe_list = '<or>|' for cpe in cpe_or_node['cpe']: @@ -128,207 +148,6 @@ class Command(BaseCommand): cpe_list += '</or>|' return cpe_list - def nist_jason(self, dct): - CVE_Items = dct['CVE_Items'] - total = len(CVE_Items) - for i, CVE_Item in enumerate(CVE_Items): - cve = CVE_Item['cve'] - references = cve['references']['reference_data'] - CVE_data_meta = cve['CVE_data_meta']['ID'] - - # DEBUGGING SUPPORT !!! TODO - scan = True - status = Cve.NOT_VULNERABLE - if (Command.debug_cve_count or len(debug_cve_list)) and not CVE_data_meta.startswith(Command.debug_include_id_prefix): - scan = False - if Command.debug_cve_count: - if i < Command.debug_cve_count: - scan = True - if len(debug_cve_list): - for debug_cve in debug_cve_list: - if cve['CVE_data_meta']['ID'].startswith(debug_cve): - scan = True - status = Cve.INVESTIGATE - if not scan: - continue - - if False: - print(" publishedDate: %s" % CVE_Item['publishedDate']) - print(" lastModifiedDate: %s" % CVE_Item['lastModifiedDate']) - - print(" publishedDate: %s" % re.sub('T.*','',CVE_Item['publishedDate'])) - print(" lastModifiedDate: %s" % re.sub('T.*','',CVE_Item['lastModifiedDate'])) - - print(" data_type: %s" % cve['data_type']) - print(" data_format: %s" % cve['data_format']) - print(" CVE_data_meta: %s" % cve['CVE_data_meta']['ID']) - print(" problemtype: %s" % cve['problemtype']['problemtype_data'][0]['description'][0]['value']) - print(" description: '%s'" % cve['description']['description_data'][0]['value']) - references = cve['references']['reference_data'] - print(" References = %d" % len(references)) - for ref in references: - print(" reference: %s" % ref['url']) - if CVE_Item['impact'] and CVE_Item['impact']['baseMetricV3']: - baseMetricV3 = CVE_Item['impact']['baseMetricV3'] - print(" cvssV3 : %s,%s" % (baseMetricV3['exploitabilityScore'],baseMetricV3['impactScore'])) - print(" vectorString = %s" % baseMetricV3['cvssV3']['vectorString']) - print(" attackVector = %s" % baseMetricV3['cvssV3']['attackVector']) - print(" attackComplexity = %s" % baseMetricV3['cvssV3']['attackComplexity']) - print(" privilegesRequired = %s" % baseMetricV3['cvssV3']['privilegesRequired']) - print(" userInteraction = %s" % baseMetricV3['cvssV3']['userInteraction']) - print(" scope = %s" % baseMetricV3['cvssV3']['scope']) - print(" confidentialityImpact = %s" % baseMetricV3['cvssV3']['confidentialityImpact']) - print(" integrityImpact = %s" % baseMetricV3['cvssV3']['integrityImpact']) - print(" availabilityImpact = %s" % baseMetricV3['cvssV3']['availabilityImpact']) - print(" baseScore = %s" % baseMetricV3['cvssV3']['baseScore']) - print(" baseSeverity = %s" % baseMetricV3['cvssV3']['baseSeverity']) - if CVE_Item['impact'] and CVE_Item['impact']['baseMetricV2']: - baseMetricV2 = CVE_Item['impact']['baseMetricV2'] - print(" cvssV2 : %s,%s" % (baseMetricV2['exploitabilityScore'],baseMetricV2['exploitabilityScore'])) - print(" vectorString = %s" % baseMetricV2['cvssV2']['vectorString']) - print(" accessVector = %s" % baseMetricV2['cvssV2']['accessVector']) - print(" accessComplexity = %s" % baseMetricV2['cvssV2']['accessComplexity']) - print(" authentication = %s" % baseMetricV2['cvssV2']['authentication']) - print(" confidentialityImpact = %s" % baseMetricV2['cvssV2']['confidentialityImpact']) - print(" integrityImpact = %s" % baseMetricV2['cvssV2']['integrityImpact']) - print(" availabilityImpact = %s" % baseMetricV2['cvssV2']['availabilityImpact']) - print(" baseScore = %s" % baseMetricV2['cvssV2']['baseScore']) - print(" severity = %s" % baseMetricV2['severity']) - - try: - CVE_data_meta = cve['CVE_data_meta']['ID'] - v, created = Cve.objects.get_or_create(name=CVE_data_meta) - - v.name = CVE_data_meta - v.source = 'NIST' - status = Cve.NOT_VULNERABLE - - # Debugging support -# if v.name.startswith("CVE-2018"): -# status = Cve.NEW -# v.status = status - v.tags = '' - v.tags_private = '' - - v.cve_data_type = cve['data_type'] - v.cve_data_format = cve['data_format'] - v.cve_data_version = cve['data_version'] - - v.description = cve['description']['description_data'][0]['value'] - v.publishedDate = re.sub('T.*','',CVE_Item['publishedDate']) - v.lastModifiedDate = re.sub('T.*','',CVE_Item['lastModifiedDate']) - - v.public = True - v.publish = Cve.PUBLISH_PUBLISHED - v.publish_date = v.publishedDate - - #v.problemtype = cve['problemtype']['problemtype_data'][0]['description'][0]['value'] - problem_list = cve['problemtype']['problemtype_data'] - CveToCwe.objects.filter(cve=v).delete() - for problem_Item in problem_list: - description_list = problem_Item['description'] - for description_Item in description_list: - value = description_Item['value'] - cwe, created = CweTable.objects.get_or_create(name=value) - if created: - print("WARNING Missing CWE = '%s'"% value) - cwe.save() - cve2cwe, created = CveToCwe.objects.get_or_create(cve=v,cwe=cwe) - if created: - cve2cwe.save() - -# if CVE_Item['impact'] and CVE_Item['impact']['baseMetricV3']: - if ('impact' in CVE_Item) and ('baseMetricV3' in CVE_Item['impact']): - baseMetricV3 = CVE_Item['impact']['baseMetricV3'] - v.cvssV3_baseScore = baseMetricV3['cvssV3']['baseScore'] - v.cvssV3_baseSeverity = baseMetricV3['cvssV3']['baseSeverity'] - v.cvssV3_vectorString = baseMetricV3['cvssV3']['vectorString'] - v.cvssV3_exploitabilityScore = baseMetricV3['exploitabilityScore'] - v.cvssV3_impactScore = baseMetricV3['impactScore'] - v.cvssV3_attackVector = baseMetricV3['cvssV3']['attackVector'] - v.cvssV3_attackComplexity = baseMetricV3['cvssV3']['attackComplexity'] - v.cvssV3_privilegesRequired = baseMetricV3['cvssV3']['privilegesRequired'] - v.cvssV3_userInteraction = baseMetricV3['cvssV3']['userInteraction'] - v.cvssV3_scope = baseMetricV3['cvssV3']['scope'] - v.cvssV3_confidentialityImpact = baseMetricV3['cvssV3']['confidentialityImpact'] - v.cvssV3_integrityImpact = baseMetricV3['cvssV3']['integrityImpact'] - v.cvssV3_availabilityImpact = baseMetricV3['cvssV3']['availabilityImpact'] - if ('impact' in CVE_Item) and ('baseMetricV2' in CVE_Item['impact']): - baseMetricV2 = CVE_Item['impact']['baseMetricV2'] - v.cvssV2_baseScore = baseMetricV2['cvssV2']['baseScore'] - v.cvssV2_severity = baseMetricV2['severity'] - v.cvssV2_vectorString = baseMetricV2['cvssV2']['vectorString'] - v.cvssV2_exploitabilityScore = baseMetricV2['exploitabilityScore'] - v.cvssV2_impactScore = baseMetricV2['exploitabilityScore'] - v.cvssV2_accessVector = baseMetricV2['cvssV2']['accessVector'] - v.cvssV2_accessComplexity = baseMetricV2['cvssV2']['accessComplexity'] - v.cvssV2_authentication = baseMetricV2['cvssV2']['authentication'] - v.cvssV2_confidentialityImpact = baseMetricV2['cvssV2']['confidentialityImpact'] - v.cvssV2_integrityImpact = baseMetricV2['cvssV2']['integrityImpact'] - -## v.save() - - CveReference.objects.filter(cve=v).delete() - for ref in references: - r, created = CveReference.objects.get_or_create(cve=v, - hyperlink=ref['url']) - r.resource = '' - r.type = '' - r.source = '' - r.name = '' - r.save() -# print(" reference: %s,%s" % (ref['url'],created)) - - - configurations = CVE_Item['configurations'] - v.cpe_list = '' - is_first_and = True - for i, config in enumerate(configurations['nodes']): - v.cpe_list += '<config>|' - v.cpe_list += '<and>|' - if "AND" == config['operator']: - # create AND record - if not is_first_and: - v.cpe_list += '</and>|' - v.cpe_list += '<and>|' - for j, cpe_or_node in enumerate(config['children']): - if "OR" == cpe_or_node['operator']: - v.cpe_list += self.nist_scan_configuration_or(v,cpe_or_node, CVE_data_meta, j) - else: - print("ERROR CONFIGURE:OR_OP?:%s" % cpe_or_node['operator']) - elif "OR" == config['operator']: - v.cpe_list += self.nist_scan_configuration_or(v,config, CVE_data_meta, 0) - else: - print("ERROR CONFIGURE:OP?:%s" % config_rec['operator']) - v.cpe_list += '</and>|' - v.cpe_list += '</config>|' - -# # create a parent CveSet if needed -# cve_set, created = CveSet.objects.get_or_create(name=v.name) -# if created: -# cve_set.name = v.name -# cve_set.description = v.description -# cve_set.cvssV3_baseScore = v.cvssV3_baseScore -# cve_set.cvssV3_baseSeverity = v.cvssV3_baseSeverity -# cve_set.publishedDate = v.publishedDate -# cve_set.lastModifiedDate = v.lastModifiedDate -# cve_set.save() -# -# # connect the Cve with the CveSet -# cve2cveset, created = CveToCveSet.objects.get_or_create(cve=v, cveset=cve_set) -# cve2cveset.save() - - # save the final CVE content - v.recommend = v.recommendation() - v.save() - - except Exception as e: - logger.warning("Failed saving CVE %s (%s)", (cve['CVE_data_meta']['ID'],e)) - return - - self.mini_progress("CVE's", i, total) - - def nist_cwe(self, content): # <td nowrap><span id="cweIdEntry-CWE-123">CWE-123</span></td> # <td nowrap><a href="http://cwe.mitre.org/data/definitions/123.html" target="_blank"> Write-what-where Condition</a></td> @@ -473,7 +292,8 @@ class Command(BaseCommand): # print("[%d]cpe23Uri=%s,%s" % (i,company,product)) - def cve_keywords_old(self, csvfile_name): + # Obsolete (slow) table-based keyword management + def cve_keywords_table(self, csvfile_name): # mode,type,keyword,weight # y,key,abiword, @@ -566,26 +386,6 @@ class Command(BaseCommand): setting = SrtSetting.objects.get_or_create(name='keywords_against')[0] setting.value = keywords_against[1:] setting.save() - - S = SrtSetting.objects.get(name='keywords_for') - #print("FOO_FOR:[%s]='%s'" % (S.name,S.value[0:30])) - S = SrtSetting.objects.get(name='keywords_against') - #print("FOO_NOT:[%s]='%s'" % (S.name,S.value[0:30])) - - - def debug_set_cve(self,key,public,vulnerability,comments,comments_private): - try: - c = Cve.objects.get(name=key) - c.public = public - c.comments = comments - c.comments_private = comments_private - c.save() - if vulnerability: - v = Vulnerability.objects.get(name=vulnerability) - cv, created = CveToVulnerablility.objects.get_or_create(vulnerability=v,cve=c) - cv.save() - except ObjectDoesNotExist: - print("Cve %s not found" % key) def update(self): """ @@ -607,11 +407,11 @@ class Command(BaseCommand): if source.loaded: logger.info("Skipping source data from %s",source.file_path) - print("Skipping CVE data for %s (already loaded)" % (source.description)) + print("Skipping CVE data from %s (already loaded)" % (source.description)) continue else: - logger.info("Fetching source data from %s",source.file_path) - print("Fetching source data for '%s'" % (source.description)) + logger.info("Fetching source data from %s:%s" % (source.source,source.file_path)) + print("Fetching source data from '%s:%s:%s'" % (source.source,source.description,source.file_path)) file_path = source.file_path url_path = source.url @@ -619,14 +419,17 @@ class Command(BaseCommand): root = None content = None source_doc = None - # prefer the file path before the url - if file_path: + # precedence: (1) script, (2) local file path, (3) url + if 'script' == source.type: + if not source.command: + # no script to run + logger.error("Data source is script but no script provided '%s'" % (source.description)) + continue + elif file_path: # load the file source_doc = source.file_path if not source.file_path.startswith('/'): file_path = os.path.join(os.getenv('SRT_BASE_DIR'), source.file_path) - source.loaded = True - source.save() if not os.path.isfile(file_path): logger.error("Data file not found '%s'" % (file_path)) continue @@ -643,7 +446,7 @@ class Command(BaseCommand): content = text_data.read() elif url_path: # load the HTML page - source_doc = source.url + href = source.url try: f = urlopen(href) content = f.read().decode('UTF-8') @@ -653,66 +456,64 @@ class Command(BaseCommand): continue else: # no data source path to load - logger.error("Unknown data source path for '%s' (%s,%s) " % (source.source.description,source.file_path,source.url)) + logger.error("Unknown data source path for '%s' (%s,%s,%s) " % (source.description,source.type,source.file_path,source.url)) continue # testing shortcut - if ('nist' == source.source) and ('yes' == SrtSetting.objects.get(name='TEST_SKIP_NIST_IMPORT').value): + if ('cve' == source.data) and ('yes' == SrtSetting.objects.get(name='SRTDBG_SKIP_CVE_IMPORT').value): + continue + if ('cpe' == source.data) and ('yes' == SrtSetting.objects.get(name='SRTDBG_SKIP_CPE_IMPORT').value): + continue + if ('defect' == source.data) and ('yes' == SrtSetting.objects.get(name='SRTDBG_SKIP_DEFECT_IMPORT').value): continue - + + # Script-based update? + if 'script' == source.type and source.command: + # do not perform backups for brand new databases + if source.data in ['backup_weekly','backup_daily']: + pass + else: + self.execute_script(source.command) + self.source_loaded(source.id,False) + continue + # Common data sources if 'common' == source.source: if 'triage_keywords' == source.data: self.cve_keywords(csvfile_name) - source.loaded = True - source.save() + self.source_loaded(source.id) continue # Common Vulnerabilities and Exposures - if 'cve' == source.data: - if 'nist' == source.source and 'json' == source.type: - self.nist_jason(dct) - source.loaded = True - source.save() - continue + # Handled by "srtool_cve.py" + # Common Weakness Enumeration if 'cwe' == source.data: if 'nist' == source.source and 'html' == source.type: self.nist_cwe(content) - source.loaded = True - source.save() + self.source_loaded(source.id) continue # Common Product Enumeration if 'cpe' == source.data: if 'nist' == source.source and 'xml' == source.type: self.nist_cpe(root) - source.loaded = True - source.save() + self.source_loaded(source.id) continue if 'nist' == source.source and 'csv' == source.type: self.nist_cpe_csv(csvfile_name) - source.loaded = True - source.save() + self.source_loaded(source.id) continue - # data source not handled logger.error("Unknown data source type for '%s' (%s,%s,%s) " % (source.file_path,source.data,source.source,source.type)) - # TEST DEBUG -# self.debug_set_cve('CVE-2017-0002',False,'','','') -# self.debug_set_cve('CVE-2017-0010',False,'','','') - self.debug_set_cve('CVE-2017-5753',True,'V0000','Spectre Variant 1: Bounds check bypass','community calling this "spectre"') - self.debug_set_cve('CVE-2017-5715',True,'V0000','Spectre Variant 2: Branch target injection','see if this will required compiler changes') - self.debug_set_cve('CVE-2017-5754',True,'V0000','Meltdown: Rogue data cache load, memory access permission check performed after kernel memory read','mostly for Intel parts') - os.system('setterm -cursor on') def handle(self, **options): # testing shortcuts - if 'yes' == SrtSetting.objects.get(name='TEST_MINIMAL_DB').value: + if 'yes' == SrtSetting.objects.get(name='SRTDBG_MINIMAL_DB').value: print("TEST: MINIMAL DATABASE LOADING") Command.debug_cve_count = 10 # 0 for any Command.debug_include_id_prefix = 'XXX' # always include CVEs with this prefix diff --git a/lib/orm/migrations/0001_initial.py b/lib/orm/migrations/0001_initial.py index 6d8b9472..764b27df 100644 --- a/lib/orm/migrations/0001_initial.py +++ b/lib/orm/migrations/0001_initial.py @@ -42,6 +42,11 @@ class Migration(migrations.Migration): ('file_path', models.FilePathField()), ('url', models.TextField(blank=True)), ('loaded', models.BooleanField(default=False)), + ('meta_url', models.TextField(blank=True)), + ('lastModifiedDate', models.CharField(max_length=50, blank=True)), + ('update_frequency', models.IntegerField(default=2)), + ('update_time', models.CharField(max_length=50, blank=True)), + ('command', models.TextField(blank=True)), ], ), @@ -58,14 +63,17 @@ class Migration(migrations.Migration): ('found', models.BooleanField(default=False)), ], ), + migrations.CreateModel( name='Cve', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('name', models.CharField(max_length=50)), - ('source', models.CharField(max_length=50)), + ('datasource', models.ForeignKey(default=None, to='orm.datasource',null=True)), + ('source', models.CharField(max_length=50)), + ('priority', models.IntegerField(default=0)), ('status', models.IntegerField(default=0)), ('comments', models.TextField(blank=True)), ('comments_private', models.TextField(blank=True)), @@ -75,44 +83,21 @@ class Migration(migrations.Migration): ('cve_data_version', models.CharField(max_length=50, blank=True)), ('public', models.BooleanField(default=False)), - ('publish', models.IntegerField(default=0)), + ('publish_state', models.IntegerField(default=0)), ('publish_date', models.CharField(max_length=50, blank=True)), ('description', models.TextField(blank=True)), ('publishedDate', models.CharField(max_length=50, blank=True)), ('lastModifiedDate', models.CharField(max_length=50, blank=True)), -# ('problemtype', models.CharField(max_length=40, blank=True)), -# ('problemtype', models.ForeignKey(CweTable, related_name='cwe')), - ('recommend', models.IntegerField(default=0)), - - ('cpe_list', models.TextField(blank=True)), + ('recommend_list', models.TextField(blank=True)), ('cvssV3_baseScore', models.CharField(max_length=50, blank=True)), ('cvssV3_baseSeverity', models.CharField(max_length=50, blank=True)), - ('cvssV3_vectorString', models.TextField(blank=True)), - ('cvssV3_exploitabilityScore', models.CharField(max_length=50, blank=True)), - ('cvssV3_impactScore', models.CharField(max_length=50, blank=True)), - ('cvssV3_attackVector', models.CharField(max_length=5, blank=True)), - ('cvssV3_attackComplexity', models.CharField(max_length=50, blank=True)), - ('cvssV3_privilegesRequired', models.CharField(max_length=50, blank=True)), - ('cvssV3_userInteraction', models.CharField(max_length=50, blank=True)), - ('cvssV3_scope', models.CharField(max_length=50, blank=True)), - ('cvssV3_confidentialityImpact', models.CharField(max_length=50, blank=True)), - ('cvssV3_integrityImpact', models.CharField(max_length=50, blank=True)), - ('cvssV3_availabilityImpact', models.CharField(max_length=50, blank=True)), ('cvssV2_baseScore',models.CharField(max_length=50, blank=True)), ('cvssV2_severity', models.CharField(max_length=50, blank=True)), - ('cvssV2_vectorString', models.TextField(blank=True)), - ('cvssV2_exploitabilityScore', models.CharField(max_length=50, blank=True)), - ('cvssV2_impactScore', models.CharField(max_length=50, blank=True)), - ('cvssV2_accessVector', models.CharField(max_length=50, blank=True)), - ('cvssV2_accessComplexity', models.CharField(max_length=50, blank=True)), - ('cvssV2_authentication', models.CharField(max_length=50, blank=True)), - ('cvssV2_confidentialityImpact', models.CharField(max_length=50, blank=True)), - ('cvssV2_integrityImpact', models.CharField(max_length=50, blank=True)), ], ), @@ -190,11 +175,11 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('name', models.CharField(max_length=50)), - ('description', models.TextField(blank=True)), - ('cve_primary_name', models.CharField(max_length=50)), + ('description', models.TextField(blank=True, default='')), + ('cve_primary_name', models.CharField(max_length=50, default='')), ('public', models.BooleanField(default=False)), - ('comments', models.TextField(blank=True)), - ('comments_private', models.TextField(blank=True)), + ('comments', models.TextField(blank=True, default='')), + ('comments_private', models.TextField(blank=True, default='')), ('status', models.IntegerField(default=0)), ('outcome', models.IntegerField(default=0)), ('severity', models.IntegerField(default=0)), @@ -251,10 +236,11 @@ class Migration(migrations.Migration): ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('name', models.CharField(max_length=50)), ('summary', models.TextField(blank=True)), + ('url', models.TextField(blank=True)), ('priority', models.IntegerField(default=0)), ('status', models.IntegerField(default=0)), ('resolution', models.IntegerField(default=0)), - ('publishOLS', models.TextField(blank=True)), + ('publish', models.TextField(blank=True)), ('release_version', models.CharField(max_length=50)), ('product', models.ForeignKey(default=None, to='orm.product', null=True)), ('date_created', models.CharField(max_length=50)), @@ -421,13 +407,15 @@ class Migration(migrations.Migration): ], ), -# migrations.AddField( -# model_name='project', -# name='release', -# field=models.ForeignKey(to='orm.Release', null=True), -# ), -# migrations.AlterUniqueTogether( -# name='layersource', -# unique_together=set([('sourcetype', 'apiurl')]), -# ), + migrations.CreateModel( + name='PublishPending', + fields=[ + ('cve', models.ForeignKey(default=None, to='orm.cve',blank=True,null=True)), + ('vulnerability', models.ForeignKey(default=None, to='orm.vulnerability',blank=True,null=True)), + ('investigation', models.ForeignKey(default=None, to='orm.investigation',blank=True,null=True)), + ('date', models.DateField(null=True, blank=True)), + ('note', models.TextField()), + ], + ), + ] diff --git a/lib/orm/models.py b/lib/orm/models.py index 3a4a39a7..0d287bb4 100644 --- a/lib/orm/models.py +++ b/lib/orm/models.py @@ -42,10 +42,8 @@ from signal import SIGUSR1 import logging logger = logging.getLogger("srt") -def _log(msg): - f1=open('/tmp/srt.log', 'a') - f1.write("|" + msg + "|\n" ) - f1.close() +# quick development/debugging support +from srtgui.api import _log # Sqlite support @@ -127,7 +125,6 @@ class SrtSetting(models.Model): def __unicode__(self): return "Setting %s = %s" % (self.name, self.value) - class HelpText(models.Model): VARIABLE = 0 HELPTEXT_AREA = ((VARIABLE, 'variable'), ) @@ -136,7 +133,25 @@ class HelpText(models.Model): key = models.CharField(max_length=100) text = models.TextField() + +#UPDATE_FREQUENCY: 0 = every minute, 1 = every hour, 2 = every day, 3 = every week, 4 = every month, 5 = every year class DataSource(models.Model): + #UPDATE FREQUENCT + MINUTELY = 0 + HOURLY = 1 + DAILY = 2 + WEEKLY = 3 + MONTHLY = 4 + ONDEMAND = 5 + FREQUENCY = ( + (MINUTELY, 'New'), + (HOURLY, 'Hourly'), + (DAILY, 'Daily'), + (WEEKLY, 'Weekly'), + (MONTHLY, 'Monthly'), + (ONDEMAND, 'OnDemand'), + ) + data = models.CharField(max_length=20) source = models.CharField(max_length=20) type = models.CharField(max_length=20) @@ -144,6 +159,15 @@ class DataSource(models.Model): file_path = models.FilePathField() url = models.TextField(blank=True) loaded = models.BooleanField(default=False) + meta_url = models.TextField(blank=True) + lastModifiedDate = models.CharField(max_length=50, blank=True) + update_frequency = models.IntegerField(choices=FREQUENCY, default=DAILY) + update_time = models.CharField(max_length=50, blank=True) + command = models.TextField(blank=True) + + + def get_frequency_text(self): + return DataSource.FREQUENCY[int(self.update_frequency)][1] class CweTable(models.Model): search_allowed_fields = ['name', 'href', 'description', 'summary'] @@ -155,9 +179,25 @@ class CweTable(models.Model): vulnerable_count = models.IntegerField(default=0) found = models.BooleanField(default=False) + class Cve(models.Model): search_allowed_fields = ['name', 'description', 'publishedDate', 'lastModifiedDate', 'comments', 'comments_private'] + + # SRTool Priority + UNDEFINED = 0 + MINOR = 1 + LOW = 2 + MEDIUM = 3 + HIGH = 4 + PRIORITY = ( + (UNDEFINED, ''), + (MINOR, 'Minor'), + (LOW, 'Low'), + (MEDIUM, 'Medium'), + (HIGH, 'High'), + ) + # WR Status NEW = 0 INVESTIGATE = 1 @@ -170,18 +210,20 @@ class Cve(models.Model): (NOT_VULNERABLE, 'Not Vulnerable'), ) - # Publish options - PUBLISH_UNDEF = 0 - PUBLISH_AUTO = 1 - PUBLISH_REQUEST = 2 - PUBLISH_PUBLISHED = 3 - PUBLISH_NOPUBLISH = 4 - PUBLISH = ( - (PUBLISH_UNDEF, 'Undetermined'), - (PUBLISH_AUTO, 'Automatic Publish Date'), - (PUBLISH_REQUEST, 'Request Publish Date'), + # Publish state + PUBLISH_UNPUBLISHED = 0 + PUBLISH_NOPUBLISH = 1 + PUBLISH_PUBLISHED = 2 + PUBLISH_REQUEST = 3 + PUBLISH_UPDATE = 4 + PUBLISH_SUBMITTED = 5 + PUBLISH_STATE = ( + (PUBLISH_UNPUBLISHED, 'Unpublished'), + (PUBLISH_NOPUBLISH, 'Not to be Published'), (PUBLISH_PUBLISHED, 'Published'), - (PUBLISH_NOPUBLISH, 'Do Not Published'), + (PUBLISH_REQUEST, 'Publish Request (New)'), + (PUBLISH_UPDATE, 'Publish Request (Update)'), + (PUBLISH_SUBMITTED, 'Publish Submitted'), ) # CPE item list @@ -193,69 +235,44 @@ class Cve(models.Model): name = models.CharField(max_length=50) source = models.CharField(max_length=50) + datasource = models.ForeignKey(DataSource,related_name="cve_datasource") + priority = models.IntegerField(default=0) status = models.IntegerField(choices=STATUS, default=NEW) comments = models.TextField(blank=True) comments_private = models.TextField(blank=True) - + cve_data_type = models.CharField(max_length=100, blank=True) cve_data_format = models.CharField(max_length=50, blank=True) cve_data_version = models.CharField(max_length=50, blank=True) public = models.BooleanField(default=True) - publish = models.IntegerField(choices=PUBLISH, default=PUBLISH_UNDEF) + publish_state = models.IntegerField(choices=PUBLISH_STATE, default=PUBLISH_UNPUBLISHED) publish_date = models.CharField(max_length=50, blank=True) description = models.TextField(blank=True) publishedDate = models.CharField(max_length=50, blank=True) lastModifiedDate = models.CharField(max_length=50, blank=True) -# problemtype = models.CharField(max_length=40, blank=True) -# problemtype = models.ForeignKey(CweTable, related_name='cwe') recommend = models.IntegerField(default=0) - - cpe_list= models.TextField(blank=True) + recommend_list = models.TextField(blank=True) cvssV3_baseScore = models.CharField(max_length=50, blank=True) cvssV3_baseSeverity = models.CharField(max_length=50, blank=True) - cvssV3_vectorString = models.TextField(blank=True) - cvssV3_exploitabilityScore = models.CharField(max_length=50, blank=True) - cvssV3_impactScore = models.CharField(max_length=50, blank=True) - cvssV3_attackVector = models.CharField(max_length=50, blank=True) - cvssV3_attackComplexity = models.CharField(max_length=50, blank=True) - cvssV3_privilegesRequired = models.CharField(max_length=50, blank=True) - cvssV3_userInteraction = models.CharField(max_length=50, blank=True) - cvssV3_scope = models.CharField(max_length=50, blank=True) - cvssV3_confidentialityImpact = models.CharField(max_length=50, blank=True) - cvssV3_integrityImpact = models.CharField(max_length=50, blank=True) - cvssV3_availabilityImpact = models.CharField(max_length=50, blank=True) cvssV2_baseScore = models.CharField(max_length=50, blank=True) cvssV2_severity = models.CharField(max_length=50, blank=True) - cvssV2_vectorString = models.TextField(blank=True) - cvssV2_exploitabilityScore = models.CharField(max_length=50, blank=True) - cvssV2_impactScore = models.CharField(max_length=50, blank=True) - cvssV2_accessVector = models.CharField(max_length=50, blank=True) - cvssV2_accessComplexity = models.CharField(max_length=50, blank=True) - cvssV2_authentication = models.CharField(max_length=50, blank=True) - cvssV2_confidentialityImpact = models.CharField(max_length=50, blank=True) - cvssV2_integrityImpact = models.CharField(max_length=50, blank=True) class Meta: unique_together = ('name', 'source' ) @property - def problemtype_summary(self): - """ Return the summary of the CWE """ - summary = '?' - try: - r = CweTable.objects.get(name=self.problemtype) - summary = r.summary - except Exception as e: - logger.warning("ERROR: could not find CWE %s" % self.problemtype) - return summary + def get_priority_text(self): + return Cve.PRIORITY[int(self.priority)][1] + @property def get_publish_text(self): - return Cve.PUBLISH[int(self.publish)][1] + return Cve.PUBLISH_STATE[int(self.publish_state)][1] + @property def get_status_text(self): return Cve.STATUS[int(self.status)][1] def get_cpe_list(self): @@ -264,31 +281,54 @@ class Cve(models.Model): cpe_array.append(cpe.split(',')) return cpe_array - FOR_LIST = ['linux','openjpeg','libtiff','libav','tcp','binutil ','ssl','ssh','glibc'] - AGAINST_LIST = ['cisco','microsoft','windows','ibm','oracle','sun','java','peoplesoft','hancom','zyxel','wordpress','sugarcrm','cobham', - 'juniper'] - - def recommendation(self): - recommendation = 0 - for s in Cve.FOR_LIST: - if 0 <= self.description.lower().find(s): - recommendation += 1 - for s in Cve.AGAINST_LIST: - if 0 <= self.description.lower().find(s): - recommendation -= 1 - return recommendation - def reasons_for(self): - reason = '' - for s in Cve.FOR_LIST: - if 0 <= self.description.lower().find(s): - reason += '%s ' % s - return reason - def reasons_against(self): - reason = '' - for s in Cve.AGAINST_LIST: - if 0 <= self.description.lower().find(s): - reason += '%s ' % s - return reason + +class CveDetail(): + # CPE item list + CPE_LIST_KEY = 0 # entry is <[/]component|and|or> tag or '|' delimited list + CPE_LIST_VULNERABLE = 0 + CPE_LIST_CPE23 = 1 + CPE_LIST_CPE22 = 2 + CPE_LIST_VERSIONEND = 3 + + name = '' +# problemtype = '' + + recommend = '' + recommend_list = '' + + cpe_list= '' + + cvssV3_baseScore = '' + cvssV3_baseSeverity = '' + cvssV3_vectorString = '' + cvssV3_exploitabilityScore = '' + cvssV3_impactScore = '' + cvssV3_attackVector = '' + cvssV3_attackComplexity = '' + cvssV3_privilegesRequired = '' + cvssV3_userInteraction = '' + cvssV3_scope = '' + cvssV3_confidentialityImpact = '' + cvssV3_integrityImpact = '' + cvssV3_availabilityImpact = '' + + cvssV2_baseScore = '' + cvssV2_severity = '' + cvssV2_vectorString = '' + cvssV2_exploitabilityScore = '' + cvssV2_impactScore = '' + cvssV2_accessVector = '' + cvssV2_accessComplexity = '' + cvssV2_authentication = '' + cvssV2_confidentialityImpact = '' + cvssV2_integrityImpact = '' + + def get_cpe_list(self): + cpe_array = [] + for cpe in self.cpe_list.split('|'): + cpe_array.append(cpe.split(',')) + return cpe_array + # Same CVE from multiple sources and/or revisions class CveSet(models.Model): @@ -319,7 +359,7 @@ class CpeTable(models.Model): cpeMatchString = models.TextField(blank=True) cpe23Uri = models.TextField(blank=True) versionEndIncluding = models.TextField(blank=True) - + class CpeToCve(models.Model): cpe = models.ForeignKey(CpeTable,related_name="cpe2cve") cve = models.ForeignKey(Cve,related_name="cve2cpe") @@ -353,7 +393,7 @@ class CpeFilter(models.Model): @property def get_status_text(self): return CpeFilter.STATUS[int(self.status)][1] - + # CVE/CWE Mapping @@ -373,6 +413,8 @@ class CveReference(models.Model): # PRODUCT class Product(models.Model): + search_allowed_fields = ['name', 'version', 'profile'] + name = models.CharField(max_length=40) version = models.CharField(max_length=40) profile = models.CharField(max_length=40) @@ -382,13 +424,14 @@ class Product(models.Model): class Meta: unique_together = ('name', 'version', 'profile', ) + @property def long_name(self): return '%s %s %s' % (self.name,self.version,self.profile) # VULNERABILITY -# Company-level Vulnerablility Record +# Company-level Vulnerablility Record class Vulnerability(models.Model): search_allowed_fields = ['name', 'comments', 'comments_private'] @@ -410,22 +453,27 @@ class Vulnerability(models.Model): (FIXED, 'Closed (Fixed)'), (NOT_FIX, "Closed (Won't Fix)"), ) - LOW = 0 - MEDIUM = 1 - HIGH = 2 + # SRTool Severity, matched with Cve/Defect Priority with placeholder for 'minor' + UNDEFINED = 0 + MINOR = 1 + LOW = 2 + MEDIUM = 3 + HIGH = 4 SEVERITY = ( + (UNDEFINED, ''), + (MINOR, 'Minor'), (LOW, 'Low'), (MEDIUM, 'Medium'), (HIGH, 'High'), ) name = models.CharField(max_length=50) - cve_primary_name = models.CharField(max_length=50) - description = models.TextField(blank=True) + cve_primary_name = models.CharField(max_length=50, default='') + description = models.TextField(blank=True, default='') - public = models.BooleanField(default=False) - comments = models.TextField(blank=True) - comments_private = models.TextField(blank=True) + public = models.BooleanField(default=True) + comments = models.TextField(blank=True, default='') + comments_private = models.TextField(blank=True, default='') status = models.IntegerField(choices=STATUS, default=INVESTIGATE) outcome = models.IntegerField(choices=OUTCOME, default=OPEN) @@ -485,7 +533,7 @@ class Vulnerability(models.Model): except IntegrityError: print("Error in new_vulnerability_name") raise - return "V%04d" % index + return "V%05d" % index class VulnerabilityComments(models.Model): vulnerability = models.ForeignKey(Vulnerability,related_name="vulnerability_comments") @@ -513,7 +561,7 @@ class CveToVulnerablility(models.Model): # Defects -# Defect Record +# Defect Record class Defect(models.Model): search_allowed_fields = ['name', 'summary', 'release_version'] #, 'product'] @@ -521,11 +569,13 @@ class Defect(models.Model): #Issue Type,Key,Summary,Priority,Status,Resolution,Publish To OLS,Fix Version #Bug,LIN10-2031,Security Advisory - libvorbis - CVE-2017-14633,P3,Closed,Fixed,Reviewed - Publish,10.17.41.3 - MINOR = 0 - LOW = 1 - MEDIUM = 2 - HIGH = 3 + NONE = 0 + MINOR = 1 + LOW = 2 + MEDIUM = 3 + HIGH = 4 Priority = ( + (NONE, 'None'), (MINOR, 'P4'), (LOW, 'P3'), (MEDIUM, 'P2'), @@ -546,35 +596,49 @@ class Defect(models.Model): (CLOSED, 'Closed'), ) UNRESOLVED = 0 - FIXED = 1 - WILL_NOT_FIX = 2 - WITHDRAWN = 3 - REJECTED = 4 - DUPLICATE = 5 + RESOLVED = 1 + FIXED = 2 + WILL_NOT_FIX = 3 + WITHDRAWN = 4 + REJECTED = 5 + DUPLICATE = 6 + NOT_APPLICABLE = 7 + REPLACED_BY_REQUIREMENT = 8 + CANNOT_REPRODUCE = 9 + DONE = 10 Resolution = ( (UNRESOLVED, 'Unresolved'), + (RESOLVED, 'Resolved'), (FIXED, 'Fixed'), (WILL_NOT_FIX, 'Won\'t Fix'), (WITHDRAWN, 'Withdrawn'), (REJECTED, 'Rejected'), (DUPLICATE, 'Duplicate'), + (NOT_APPLICABLE, 'Not Applicable'), + (REPLACED_BY_REQUIREMENT, 'Replaced By Requirement'), + (CANNOT_REPRODUCE, 'Cannot Reproduce'), + (DONE, 'Done'), ) name = models.CharField(max_length=50) summary = models.TextField(blank=True) + url = models.TextField(blank=True) priority = models.IntegerField(choices=Priority, default=MINOR) status = models.IntegerField(choices=Status, default=OPEN) resolution = models.IntegerField(choices=Resolution, default=UNRESOLVED) - publishOLS = models.TextField(blank=True) + publish = models.TextField(blank=True) release_version = models.CharField(max_length=50) product = models.ForeignKey(Product,related_name="product_defect") date_created = models.CharField(max_length=50) date_updated = models.CharField(max_length=50) # Methods + @property def get_priority_text(self): return Defect.Priority[int(self.priority)][1] + @property def get_status_text(self): return Defect.Status[int(self.status)][1] + @property def get_resolution_text(self): return Defect.Resolution[int(self.resolution)][1] def get_long_name(self): @@ -585,7 +649,7 @@ class Defect(models.Model): # INVESTIGATION -# Product-level Vulnerablility Investigation Record +# Product-level Vulnerablility Investigation Record class Investigation(models.Model): search_allowed_fields = ['name', 'comments', 'comments_private'] @@ -607,10 +671,15 @@ class Investigation(models.Model): (FIXED, 'Fixed'), (NOT_FIX, "Won't Fix"), ) - LOW = 0 - MEDIUM = 1 - HIGH = 2 + # SRTool Severity, matched with Cve/Defect Priority with placeholder for 'minor' + UNDEFINED = 0 + MINOR = 1 + LOW = 2 + MEDIUM = 3 + HIGH = 4 SEVERITY = ( + (UNDEFINED, ''), + (MINOR, 'Minor'), (LOW, 'Low'), (MEDIUM, 'Medium'), (HIGH, 'High'), @@ -650,7 +719,7 @@ class Investigation(models.Model): index = int(current_investigation_index.value) + 1 current_investigation_index.value = str(index) current_investigation_index.save() - return "I%04d" % index + return "I%05d" % index class InvestigationToDefect(models.Model): investigation = models.ForeignKey(Investigation,related_name="investigation_to_defect") @@ -700,7 +769,7 @@ class User(models.Model): # the default guest user account is ID=1 USER_GUEST = 1 - # Access model + # Access model READER = 0 CONTRIBUTOR = 1 CREATOR = 2 @@ -720,6 +789,32 @@ class User(models.Model): def get_access_text(self): return User.ACCESS[int(self.access)][1] + @property + def builtin(self): + return( ('Guest' == self.name) or ('SRTool' == self.name)) + +# Minimal and safe User object to pass to web pages (no passwords) +class UserSafe(): + def __init__(self, pk, name, email): + self.pk = pk + self.name = name + self.email = email + + def __str__(self): + return "UserSafeStr=%d,%s,%s" % (self.pk, self.name, self.email) + + @staticmethod + def get_safe_userlist(allow_assignment=True): + user_list = [] + for user in User.objects.all(): + if 'SRTool' == user.name: + continue + if allow_assignment and ('Guest' == user.name): + continue + u = UserSafe(user.id,user.name,user.email) + user_list.append(u) + return user_list + class VulnerabilityAccess(models.Model): vulnerability = models.ForeignKey(Vulnerability,related_name="vulnerability_users") user = models.ForeignKey(User,related_name="vulnerability_user") @@ -759,47 +854,59 @@ class Keywords(models.Model): def get_keytype_text(self): return Keywords.KeyType[int(self.key_type)][1] - - -# ==== +# Items waiting for SRTool external publishing +class PublishPending(models.Model): + cve = models.ForeignKey(Cve,related_name="publish_pending_cves",blank=True,null=True) + vulnerability = models.ForeignKey(Vulnerability,related_name="publish_pending_vulnerabilities",blank=True,null=True) + investigation = models.ForeignKey(Investigation,related_name="publish_pending_investigations",blank=True,null=True) + date = models.DateField(null=True, blank=True) + note = models.TextField(blank=True) + +# ==== Support clases, meta classes ==== + +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.iteritems(): + s += '(%s=%s),' % (key,value) + s += ')' + _log(s) class Access(): # default user is "Guest" - - def read_values(self): - v, created = SrtSetting.objects.get_or_create(name='current_user') - if created: - v.value = User.USER_GUEST - v.save() - self.current_user = int(v.value) + + def __init__(self, *args, **kwargs): + _log_args("ACCESS:", *args, **kwargs) + srt_user_id = args[0] + + # default to "Guest" + if 0 == srt_user_id: + srt_user_id = 1 + + self.current_user = srt_user_id try: - self.current_user_name = User.objects.get(pk=self.current_user).name + user = User.objects.get(pk=self.current_user) + self.current_user_name = user.name + self.current_user_access = user.access except: self.current_user_name = '<not_found>' - - v, created = SrtSetting.objects.get_or_create(name='current_user_access') - if created: - v.value = User.READER - v.save() - self.current_user_access = int(v.value) + self.current_user_access = User.READER def is_guest(self): - self.read_values() return self.current_user == User.USER_GUEST def is_reader(self): - self.read_values() return self.current_user_access >= User.READER def is_contributor(self): - self.read_values() return self.current_user_access >= User.CONTRIBUTOR def is_creator(self): - self.read_values() return self.current_user_access >= User.CREATOR def is_admin(self): - self.read_values() return self.current_user_access >= User.ADMIN def user_name(self): - self.read_values() return self.current_user_name # Database Cache Support diff --git a/lib/srtgui/api.py b/lib/srtgui/api.py index b2c15b97..68aedb5d 100644 --- a/lib/srtgui/api.py +++ b/lib/srtgui/api.py @@ -1,7 +1,7 @@ # # BitBake Toaster Implementation # -# Copyright (C) 2016 Intel Corporation +# Copyright (C) 2016-2018 Intel Corporation # # 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 @@ -24,13 +24,12 @@ import logging import json from collections import Counter -from orm.models import Vulnerability - from django.http import HttpResponse, JsonResponse from django.views.generic import View from django.core.urlresolvers import reverse from django.db.models import Q, F from django.db import Error + from srtgui.templatetags.projecttags import filtered_filesizeformat logger = logging.getLogger("srt") @@ -40,3 +39,144 @@ def error_response(error): return JsonResponse({"error": error}) +# quick development/debugging support +def _log(msg): + DBG_LVL = os.environ['SRTDBG_LVL'] if ('SRTDBG_LVL' in os.environ) else 2 + DBG_LOG = os.environ['SRTDBG_LOG'] if ('SRTDBG_LOG' in os.environ) else '/tmp/toaster.log' + if 1 == DBG_LVL: + print(msg) + elif 2 == DBG_LVL: + f1=open(DBG_LOG, 'a') + f1.write("|" + msg + "|\n" ) + f1.close() + + +## add os.makedirs(cache_path) just to be safe? +def readCveDetails(cve): + from orm.models import CveDetail, DataSource + cve_name = cve.name + cve_datasource = cve.datasource + + # Initialize and populate CveDetail object to return + v = CveDetail() + v.name = cve_name + + # Fetch cached data, else extract data from datasource file + cache_path = os.path.join(os.environ['SRT_BASE_DIR'], "data/cache") + cve_path = os.path.join(cache_path, cve_name + ".json") + + #check if in cache, and use if exists. Else fetch from appropriate CVE JSON feed file + if (os.path.isfile(cve_path)): + try: + f = open(cve_path, 'r') + CVE_Item = json.load(f) + except Exception as e: + _log("| error reading json file %s: %s |\n" % (cve_path,e,)) + return v + else: + try: + SRT_BASE_DIR= os.environ['SRT_BASE_DIR'] + f = open(os.path.join(SRT_BASE_DIR, cve_datasource.file_path), '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 + if (item['cve']['CVE_data_meta']['ID'] == cve_name): + CVE_Item = item + cve_cache_file = open(os.path.join(SRT_BASE_DIR, "data", "cache", cve_name + ".json"), "w+") #write the cve to json file in cache + cve_cache_file.write(json.dumps(CVE_Item)) + break + except Exception as e: + _log("| error reading json file: %s |\n" % os.path.join(SRT_BASE_DIR, cve_datasource.file_path) ) + return v + + #initialize and populate CveDetail object to return + v = CveDetail() + v.name = cve_name + + if ('impact' in CVE_Item) and ('baseMetricV3' in CVE_Item['impact']): + baseMetricV3 = CVE_Item['impact']['baseMetricV3'] + v.cvssV3_baseScore = baseMetricV3['cvssV3']['baseScore'] + v.cvssV3_baseSeverity = baseMetricV3['cvssV3']['baseSeverity'] + v.cvssV3_vectorString = baseMetricV3['cvssV3']['vectorString'] + v.cvssV3_exploitabilityScore = baseMetricV3['exploitabilityScore'] + v.cvssV3_impactScore = baseMetricV3['impactScore'] + v.cvssV3_attackVector = baseMetricV3['cvssV3']['attackVector'] + v.cvssV3_attackComplexity = baseMetricV3['cvssV3']['attackComplexity'] + v.cvssV3_privilegesRequired = baseMetricV3['cvssV3']['privilegesRequired'] + v.cvssV3_userInteraction = baseMetricV3['cvssV3']['userInteraction'] + v.cvssV3_scope = baseMetricV3['cvssV3']['scope'] + v.cvssV3_confidentialityImpact = baseMetricV3['cvssV3']['confidentialityImpact'] + v.cvssV3_integrityImpact = baseMetricV3['cvssV3']['integrityImpact'] + v.cvssV3_availabilityImpact = baseMetricV3['cvssV3']['availabilityImpact'] + if ('impact' in CVE_Item) and ('baseMetricV2' in CVE_Item['impact']): + baseMetricV2 = CVE_Item['impact']['baseMetricV2'] + v.cvssV2_baseScore = baseMetricV2['cvssV2']['baseScore'] + v.cvssV2_severity = baseMetricV2['severity'] + v.cvssV2_vectorString = baseMetricV2['cvssV2']['vectorString'] + v.cvssV2_exploitabilityScore = baseMetricV2['exploitabilityScore'] + v.cvssV2_impactScore = baseMetricV2['exploitabilityScore'] + v.cvssV2_accessVector = baseMetricV2['cvssV2']['accessVector'] + v.cvssV2_accessComplexity = baseMetricV2['cvssV2']['accessComplexity'] + v.cvssV2_authentication = baseMetricV2['cvssV2']['authentication'] + v.cvssV2_confidentialityImpact = baseMetricV2['cvssV2']['confidentialityImpact'] + v.cvssV2_integrityImpact = baseMetricV2['cvssV2']['integrityImpact'] + + configurations = CVE_Item['configurations'] + is_first_and = True + for i, config in enumerate(configurations['nodes']): + v.cpe_list += '[config]|' + v.cpe_list += '[and]|' + if "AND" == config['operator']: + # create AND record + if not is_first_and: + v.cpe_list += '[/and]|' + v.cpe_list += '[and]|' + #is_first_and = False + if 'children' in config: + for j, cpe_or_node in enumerate(config['children']): + if "OR" == cpe_or_node['operator']: + v.cpe_list += nist_scan_configuration_or(v,cpe_or_node, cve_name, j) + else: + print("ERROR CONFIGURE:OR_OP?:%s" % cpe_or_node['operator']) + elif "OR" == config['operator']: + v.cpe_list += nist_scan_configuration_or(v,config, cve_name, 0) + else: + print("ERROR CONFIGURE:OP?:%s" % config_rec['operator']) + v.cpe_list += '[/and]|' + v.cpe_list += '[/config]|' + + return v + +def nist_scan_configuration_or(cve, cpe_or_node, name, and_enum): + cpe_list = '[or]|' + for cpe in cpe_or_node['cpe']: + cpe23Uri = cpe['cpe23Uri'] + if 'cpeMatchString' in cpe: + cpeMatchString = cpe['cpeMatchString'] + else: + cpeMatchString = '' + if 'versionEndIncluding' in cpe: + versionEndIncluding = cpe['versionEndIncluding'] + else: + versionEndIncluding = '' + cpe_list += '%s,%s,%s,%s|' % (cpe['vulnerable'],cpe23Uri,cpeMatchString,versionEndIncluding) + cpe_list += '[/or]|' + return cpe_list + +def saveCveToCache(cve): + #Encode cve to json and save into cache + path = os.path.join(os.environ['SRT_BASE_DIR'], "data/cache") + # Catch the post against this page + try: + os.makedirs(path) + except: + pass + + cve_name = cve['CVE_data_meta']['ID'] + file = open(os.path.join(path, cve_name + ".json" ), 'w+') + file.write(json.dumps(cve)) diff --git a/lib/srtgui/reports.py b/lib/srtgui/reports.py new file mode 100644 index 00000000..f27beb34 --- /dev/null +++ b/lib/srtgui/reports.py @@ -0,0 +1,1326 @@ +# +# Security Response Tool Implementation +# +# Copyright (C) 2017-2018 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 + +from orm.models import Cve, Vulnerability, Investigation, Defect, Product +from orm.models import SrtSetting, Access, Keywords + +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) + +class Report(): + def __init__(self, parent_page, *args, **kwargs): + self.parent_page = parent_page + self.report_name = '%s%s' % (parent_page[0].upper(),parent_page[1:]) + self.title = self.report_name + self.request = kwargs['request'] + + def get_context_data(self, *args, **kwargs): + context = {} + context['title'] = self.title + context['parent_page'] = self.parent_page + context['report_name'] = self.report_name + context['report_enable_submit'] = '1' + # global variables + context['access'] = Access(int(self.request.session.get('srt_user_id', '0'))) + + return context + + def exec_report(self, *args, **kwargs): + return None + + +class ManagementReport(Report): + """Report for the Management Page""" + + def __init__(self, parent_page, *args, **kwargs): + _log_args("REPORT_MANAGEMENT_INIT(%s)" % parent_page, *args, **kwargs) + super(ManagementReport, self).__init__(parent_page, *args, **kwargs) + + def get_context_data(self, *args, **kwargs): + _log_args("REPORT_MANAGEMENT_CONTEXT", *args, **kwargs) + context = super(ManagementReport, self).get_context_data(*args, **kwargs) + + context['report_type_list'] = '\ + <option value="status">Overall Status</option> \ + <option value="vulnerabilities">Open Vulnerabilities</option> \ + <option value="investigations">Open Investigations</option> \ + ' + context['report_get_title'] = '1' + context['report_recordrange_list'] = '\ + <input type="radio" name="records" value="selected" checked> Selected<br> \ + ' + context['report_columnrange_list'] = '' + context['report_format_list'] = '\ + <input type="radio" name="format" value="txt" checked> Text<br> \ + <input type="radio" name="format" value="csv"> CSV<br> \ + ' + return context + + def exec_report(self, *args, **kwargs): + _log_args("REPORT_MANAGEMENT_EXEC", *args, **kwargs) + super(ManagementReport, self).exec_report(*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', '') + + report_name = '%s/management_%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: + tab = "\t" + else: + tab = " = " + + if 'status' == report_type: + if 'txt' == format: + file.write("Report : Management - Summary\n") + file.write("\n") + file.write("%s%s%s\n" % ('cve_total',tab,Cve.objects.all().count())) + file.write("%s%s%s\n" % ('cve_new',tab,Cve.objects.filter(status=Cve.NEW).count())) + file.write("%s%s%s\n" % ('cve_open',tab,Cve.objects.filter( Q(status=Cve.INVESTIGATE) & Q(status=Cve.VULNERABLE) ).count())) + file.write("%s%s%s\n" % ('vulnerability_total',tab,Vulnerability.objects.all().count())) + file.write("%s%s%s\n" % ('vulnerability_open',tab,Vulnerability.objects.filter(outcome=Vulnerability.OPEN).count())) + file.write("%s%s%s\n" % ('vulnerability_high',tab,Vulnerability.objects.filter(severity=Vulnerability.HIGH).count())) + file.write("%s%s%s\n" % ('vulnerability_medium',tab,Vulnerability.objects.filter(severity=Vulnerability.MEDIUM).count())) + file.write("%s%s%s\n" % ('vulnerability_low',tab,Vulnerability.objects.filter(severity=Vulnerability.HIGH).count())) + file.write("%s%s%s\n" % ('investigation_total',tab,Investigation.objects.all().count())) + file.write("%s%s%s\n" % ('investigation_open',tab,Investigation.objects.filter(outcome=Investigation.OPEN).count())) + file.write("%s%s%s\n" % ('investigation_high',tab,Investigation.objects.filter(severity=Investigation.HIGH).count())) + file.write("%s%s%s\n" % ('investigation_medium',tab,Investigation.objects.filter(severity=Investigation.MEDIUM).count())) + file.write("%s%s%s\n" % ('investigation_low',tab,Investigation.objects.filter(severity=Investigation.LOW).count())) + file.write("%s%s%s\n" % ('defect_total',tab,Defect.objects.all().count())) + + if 'vulnerabilities' == report_type: + if 'txt' == format: + file.write("Report : Management - Open Vulnerabilities\n") + file.write("\n") + else: + file.write("Name\tStatus\tOutcome\tSeverity\tComments\tCVEs\tInvestigations\n") + for v in Vulnerability.objects.filter(outcome=Vulnerability.OPEN): + if 'txt' == format: + file.write("Name: %s\n" % v.name) + file.write(" Status: %s\n" % v.get_status_text) + file.write(" Outcome: %s\n" % v.get_outcome_text) + file.write(" Severity: %s\n" % v.get_severity_text) + file.write(" Comments: %s\n" % v.comments) + file.write(" CVEs: ") + for i,vc in enumerate(v.vulnerability_to_cve.all()): + if i > 0: + file.write(",") + file.write("%s" % vc.cve.name) + file.write("\n") + file.write(" Investigations: ") + for i,investigation in enumerate(Investigation.objects.filter(vulnerability=v)): + if i > 0: + file.write(",") + file.write("%s" % investigation.name) + file.write("\n") + file.write("\n") + else: + file.write("%s\t%s\t%s\t%s\t%s\t" % (v.name,v.get_status_text,v.get_outcome_text,v.get_severity_text,v.comments)) + for i,vc in enumerate(v.vulnerability_to_cve.all()): + if i > 0: + file.write(",") + file.write("%s" % vc.cve.name) + file.write("\t") + for i,investigation in enumerate(Investigation.objects.filter(vulnerability=v)): + if i > 0: + file.write(",") + file.write("%s" % investigation.name) + file.write("\n") + + if 'investigations' == report_type: + if 'txt' == format: + file.write("Report : Management - Open Vulnerabilities\n") + file.write("\n") + else: + file.write("Name\tStatus\tOutcome\tSeverity\tComments\tDefects\n") + for investigation in Investigation.objects.filter(outcome=Vulnerability.OPEN): + if 'txt' == format: + file.write("Name: %s\n" % investigation.name) + file.write(" Status: %s\n" % investigation.get_status_text) + file.write(" Outcome: %s\n" % investigation.get_outcome_text) + file.write(" Severity: %s\n" % investigation.get_severity_text) + file.write(" Comments: %s\n" % investigation.comments) + file.write(" Defects: ") + for i,id in enumerate(investigation.investigation_to_defect.filter(investigation=investigation)): + if i > 0: + file.write(",") + file.write("%s" % id.defect.name) + file.write("\n") + file.write("\n") + else: + file.write("%s\t%s\t%s\t%s\t%s\t" % (investigation.name,investigation.get_status_text, + investigation.get_outcome_text,investigation.get_severity_text,investigation.comments)) + for i,id in enumerate(investigation.investigation_to_defect.filter(investigation=investigation)): + if i > 0: + file.write(",") + file.write("%s" % id.defect.name) + file.write("\n") + + return report_name,os.path.basename(report_name) + +class CveReport(Report): + """Report for the CVE Page""" + + def __init__(self, parent_page, *args, **kwargs): + _log_args("REPORT_CVE_INIT(%s)" % parent_page, *args, **kwargs) + super(CveReport, self).__init__(parent_page, *args, **kwargs) + + def get_context_data(self, *args, **kwargs): + _log_args("REPORT_CVE_CONTEXT", *args, **kwargs) + context = super(CveReport, self).get_context_data(*args, **kwargs) + + context['report_type_list'] = '\ + <option value="summary">CVE Summary</option> \ + <option value="audit">CVE Audit</option> \ + ' + context['report_get_title'] = '1' + context['report_recordrange_list'] = '\ + <input type="radio" name="records" value="selected" checked> Selected<br> \ + ' + context['report_columnrange_list'] = '' + context['report_format_list'] = '\ + <input type="radio" name="format" value="txt" checked> Text<br> \ + ' + context['report_custom_list'] = '\ + <input type="checkbox" class="checkbox-options" name="cvss_v2" value="cvss_v2"> CVSS_v2<br> \ + <input type="checkbox" class="checkbox-options" name="ref" type="ref"> References<br> \ + <input type="checkbox" class="checkbox-options" name="cpe" type="cpe"> CPE list<br> \ + ' + return context + + def exec_report(self, *args, **kwargs): + _log_args("REPORT_CVE_EXEC", *args, **kwargs) + super(CveReport, self).exec_report(*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', '') + + cvss_v2 = request_POST.get('cvss_v2', '') + ref_list = request_POST.get('ref', '') + cpe = request_POST.get('cpe', '') + + cve = Cve.objects.get(id=record_list) + + report_name = '%s/cve_%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: + tab = "\t" + else: + tab = " = " + + if ('summary' == report_type) or ('audit' == report_type): + if 'txt' == format: + file.write("Report : CVE %s - Summary\n" % cve.name) + file.write("\n") + file.write("%s%s%s\n" % ('source',tab,cve.source)) + file.write("%s%s%s\n" % ('status',tab,cve.status)) + file.write("%s%s%s\n" % ('cve_data_type',tab,cve.cve_data_type)) + file.write("%s%s%s\n" % ('cve_data_format',tab,cve.cve_data_format)) + file.write("%s%s%s\n" % ('cve_data_version',tab,cve.cve_data_version)) + file.write("%s%s%s\n" % ('description',tab,cve.description)) + file.write("\n") + + file.write("CVSS Version 3:\n") + file.write(" %s%s%s\n" % ('cvssV3_baseScore',tab,cve.cvssV3_baseScore)) + file.write(" %s%s%s\n" % ('cvssV3_baseSeverity',tab,cve.cvssV3_baseSeverity)) + file.write(" %s%s%s\n" % ('cvssV3_vectorString',tab,cve.cvssV3_vectorString)) + file.write(" %s%s%s\n" % ('cvssV3_exploitabilityScore',tab,cve.cvssV3_exploitabilityScore)) + file.write(" %s%s%s\n" % ('cvssV3_impactScore',tab,cve.cvssV3_impactScore)) + file.write(" %s%s%s\n" % ('cvssV3_attackVector',tab,cve.cvssV3_attackVector)) + file.write(" %s%s%s\n" % ('cvssV3_attackComplexity',tab,cve.cvssV3_attackComplexity)) + file.write(" %s%s%s\n" % ('cvssV3_privilegesRequired',tab,cve.cvssV3_privilegesRequired)) + file.write(" %s%s%s\n" % ('cvssV3_userInteraction',tab,cve.cvssV3_userInteraction)) + file.write(" %s%s%s\n" % ('cvssV3_scope',tab,cve.cvssV3_scope)) + file.write(" %s%s%s\n" % ('cvssV3_confidentialityImpact',tab,cve.cvssV3_confidentialityImpact)) + file.write(" %s%s%s\n" % ('cvssV3_integrityImpact',tab,cve.cvssV3_integrityImpact)) + file.write(" %s%s%s\n" % ('cvssV3_availabilityImpact',tab,cve.cvssV3_availabilityImpact)) + + if (cvss_v2): + file.write("\n") + file.write("CVSS Version 2:\n") + file.write(" %s%s%s\n" % ('cvssV2_baseScore',tab,cve.cvssV2_baseScore)) + file.write(" %s%s%s\n" % ('cvssV2_severity',tab,cve.cvssV2_severity)) + file.write(" %s%s%s\n" % ('cvssV2_vectorString',tab,cve.cvssV2_vectorString)) + file.write(" %s%s%s\n" % ('cvssV2_exploitabilityScore',tab,cve.cvssV2_exploitabilityScore)) + file.write(" %s%s%s\n" % ('cvssV2_impactScore',tab,cve.cvssV2_impactScore)) + file.write(" %s%s%s\n" % ('cvssV2_accessVector',tab,cve.cvssV2_accessVector)) + file.write(" %s%s%s\n" % ('cvssV2_accessComplexity',tab,cve.cvssV2_accessComplexity)) + file.write(" %s%s%s\n" % ('cvssV2_authentication',tab,cve.cvssV2_authentication)) + file.write(" %s%s%s\n" % ('cvssV2_confidentialityImpact',tab,cve.cvssV2_confidentialityImpact)) + file.write(" %s%s%s\n" % ('cvssV2_integrityImpact',tab,cve.cvssV2_integrityImpact)) + + if (ref_list): + file.write("\n") + file.write("References:\n") + for i,ref in enumerate(cve.references.all()): + file.write(" %s\n" % ref.hyperlink) + + if (cpe): + file.write("\n") + file.write("CPE Table:\n") + for cpe in cve.cpe_list.split("|"): + if '<config>' == cpe: + file.write(" Configation:\n") + elif '<and>' == cpe: + file.write(" * AND\n") + elif '<or>' == cpe: + file.write(" * OR\n") + else : + file.write(" %s\n" % cpe) + + if 'audit' == report_type: + + for cv in cve.cve_to_vulnerability.all(): + v = cv.vulnerability + file.write("\n") + file.write("-------------------------------------------\n") + file.write("Vulnerability: %s\n" % v.name) + file.write(" Status: %s\n" % v.get_status_text) + file.write(" Outcome: %s\n" % v.get_outcome_text) + file.write(" Severity: %s\n" % v.get_severity_text) + file.write(" Comments: %s\n" % v.comments) + file.write("\n") + file.write(" Investigations:\n") + for investigation in Investigation.objects.filter(vulnerability=v): + file.write(" Name: %s\n" % investigation.name) + file.write(" Status: %s\n" % investigation.get_status_text) + file.write(" Outcome: %s\n" % investigation.get_outcome_text) + file.write(" Severity: %s\n" % investigation.get_severity_text) + file.write(" Defects: ") + for i,id in enumerate(investigation.investigation_to_defect.all()): + if i > 0: + file.write(",") + file.write("%s (%s)" % (id.defect.name,id.defect.get_status_text)) + file.write("\n") + + file.write("\n") + file.write(" Comments:\n") + for i,vc in enumerate(v.vulnerability_comments.all()): + file.write(" %s (%s): %s\n" % (vc.date,vc.author,vc.comment)) + + file.write("\n") + file.write(" Audit Trail:\n") + for i,vh in enumerate(v.vulnerability_history.all()): + file.write(" %s (%s): %s\n" % (vh.date,vh.author,vh.comment)) + + file.write("\n") + else: + file.write("Investigations: no attached investigations as this time\n") + + return report_name,os.path.basename(report_name) + +class VulnerabilityReport(Report): + """Report for the Vulnerability Page""" + + def __init__(self, parent_page, *args, **kwargs): + _log_args("REPORT_VULNERABILITY_INIT(%s)" % parent_page, *args, **kwargs) + super(VulnerabilityReport, self).__init__(parent_page, *args, **kwargs) + + def get_context_data(self, *args, **kwargs): + _log_args("REPORT_VULNERABILITY_CONTEXT", *args, **kwargs) + context = super(VulnerabilityReport, self).get_context_data(*args, **kwargs) + + context['report_type_list'] = '\ + <option value="summary">Vulnerability Summary</option> \ + <option value="audit">Vulnerability Audit</option> \ + ' + context['report_get_title'] = '1' + context['report_recordrange_list'] = '\ + <input type="radio" name="records" value="selected" checked> Selected<br> \ + ' + context['report_columnrange_list'] = '' + context['report_format_list'] = '\ + <input type="radio" name="format" value="txt" checked> Text<br> \ + ' + return context + + def exec_report(self, *args, **kwargs): + _log_args("REPORT_VULNERABILITY_EXEC", *args, **kwargs) + super(VulnerabilityReport, self).exec_report(*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', '') + + v = Vulnerability.objects.get(id=record_list) + + report_name = '%s/vulnerability_%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: + tab = "\t" + else: + tab = " = " + + if ('summary' == report_type) or ('audit' == report_type): + if 'txt' == format: + file.write("Report : Vulnerability %s - Summary\n" % v.name) + file.write("\n") + + file.write("Vulnerability: %s\n" % v.name) + file.write(" Status: %s\n" % v.get_status_text) + file.write(" Outcome: %s\n" % v.get_outcome_text) + file.write(" Severity: %s\n" % v.get_severity_text) + file.write(" Comments: %s\n" % v.comments) + + file.write("\n") + file.write("Affected Products:\n") + found_p = False + for i,p in enumerate(v.get_affected_list): + found_p = True + file.write("%2d) Product: %s\n" % (i,p.product.long_name)) + found_i = False + for investigation in Investigation.objects.filter(vulnerability=v,product=p.product): + found_i = True + file.write(" Investigation: %s\n" % investigation.name) + file.write(" Status: %s\n" % investigation.get_status_text) + file.write(" Outcome: %s\n" % investigation.get_outcome_text) + file.write(" Severity: %s\n" % investigation.get_severity_text) + file.write(" Defects: ") + for j,id in enumerate(investigation.investigation_to_defect.all()): + if j > 0: + file.write(",") + file.write("%s (%s)" % (id.defect.name,id.defect.get_status_text)) + file.write("\n") + if not found_i: + file.write(" No investigations found\n") + if not found_p: + file.write(" No affected products found\n") + + file.write("\n") + file.write("Related Products:\n") + found_p = False + for i,p in enumerate(v.get_related_list): + found_p = True + file.write("%2d) Product: %s\n" % (i,p.product.long_name)) + if not found_p: + file.write(" No related products found\n") + + file.write("\n") + file.write("Comments:\n") + found_c = False + for i,vc in enumerate(v.vulnerability_comments.all()): + found_c = True + file.write(" %2d) %s (%s): %s\n" % (i,vc.date,vc.author,vc.comment)) + if not found_c: + file.write(" No comments found\n") + + if 'audit' == report_type: + file.write("\n") + file.write("Audit Trail:\n") + for i,vh in enumerate(v.vulnerability_history.all()): + file.write(" %2d) %s (%s): %s\n" % (i,vh.date,vh.author,vh.comment)) + + file.write("\n") + + return report_name,os.path.basename(report_name) + +class InvestigationReport(Report): + """Report for the Investigation Page""" + + def __init__(self, parent_page, *args, **kwargs): + _log_args("REPORT_INVESTIGATION_INIT(%s)" % parent_page, *args, **kwargs) + super(InvestigationReport, self).__init__(parent_page, *args, **kwargs) + + def get_context_data(self, *args, **kwargs): + _log_args("REPORT_INVESTIGATION_CONTEXT", *args, **kwargs) + context = super(InvestigationReport, self).get_context_data(*args, **kwargs) + + context['report_type_list'] = '\ + <option value="summary">Investigation Summary</option> \ + <option value="audit">Investigation Audit</option> \ + ' + context['report_get_title'] = '1' + context['report_recordrange_list'] = '\ + <input type="radio" name="records" value="selected" checked> Selected<br> \ + ' + context['report_columnrange_list'] = '' + context['report_format_list'] = '\ + <input type="radio" name="format" value="txt" checked> Text<br> \ + ' + return context + + def exec_report(self, *args, **kwargs): + _log_args("REPORT_INVESTIGATION_EXEC", *args, **kwargs) + super(InvestigationReport, self).exec_report(*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', '') + + investigation = Investigation.objects.get(id=record_list) + + report_name = '%s/investigation_%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: + tab = "\t" + else: + tab = " = " + + if ('summary' == report_type) or ('audit' == report_type): + if 'txt' == format: + file.write("Report : Investigation %s - Summary\n" % investigation.name) + file.write("\n") + + file.write("Name: %s\n" % investigation.name) + file.write(" Status: %s\n" % investigation.get_status_text) + file.write(" Outcome: %s\n" % investigation.get_outcome_text) + file.write(" Severity: %s\n" % investigation.get_severity_text) + file.write(" Defects: ") + for i,id in enumerate(investigation.investigation_to_defect.all()): + if i > 0: + file.write(",") + file.write("%s (%s)" % (id.defect.name,id.defect.get_status_text)) + file.write("\n") + + file.write("\n") + file.write("Comments:\n") + found_c = False + for i,vc in enumerate(investigation.investigation_comments.all()): + found_c = True + file.write(" %s (%s): %s\n" % (ic.date,ic.author,ic.comment)) + if not found_c: + file.write(" No comments found\n") + + if 'audit' == report_type: + file.write("\n") + file.write(" Audit Trail:\n") + for i,ih in enumerate(investigation.investigation_history.all()): + file.write(" %s (%s): %s\n" % (ih.date,ih.author,ih.comment)) + + file.write("\n") + + return report_name,os.path.basename(report_name) + +class DefectReport(Report): + """Report for the Defect Page""" + + def __init__(self, parent_page, *args, **kwargs): + _log_args("REPORT_DEFECT_INIT(%s)" % parent_page, *args, **kwargs) + super(DefectReport, self).__init__(parent_page, *args, **kwargs) + + def get_context_data(self, *args, **kwargs): + _log_args("REPORT_DEFECT_CONTEXT", *args, **kwargs) + context = super(DefectReport, self).get_context_data(*args, **kwargs) + + context['report_type_list'] = '\ + <option value="summary">Defect Summary</option> \ + ' + context['report_get_title'] = '1' + context['report_recordrange_list'] = '\ + <input type="radio" name="records" value="selected" checked> Selected<br> \ + ' + context['report_columnrange_list'] = '' + context['report_format_list'] = '\ + <input type="radio" name="format" value="txt" checked> Text<br> \ + <input type="radio" name="format" value="csv"> CSV<br> \ + ' + return context + + def exec_report(self, *args, **kwargs): + _log_args("REPORT_DEFECT_EXEC", *args, **kwargs) + super(DefectReport, self).exec_report(*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', '') + + report_name = '%s/defect_%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: + tab = "\t" + else: + tab = "," + + if ('summary' == report_type): + if 'csv' == format: + file.write("Name\tSummary\tPriority\tStatus\tResolution\tReleased Version\tURL\tInvestigations\tProduct\n") + if 'txt' == format: + file.write("Report : Defects Table\n") + file.write("\n") + file.write("Name,Summary,Priority,Status,Resolution,Released Version,URL,Investigations,Product\n") + + defect = Defect.objects.get(id=record_list) + file.write("%s%s" % (defect.name,tab)) + file.write("%s%s" % (defect.summary,tab)) + file.write("%s%s" % (defect.get_priority_text,tab)) + file.write("%s%s" % (defect.get_status_text,tab)) + file.write("%s%s" % (defect.get_resolution_text,tab)) + file.write("%s%s" % (defect.release_version,tab)) + file.write("%s%s" % (defect.publish,tab)) + file.write("%s%s" % (defect.url,tab)) + for i,di in enumerate(defect.defect_to_investigation.all()): + if i > 0: + file.write(" ") + file.write("%s" % (di.investigation.name)) + file.write("%s" % tab) + tab='' # EOL + file.write("%s%s" % (defect.product.long_name,tab)) + file.write("\n") + + return report_name,os.path.basename(report_name) + + +class CvesReport(Report): + """Report for the CVEs Page""" + + def __init__(self, parent_page, *args, **kwargs): + _log_args("REPORT_CVES_INIT(%s)" % parent_page, *args, **kwargs) + super(CvesReport, self).__init__(parent_page, *args, **kwargs) + + def get_context_data(self, *args, **kwargs): + _log_args("REPORT_CVES_CONTEXT", *args, **kwargs) + context = super(CvesReport, self).get_context_data(*args, **kwargs) + + context['report_type_list'] = '\ + <option value="summary">CVEs Table</option> \ + ' + context['report_get_title'] = '1' + context['report_recordrange_list'] = '\ + <input type="radio" name="records" value="selected" checked> Selected<br> \ + ' + context['report_columnrange_list'] = '' + context['report_format_list'] = '\ + <input type="radio" name="format" value="txt" checked> Text<br> \ + <input type="radio" name="format" value="csv"> CSV<br> \ + ' + return context + + def exec_report(self, *args, **kwargs): + _log_args("REPORT_CVES_EXEC", *args, **kwargs) + super(CvesReport, self).exec_report(*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', '') + + report_name = '%s/cves_%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: + tab = "\t" + else: + tab = "," + + if ('summary' == report_type): + if 'csv' == format: + file.write("Name\tStatus\tType\tFormat\tVersion\tVulnerabilities\tDescription\n") + if 'txt' == format: + file.write("Report : CVEs Table\n") + file.write("\n") + file.write("Name,Status,Type,Format,Version,Vulnerabilities,Description\n") + + for id in record_list.split(','): + if not id: + continue + cve = Cve.objects.get(id=id) + file.write("%s%s" % (cve.name,tab)) + file.write("%s%s" % (cve.get_status_text,tab)) + file.write("%s%s" % (cve.cve_data_type,tab)) + file.write("%s%s" % (cve.cve_data_format,tab)) + file.write("%s%s" % (cve.cve_data_version,tab)) + + for i,cv in enumerate(cve.cve_to_vulnerability.all()): + if i > 0: + file.write(" ") + file.write("%s" % cv.vulnerability.name) + file.write("%s" % tab) + + file.write("%s%s" % (cve.description,tab)) + file.write("\n") + + return report_name,os.path.basename(report_name) + +class SelectCvesReport(Report): + """Report for the Select CVEs Page""" + + def __init__(self, parent_page, *args, **kwargs): + _log_args("REPORT_SELECTCVES_INIT(%s)" % parent_page, *args, **kwargs) + super(SelectCvesReport, self).__init__(parent_page, *args, **kwargs) + + def get_context_data(self, *args, **kwargs): + _log_args("REPORT_SELECTCVES_CONTEXT", *args, **kwargs) + context = super(SelectCvesReport, self).get_context_data(*args, **kwargs) + + context['report_type_list'] = '\ + <option value="summary">CVEs Table</option> \ + ' + context['report_get_title'] = '1' + context['report_recordrange_list'] = '\ + <input type="radio" name="records" value="selected" checked> Selected<br> \ + ' + context['report_columnrange_list'] = '' + context['report_format_list'] = '\ + <input type="radio" name="format" value="txt" checked> Text<br> \ + <input type="radio" name="format" value="csv"> CSV<br> \ + ' + return context + + def exec_report(self, *args, **kwargs): + _log_args("REPORT_SELECTCVES_EXEC", *args, **kwargs) + super(SelectCvesReport, self).exec_report(*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', '') + + report_name = '%s/select_cves_%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: + tab = "\t" + else: + tab = "," + + if ('summary' == report_type): + if 'csv' == format: + file.write("Name\tStatus\tType\tFormat\tVersion\tVulnerabilities\tDescription\n") + if 'txt' == format: + file.write("Report : CVEs Table\n") + file.write("\n") + file.write("Name,Status,Type,Format,Version,Vulnerabilities,Description\n") + + for id in record_list.split(','): + if not id: + continue + cve = Cve.objects.get(id=id) + file.write("%s%s" % (cve.name,tab)) + file.write("%s%s" % (cve.get_status_text,tab)) + file.write("%s%s" % (cve.cve_data_type,tab)) + file.write("%s%s" % (cve.cve_data_format,tab)) + file.write("%s%s" % (cve.cve_data_version,tab)) + + for i,cv in enumerate(cve.cve_to_vulnerability.all()): + if i > 0: + file.write(" ") + file.write("%s" % cv.vulnerability.name) + file.write("%s" % tab) + + file.write("%s%s" % (cve.description,tab)) + file.write("\n") + + return report_name,os.path.basename(report_name) + +class VulnerabilitiesReport(Report): + """Report for the Vulnerabilities Page""" + + def __init__(self, parent_page, *args, **kwargs): + _log_args("REPORT_VULNERABILITIES_INIT(%s)" % parent_page, *args, **kwargs) + super(VulnerabilitiesReport, self).__init__(parent_page, *args, **kwargs) + + def get_context_data(self, *args, **kwargs): + _log_args("REPORT_VULNERABILITIES_CONTEXT", *args, **kwargs) + context = super(VulnerabilitiesReport, self).get_context_data(*args, **kwargs) + + context['report_type_list'] = '\ + <option value="summary">Vulnerabilities Table</option> \ + ' + context['report_get_title'] = '1' + context['report_recordrange_list'] = '\ + <input type="radio" name="records" value="selected" checked> Selected<br> \ + ' + context['report_columnrange_list'] = '' + context['report_format_list'] = '\ + <input type="radio" name="format" value="txt" checked> Text<br> \ + <input type="radio" name="format" value="csv"> CSV<br> \ + ' + return context + + def exec_report(self, *args, **kwargs): + _log_args("REPORT_VULNERABILITIES_EXEC", *args, **kwargs) + super(VulnerabilitiesReport, self).exec_report(*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', '') + + report_name = '%s/vulnerabilities_%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: + tab = "\t" + else: + tab = "," + + if ('summary' == report_type): + if 'csv' == format: + file.write("Name\tStatus\tOutcome\tSeverity\tCVEs\tInvestigations\n") + if 'txt' == format: + file.write("Report : Investigations Table\n") + file.write("\n") + file.write("Name,Status,Outcome,Severity,CVEs,Investigations\n") + + for id in record_list.split(','): + if not id: + continue + v = Vulnerability.objects.get(id=id) + file.write("%s%s" % (v.name,tab)) + file.write("%s%s" % (v.get_status_text,tab)) + file.write("%s%s" % (v.get_outcome_text,tab)) + file.write("%s%s" % (v.get_severity_text,tab)) + for i,vc in enumerate(v.vulnerability_to_cve.all()): + if i > 0: + file.write(" ") + file.write("%s" % vc.cve.name) + file.write("%s" % tab) + for i,investigation in enumerate(Investigation.objects.filter(vulnerability=v)): + if i > 0: + file.write(" ") + file.write("%s" % investigation.name) + #file.write("%s" % tab) + file.write("\n") + + return report_name,os.path.basename(report_name) + +class InvestigationsReport(Report): + """Report for the Investigations Page""" + + def __init__(self, parent_page, *args, **kwargs): + _log_args("REPORT_INVESTIGATIONS_INIT(%s)" % parent_page, *args, **kwargs) + super(InvestigationsReport, self).__init__(parent_page, *args, **kwargs) + + def get_context_data(self, *args, **kwargs): + _log_args("REPORT_INVESTIGATIONS_CONTEXT", *args, **kwargs) + context = super(InvestigationsReport, self).get_context_data(*args, **kwargs) + + context['report_type_list'] = '\ + <option value="summary">Investigations Table</option> \ + ' + context['report_get_title'] = '1' + context['report_recordrange_list'] = '\ + <input type="radio" name="records" value="selected" checked> Selected<br> \ + ' + context['report_columnrange_list'] = '' + context['report_format_list'] = '\ + <input type="radio" name="format" value="txt" checked> Text<br> \ + <input type="radio" name="format" value="csv"> CSV<br> \ + ' + return context + + def exec_report(self, *args, **kwargs): + _log_args("REPORT_INVESTIGATIONS_EXEC", *args, **kwargs) + super(InvestigationsReport, self).exec_report(*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', '') + + report_name = '%s/investigations_%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: + tab = "\t" + else: + tab = "," + + if ('summary' == report_type): + if 'csv' == format: + file.write("Name\tStatus\tOutcome\tSeverity\tProduct\tDefects\n") + if 'txt' == format: + file.write("Report : Investigations Table\n") + file.write("\n") + file.write("Name,Status,Outcome,Severity,Product,Defects\n") + + for id in record_list.split(','): + if not id: + continue + investigation = Investigation.objects.get(id=id) + file.write("%s%s" % (investigation.name,tab)) + file.write("%s%s" % (investigation.get_status_text,tab)) + file.write("%s%s" % (investigation.get_outcome_text,tab)) + file.write("%s%s" % (investigation.get_severity_text,tab)) + file.write("%s%s" % (investigation.product.long_name,tab)) + for i,id in enumerate(investigation.investigation_to_defect.all()): + if i > 0: + file.write(" ") + file.write("%s (%s)" % (id.defect.name,id.defect.get_status_text)) + #file.write("%s" % tab) + file.write("\n") + + return report_name,os.path.basename(report_name) + +class DefectsReport(Report): + """Report for the Defects Page""" + + def __init__(self, parent_page, *args, **kwargs): + _log_args("REPORT_DEFECTS_INIT(%s)" % parent_page, *args, **kwargs) + super(DefectsReport, self).__init__(parent_page, *args, **kwargs) + + def get_context_data(self, *args, **kwargs): + _log_args("REPORT_DEFECTS_CONTEXT", *args, **kwargs) + context = super(DefectsReport, self).get_context_data(*args, **kwargs) + + context['report_type_list'] = '\ + <option value="summary">Defects Table</option> \ + ' + context['report_get_title'] = '1' + context['report_recordrange_list'] = '\ + <input type="radio" name="records" value="selected" checked> Selected<br> \ + ' + context['report_columnrange_list'] = '' + context['report_format_list'] = '\ + <input type="radio" name="format" value="txt" checked> Text<br> \ + <input type="radio" name="format" value="csv"> CSV<br> \ + ' + return context + + def exec_report(self, *args, **kwargs): + _log_args("REPORT_DEFECTS_EXEC", *args, **kwargs) + super(DefectsReport, self).exec_report(*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', '') + + report_name = '%s/defects_%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: + tab = "\t" + else: + tab = "," + + if ('summary' == report_type): + if 'csv' == format: + file.write("Name\tSummary\tPriority\tStatus\tResolution\tReleased Version\tURL\tInvestigations\tProduct\n") + if 'txt' == format: + file.write("Report : Defects Table\n") + file.write("\n") + file.write("Name,Summary,Priority,Status,Resolution,Released Version,URL,Investigations,Product\n") + + for id in record_list.split(','): + if not id: + continue + defect = Defect.objects.get(id=id) + file.write("%s%s" % (defect.name,tab)) + file.write("%s%s" % (defect.summary,tab)) + file.write("%s%s" % (defect.get_priority_text,tab)) + file.write("%s%s" % (defect.get_status_text,tab)) + file.write("%s%s" % (defect.get_resolution_text,tab)) + file.write("%s%s" % (defect.release_version,tab)) + file.write("%s%s" % (defect.publish,tab)) + file.write("%s%s" % (defect.url,tab)) + for i,di in enumerate(defect.defect_to_investigation.all()): + if i > 0: + file.write(" ") + file.write("%s" % (di.investigation.name)) + file.write("%s" % tab) + tab='' # EOL + file.write("%s%s" % (defect.product.long_name,tab)) + file.write("\n") + + return report_name,os.path.basename(report_name) + +class ProductsReport(Report): + """Report for the Products Page""" + + def __init__(self, parent_page, *args, **kwargs): + _log_args("REPORT_PRODUCTS_INIT(%s)" % parent_page, *args, **kwargs) + super(ProductsReport, self).__init__(parent_page, *args, **kwargs) + + def get_context_data(self, *args, **kwargs): + _log_args("REPORT_PRODUCTS_CONTEXT", *args, **kwargs) + context = super(ProductsReport, self).get_context_data(*args, **kwargs) + + context['report_type_list'] = '\ + <option value="summary">Products Table</option> \ + ' + context['report_get_title'] = '1' + context['report_recordrange_list'] = '\ + <input type="radio" name="records" value="selected" checked> Selected<br> \ + ' + context['report_columnrange_list'] = '' + context['report_format_list'] = '\ + <input type="radio" name="format" value="txt" checked> Text<br> \ + <input type="radio" name="format" value="csv"> CSV<br> \ + ' + return context + + def exec_report(self, *args, **kwargs): + _log_args("REPORT_PRODUCTS_EXEC", *args, **kwargs) + super(ProductsReport, self).exec_report(*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', '') + + report_name = '%s/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: + tab = "\t" + else: + tab = "," + + if ('summary' == report_type): + if 'csv' == format: + file.write("Name\tVersion\tProfile\tCPE\tSRT SPE\tInvestigations\tDefects\n") + if 'txt' == format: + file.write("Report : Investigations Table\n") + file.write("\n") + file.write("Name,Version,Profile,CPE,SRT SPE,Investigations,Defects\n") + + for id in record_list.split(','): + if not id: + continue + product = Product.objects.get(id=id) + file.write("%s%s" % (product.name,tab)) + file.write("%s%s" % (product.version,tab)) + file.write("%s%s" % (product.profile,tab)) + file.write("%s%s" % (product.cpe,tab)) + file.write("%s%s" % (product.srt_cpe,tab)) + file.write("%s%s" % (product.defect_prefix,tab)) + for i,pi in enumerate(product.product_investigation.all()): + if i > 0: + file.write(" ") + file.write("%s" % (pi.name)) + file.write("%s" % tab) + for i,pd in enumerate(product.product_defect.all()): + if i > 0: + file.write(" ") + file.write("%s" % (pd.name)) + #file.write("%s" % tab) + file.write("\n") + + return report_name,os.path.basename(report_name) + +class PublishCveReport(Report): + """Report for the Publish Cve Page""" + + def __init__(self, parent_page, *args, **kwargs): + _log_args("REPORT_PUBLISHCVE_INIT(%s)" % parent_page, *args, **kwargs) + super(PublishCveReport, self).__init__(parent_page, *args, **kwargs) + + def get_context_data(self, *args, **kwargs): + _log_args("REPORT_PUBLISHCVE_CONTEXT", *args, **kwargs) + context = super(PublishCveReport, self).get_context_data(*args, **kwargs) + + context['report_type_list'] = '\ + <option value="summary">CVE Publish Request Table</option> \ + ' + context['report_get_title'] = '1' + context['report_recordrange_list'] = '\ + <input type="radio" name="records" value="selected" checked> Selected<br> \ + ' + context['report_columnrange_list'] = '' + context['report_format_list'] = '\ + <input type="radio" name="format" value="txt" checked> Text<br> \ + <input type="radio" name="format" value="csv"> CSV<br> \ + ' + return context + + def exec_report(self, *args, **kwargs): + _log_args("REPORT_PUBLISHCVE_EXEC", *args, **kwargs) + super(PublishCveReport, self).exec_report(*args, **kwargs) + + _log("FOO1") + 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', '') + + _log("FOO2 (%s,%s,%s" % (record_list,format,report_type)) + report_name = '%s/cve_publish_%s_%s.%s' % (SRT_REPORT_DIR,report_type,datetime.today().strftime('%Y%m%d%H%M'),format) + with open(report_name, 'w') as file: + + _log("FOO3") + if 'csv' == format: + tab = "\t" + else: + tab = "," + + if ('summary' == report_type): + if 'csv' == format: + file.write("Name\tStatus\tType\tFormat\tVersion\tVulnerabilities\tDescription\n") + if 'txt' == format: + file.write("Report : CVEs Table\n") + file.write("\n") + file.write("Name,Status,Type,Format,Version,Vulnerabilities,Description\n") + + _log("FOO4") + for id in record_list.split(','): + _log("FOO5:%s" % id) + if not id: + continue + try: + cve = Cve.objects.get(id=id) + file.write("%s%s" % (cve.name,tab)) + file.write("%s%s" % (cve.get_status_text,tab)) + file.write("%s%s" % (cve.cve_data_type,tab)) + file.write("%s%s" % (cve.cve_data_format,tab)) + file.write("%s%s" % (cve.cve_data_version,tab)) + + for i,cv in enumerate(cve.cve_to_vulnerability.all()): + if i > 0: + file.write(" ") + file.write("%s" % cv.vulnerability.name) + file.write("%s" % tab) + + file.write("%s" % (cve.description)) + file.write("\n") + except Exception as e: + _log("FOOX:%s" % e) + + _log("FOO9:%s" % (report_name)) + return report_name,os.path.basename(report_name) + +class PublishPendingCveReport(Report): + """Report for the Publish Cve Page""" + + def __init__(self, parent_page, *args, **kwargs): + _log_args("REPORT_PUBLISHPENDINGCVE_INIT(%s)" % parent_page, *args, **kwargs) + super(PublishPendingCveReport, self).__init__(parent_page, *args, **kwargs) + + def get_context_data(self, *args, **kwargs): + _log_args("REPORT_PUBLISHPENDINGCVE_CONTEXT", *args, **kwargs) + context = super(PublishPendingCveReport, self).get_context_data(*args, **kwargs) + context['report_type_list'] = '\ + <option value="summary">CVE Publish Request Table</option> \ + ' + context['report_get_title'] = '1' + context['report_recordrange_list'] = '\ + <input type="radio" name="records" value="selected" checked> Selected<br> \ + ' + context['report_columnrange_list'] = '' + context['report_format_list'] = '\ + <input type="radio" name="format" value="txt" checked> Text<br> \ + <input type="radio" name="format" value="csv"> CSV<br> \ + ' + return context + + def exec_report(self, *args, **kwargs): + _log_args("REPORT_PUBLISHPENDINGCVE_EXEC", *args, **kwargs) + super(PublishPendingCveReport, self).exec_report(*args, **kwargs) + + _log("FOO1") + 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', '') + + _log("FOO2 (%s,%s,%s" % (record_list,format,report_type)) + report_name = '%s/cve_publish_%s_%s.%s' % (SRT_REPORT_DIR,report_type,datetime.today().strftime('%Y%m%d%H%M'),format) + with open(report_name, 'w') as file: + + _log("FOO3") + if 'csv' == format: + tab = "\t" + else: + tab = "," + + if ('summary' == report_type): + if 'csv' == format: + file.write("Name\tStatus\tType\tFormat\tVersion\tVulnerabilities\tDescription\n") + if 'txt' == format: + file.write("Report : CVEs Table\n") + file.write("\n") + file.write("Name,Status,Type,Format,Version,Vulnerabilities,Description\n") + + _log("FOO4") + for id in record_list.split(','): + if not id: + continue + _log("FOO5:%s" % id) + try: + cve = Cve.objects.get(id=id) + file.write("%s%s" % (cve.name,tab)) + file.write("%s%s" % (cve.get_status_text,tab)) + file.write("%s%s" % (cve.cve_data_type,tab)) + file.write("%s%s" % (cve.cve_data_format,tab)) + file.write("%s%s" % (cve.cve_data_version,tab)) + + for i,cv in enumerate(cve.cve_to_vulnerability.all()): + if i > 0: + file.write(" ") + file.write("%s" % cv.vulnerability.name) + file.write("%s" % tab) + + file.write("%s" % (cve.description)) + file.write("\n") + except Exception as e: + _log("FOOX:%s" % e) + + _log("FOO9:%s" % (report_name)) + return report_name,os.path.basename(report_name) + +class DefaultReport(Report): + """Report for the Default Page""" + + def __init__(self, parent_page, *args, **kwargs): + _log_args("REPORT_GENERIC_INIT(%s)" % parent_page, *args, **kwargs) + super(DefaultReport, self).__init__(parent_page, *args, **kwargs) + self.default_orderby = "name" + + def get_context_data(self, *args, **kwargs): + _log_args("REPORT_GENERIC_CONTEXT", *args, **kwargs) + context = super(DefaultReport, self).get_context_data(*args, **kwargs) + context['report_enable_submit'] = '' + + #context['report_recordrange_list'] = '\ + # <input type="radio" name="records" value="selected" checked> Selected<br> \ + # <input type="radio" name="records" value="all"> all<br> \ + #context['report_columnrange_list'] = '' + # <input type="radio" name="columns" value="selected" checked> Selected<br> + # <input type="radio" name="columns" value="all"> All<br> + #context['report_format_list'] = '\ + # <input type="radio" name="format" value="txt" checked> Text<br> \ + # <input type="radio" name="format" value="csv"> CSV<br> \ + # <input type="radio" name="format" value="rtf"> RTF<br> + # <input type="radio" name="format" value="xml"> XML<br> + # <input type="radio" name="format" value="json"> CVE NIST JSON<br> + + return context + + def exec_report(self, *args, **kwargs): + _log_args("REPORT_GENERIC_EXEC", *args, **kwargs) + super(ManagementReport, self).exec_report(*args, **kwargs) + return None + + +class ReportManager(): + @staticmethod + def get_report_class(parent_page, *args, **kwargs): + if ('management' == parent_page) or ('manage' == parent_page): + return ManagementReport(parent_page, *args, **kwargs) + + elif 'cve' == parent_page: + return CveReport(parent_page, *args, **kwargs) + elif 'vulnerability' == parent_page: + return VulnerabilityReport(parent_page, *args, **kwargs) + elif 'investigation' == parent_page: + return InvestigationReport(parent_page, *args, **kwargs) + elif 'defect' == parent_page: + return DefectReport(parent_page, *args, **kwargs) + + elif 'cves' == parent_page: + return CvesReport(parent_page, *args, **kwargs) + elif 'select-cves' == parent_page: + return SelectCvesReport(parent_page, *args, **kwargs) + elif 'vulnerabilities' == parent_page: + return VulnerabilitiesReport(parent_page, *args, **kwargs) + elif 'investigations' == parent_page: + return InvestigationsReport(parent_page, *args, **kwargs) + elif 'defects' == parent_page: + return DefectsReport(parent_page, *args, **kwargs) + elif 'products' == parent_page: + return ProductsReport(parent_page, *args, **kwargs) + + elif 'select-publish' == parent_page: + return PublishCveReport(parent_page, *args, **kwargs) + elif 'update-published' == parent_page: + return PublishPendingCveReport(parent_page, *args, **kwargs) + + + else: + return DefaultReport(parent_page, *args, **kwargs) + + @staticmethod + def get_context_data(parent_page, *args, **kwargs): + reporter = ReportManager.get_report_class(parent_page, *args, **kwargs) + return reporter.get_context_data(*args, **kwargs) + + @staticmethod + def exec_report(parent_page, *args, **kwargs): + reporter = ReportManager.get_report_class(parent_page, *args, **kwargs) + return reporter.exec_report(*args, **kwargs) diff --git a/lib/srtgui/tables.py b/lib/srtgui/tables.py index 798855d6..643dd34d 100644 --- a/lib/srtgui/tables.py +++ b/lib/srtgui/tables.py @@ -22,7 +22,8 @@ from srtgui.widgets import ToasterTable from orm.models import SrtSetting from orm.models import Cve, Vulnerability, Investigation, CweTable, Product -from orm.models import CpeTable, Access, CpeFilter, Defect, Keywords +from orm.models import CpeTable, Access, CpeFilter, Defect, Keywords, DataSource +from orm.models import PublishPending from django.db.models import Q, Max, Sum, Count, When, Case, Value, IntegerField from django.conf.urls import url @@ -35,47 +36,46 @@ from srtgui.tablefilter import TableFilterActionToggle from srtgui.tablefilter import TableFilterActionDateRange from srtgui.tablefilter import TableFilterActionDay -def _log(msg): - f1=open('/tmp/srt.log', 'a') - f1.write("|" + msg + "|\n" ) - f1.close() +import os +# quick development/debugging support +from srtgui.api import _log -class AllCveTable(ToasterTable): +class CvesTable(ToasterTable): """Table of All CVE's in SRTool""" def __init__(self, *args, **kwargs): - super(AllCveTable, self).__init__(*args, **kwargs) + super(CvesTable, self).__init__(*args, **kwargs) self.default_orderby = "name" def get_context_data(self, **kwargs): - context = super(AllCveTable, self).get_context_data(**kwargs) + context = super(CvesTable, self).get_context_data(**kwargs) return context def setup_filters(self, *args, **kwargs): - # Is Vulnerable filter + # Is Status filter is_status = TableFilter(name="is_status", title="Filter CVE's by 'Status") - exec_is_new = TableFilterActionToggle( + is_status.add_action(TableFilterActionToggle( "new", "New", Q(status=Cve.NEW)) - exec_is_investigate = TableFilterActionToggle( + ) + is_status.add_action(TableFilterActionToggle( "investigate", "Investigate", Q(status=Cve.INVESTIGATE)) - exec_is_vulnerable = TableFilterActionToggle( + ) + is_status.add_action(TableFilterActionToggle( "vulnerable", "Is Vulnerable", Q(status=Cve.VULNERABLE)) - exec_is_not_vulnerable = TableFilterActionToggle( + ) + is_status.add_action(TableFilterActionToggle( "not_vulnerable", "Not Vulnerable", Q(status=Cve.NOT_VULNERABLE)) - is_status.add_action(exec_is_new) - is_status.add_action(exec_is_investigate) - is_status.add_action(exec_is_vulnerable) - is_status.add_action(exec_is_not_vulnerable) + ) self.add_filter(is_status) # Recommends filter @@ -123,38 +123,37 @@ class AllCveTable(ToasterTable): Cve.objects.all() # filter out hidden records - userAccess = Access() + userAccess = Access(self.request.session.get('srt_user_id', '0')) if not userAccess.is_admin(): self.queryset = self.queryset.exclude(public = False) self.queryset = self.queryset.order_by(self.default_orderby) -# self.static_context_extra['in_prj'] = ProjectLayer.objects.filter(Q(project=kwargs['pid']) & Q(layercommit=kwargs['layerid'])).count() + def setup_columns(self, *args, **kwargs): id_link_template = ''' - <a href="{% url 'cve' data.id %}"> - {{data.name}} - </a> + <a href="{% url 'cve' data.id %}" id="dataid_{{data.id}}">{{data.name}}</a> ''' - self.add_column(title="ID", + self.add_column(title="Name", hideable=False, orderable=True, field_name="name", static_data_name="name", - static_data_template=id_link_template) + static_data_template=id_link_template, + ) self.add_column(title="Status", field_name="status", hideable=True, orderable=True, - filter_name="is_status", +# filter_name="is_status", static_data_name="status", static_data_template="{{data.get_status_text}}" ) score_link_template = ''' - {% if 0 == data.recommend %}0{% else %}{{data.recommend}}{% endif %} + {% if 0 == data.recommend %}0{% else %}{{data.recommend}}{% endif %} ''' self.add_column(title="Score", field_name="recommend", @@ -189,17 +188,18 @@ class AllCveTable(ToasterTable): ) severity_v3_template = ''' - {{data.cvssV3_baseScore}} {{data.cvssV3_baseSeverity}} + {{data.cvssV3_baseScore}} {{data.cvssV3_baseSeverity}} ''' self.add_column(title="Severity (V3)", help_text="Severity of the CVE (v3)", hideable=False, orderable=False, static_data_name='severity_v3', - static_data_template=severity_v3_template) + static_data_template=severity_v3_template, + ) severity_v2_template = ''' - {{data.cvssV2_baseScore}} {{data.cvssV2_severity}} + {{data.cvssV2_baseScore}} {{data.cvssV2_severity}} ''' self.add_column(title="Severity (V2)", help_text="Severity of the CVE (v2)", @@ -207,19 +207,22 @@ class AllCveTable(ToasterTable): hidden=True, orderable=False, static_data_name='severity_v2', - static_data_template=severity_v2_template) + static_data_template=severity_v2_template, + ) self.add_column(title="Published", help_text="Initial publish date of the CVE", hideable=False, orderable=True, - field_name="publishedDate") + field_name="publishedDate", + ) self.add_column(title="Modified", help_text="Last modification date of the CVE", hideable=True, orderable=True, - field_name="lastModifiedDate") + field_name="lastModifiedDate", + ) self.add_column(title="Comments", field_name="comments", @@ -227,7 +230,25 @@ class AllCveTable(ToasterTable): orderable=True, ) - userAccess = Access() + self.add_column(title="Publish Request", + help_text="SRT Publish Request State", + hideable=True, + hidden=True, + orderable=True, + field_name="publish_state", + static_data_name="publish_state", + static_data_template='{{data.get_publish_text}}', + ) + + self.add_column(title="Publish Date", + help_text="SRT Publish date of the CVE", + hideable=True, + hidden=True, + orderable=True, + field_name="publish_date" + ) + + userAccess = Access(self.request.session.get('srt_user_id', '0')) if userAccess.is_admin(): self.add_column(title="Comments Private", field_name="comments_private", @@ -235,27 +256,32 @@ class AllCveTable(ToasterTable): ) vulnerability_link_template = ''' - {% if data.cve_to_vulnerability.all %} - {% for cv in data.cve_to_vulnerability.all %} - {% if not forloop.first %}| {% endif %}<a href="{% url 'vulnerability' cv.vulnerability.pk %}">{{cv.vulnerability.name}}</a> - {% endfor %} - {% endif %} + {% for cv in data.cve_to_vulnerability.all %} + {% if not forloop.first %}| {% endif %}<a href="{% url 'vulnerability' cv.vulnerability.pk %}">{{cv.vulnerability.name}}</a> + {% endfor %} ''' self.add_column(title="Vulnerability", static_data_name="vulnerability", static_data_template=vulnerability_link_template, - hidden=False) - - if False: - defect_template = ''' +# static_data_template='', + hidden=False, + ) - ''' - self.add_column(title="Defect", - help_text="Associated Defects", - hideable=True, - orderable=True, - static_data_name='defect', - static_data_template=defect_template) + defect_link_template = ''' + {% for cv in data.cve_to_vulnerability.all %} + {% for investigation in cv.vulnerability.vulnerability_investigation.all %} + {% for id in investigation.investigation_to_defect.all %} + {% if not forloop.first %}| {% endif %}<a href="{% url 'defect' id.defect.pk %}">{{id.defect.name}}</a> + {% endfor %} + {% endfor %} + {% endfor %} + ''' + self.add_column(title="Defect", + static_data_name="defect", + static_data_template=defect_link_template, +# static_data_template='', + hidden=False, + ) class SelectCveTable(ToasterTable): @@ -274,27 +300,15 @@ class SelectCveTable(ToasterTable): data = super(SelectCveTable, self).apply_row_customization(data) # data:dict_keys(['rows', 'total', 'default_orderby', 'error', 'columns']) - if False: - # TEST - KeywordsFor = SrtSetting.objects.get(name='keywords_for').value.split('|') - KeywordsAgainst = SrtSetting.objects.get(name='keywords_against').value.split('|') - qq = 0 - for i in range(len(data['rows'])): - data['rows'][i]['for'] = 'Y:%s%d' % (KeywordsFor[qq],i) - data['rows'][i]['against'] = 'N:%s%d' % (KeywordsAgainst[qq],i) - qq += 1 - if 10<qq: - qq = 0 - - if True: - for i in range(len(data['rows'])): - data['rows'][i]['for'] = '' - data['rows'][i]['against'] = '' - for key in data['rows'][i]['comments_private'].split(','): - if key.startswith('+'): - data['rows'][i]['for'] += '%s ' % key[1:] - elif key.startswith('-'): - data['rows'][i]['against'] += '%s ' % key[1:] + # comments_private -> recommend_list + for i in range(len(data['rows'])): + data['rows'][i]['for'] = '' + data['rows'][i]['against'] = '' + for key in data['rows'][i]['recommend_list'].split(','): + if key.startswith('+'): + data['rows'][i]['for'] += '%s ' % key[1:] + elif key.startswith('-'): + data['rows'][i]['against'] += '%s ' % key[1:] return data @@ -365,14 +379,12 @@ class SelectCveTable(ToasterTable): Cve.objects.filter(status = Cve.NEW,name__startswith = 'CVE-2018') # filter out hidden records !!! ALL NEW ONES SHOULD BE PUBLIC -# userAccess = Access() +# userAccess = Access(self.request.session.get('srt_user_id', '0')) # if not userAccess.is_admin(): # self.queryset = self.queryset.exclude(public = False) self.queryset = self.queryset.order_by(self.default_orderby) -# self.static_context_extra['in_prj'] = ProjectLayer.objects.filter(Q(project=kwargs['pid']) & Q(layercommit=kwargs['layerid'])).count() - def setup_columns(self, *args, **kwargs): @@ -380,7 +392,7 @@ class SelectCveTable(ToasterTable): field_name="Select", hideable=False, static_data_name="select", - static_data_template='<input type="checkbox" name="{{data.name}}" />' + static_data_template='<input type="checkbox" name="{{data.name}}" />', ) self.add_column(title="Status", @@ -389,18 +401,17 @@ class SelectCveTable(ToasterTable): orderable=True, filter_name="is_status", static_data_name="status", - static_data_template="{{data.get_status_text}}" + static_data_template="{{data.get_status_text}}", ) - self.add_column(title="Comments", - field_name="comments_private", + self.add_column(title="Recommend List", + field_name="recommend_list", hideable=True, hidden=True, ) -# {% if 0 == data.recommend %}0{% else %}{{data.recommend}}{% endif %} recommend_link_template = ''' - {% load projecttags %}{{data.recommend|recommend_display}} + {% load projecttags %}{{data.recommend|recommend_display}} ''' self.add_column(title="Recommendation", hideable=False, @@ -412,16 +423,15 @@ class SelectCveTable(ToasterTable): ) id_link_template = ''' - <a href="{% url 'cve' data.id %}" target="_blank"> - {{data.name}} - </a> + <a href="{% url 'cve' data.id %}" id="dataid_{{data.id}}" target="_blank">{{data.name}}</a> ''' self.add_column(title="Name", hideable=False, orderable=True, field_name="name", static_data_name="name", - static_data_template=id_link_template) + static_data_template=id_link_template, + ) self.add_column(title="Description", field_name="description", @@ -429,17 +439,18 @@ class SelectCveTable(ToasterTable): ) severity_v3_template = ''' - {{data.cvssV3_baseScore}} {{data.cvssV3_baseSeverity}} + {{data.cvssV3_baseScore}} {{data.cvssV3_baseSeverity}} ''' self.add_column(title="Severity (V3)", help_text="Severity of the CVE (v3)", hideable=False, orderable=False, static_data_name='severity_v3', - static_data_template=severity_v3_template) + static_data_template=severity_v3_template, + ) severity_v2_template = ''' - {{data.cvssV2_baseScore}} {{data.cvssV2_severity}} + {{data.cvssV2_baseScore}} {{data.cvssV2_severity}} ''' self.add_column(title="Severity (V2)", help_text="Severity of the CVE (v2)", @@ -447,15 +458,11 @@ class SelectCveTable(ToasterTable): hidden=True, orderable=False, static_data_name='severity_v2', - static_data_template=severity_v2_template) - - -#{{keyscore|get_dict_value:data.name}} -#{{keysfor|get_dict_value:data.name}} -#{{keysagainst|get_dict_value:data.name}} + static_data_template=severity_v2_template, + ) for_template = ''' - {{keysfor|get_dict_value:data.name}} + {{keysfor|get_dict_value:data.name}} ''' self.add_column(title="Reasons For", help_text="Keywords for accepting this CVE", @@ -466,7 +473,7 @@ class SelectCveTable(ToasterTable): ) against_template = ''' - {{keysagainst|get_dict_value:data.name}} + {{keysagainst|get_dict_value:data.name}} ''' self.add_column(title="Reasons Against", help_text="Keywords for not accepting this CVE", @@ -477,15 +484,15 @@ class SelectCveTable(ToasterTable): ) -class AllDefectTable(ToasterTable): - """Table of All Defects's in SRTool""" +class DefectsTable(ToasterTable): + """Table of All Defects in SRTool""" def __init__(self, *args, **kwargs): - super(AllDefectTable, self).__init__(*args, **kwargs) + super(DefectsTable, self).__init__(*args, **kwargs) self.default_orderby = "name" def get_context_data(self, **kwargs): - context = super(AllDefectTable, self).get_context_data(**kwargs) + context = super(DefectsTable, self).get_context_data(**kwargs) return context @@ -493,102 +500,47 @@ class AllDefectTable(ToasterTable): # Priority filter is_priority = TableFilter(name="is_priority", title="Filter defects by 'Priority'") - exec_is_low = TableFilterActionToggle( - "low", - "Low", - Q(priority=Defect.LOW)) - exec_is_medium = TableFilterActionToggle( - "medium", - "Medium", - Q(priority=Defect.MEDIUM)) - exec_is_high = TableFilterActionToggle( - "high", - "High", - Q(priority=Defect.HIGH)) - is_priority.add_action(exec_is_low) - is_priority.add_action(exec_is_medium) - is_priority.add_action(exec_is_high) + for priority in range(len(Defect.Priority)): + is_priority.add_action(TableFilterActionToggle( + Defect.Priority[priority][1].lower().replace(' ','_'), + Defect.Priority[priority][1], + Q(resolution=Defect.Priority[priority][0])) + ) self.add_filter(is_priority) # Status filter is_status = TableFilter(name="is_status", title="Filter defects by 'Status'") - exec_is_open = TableFilterActionToggle( - "open", - "Open", - Q(status=Defect.OPEN)) - exec_is_in_progress = TableFilterActionToggle( - "in_progress", - "In progress", - Q(status=Defect.IN_PROGRESS)) - exec_is_on_hold = TableFilterActionToggle( - "on_hold", - "On Hold", - Q(status=Defect.ON_HOLD)) - exec_is_checked_in = TableFilterActionToggle( - "checked_in", - "Checked In", - Q(status=Defect.CHECKED_IN)) - exec_is_resolved = TableFilterActionToggle( - "resolved", - "Resolved", - Q(status=Defect.RESOLVED)) - exec_is_closed = TableFilterActionToggle( - "closed", - "Closed", - Q(status=Defect.CLOSED)) - is_status.add_action(exec_is_open) - is_status.add_action(exec_is_in_progress) - is_status.add_action(exec_is_on_hold) - is_status.add_action(exec_is_checked_in) - is_status.add_action(exec_is_resolved) - is_status.add_action(exec_is_closed) + for status in range(len(Defect.Status)): + is_status.add_action(TableFilterActionToggle( + Defect.Status[status][1].lower().replace(' ','_'), + Defect.Status[status][1], + Q(resolution=Defect.Status[status][0])) + ) self.add_filter(is_status) # Resolution filter is_resolution = TableFilter(name="is_resolution", title="Filter defects by 'Resolution'") - exec_is_unresolved = TableFilterActionToggle( - "unresolved", - "Unresolved", - Q(resolution=Defect.UNRESOLVED)) - exec_is_fixed = TableFilterActionToggle( - "fixed", - "Fixed", - Q(resolution=Defect.FIXED)) - exec_is_will_not_fix = TableFilterActionToggle( - "will_not_fix", - "Will Not Fix", - Q(resolution=Defect.WILL_NOT_FIX)) - exec_is_withdrawn = TableFilterActionToggle( - "withdrawn", - "Withdrawn", - Q(resolution=Defect.WITHDRAWN)) - exec_is_rejected = TableFilterActionToggle( - "rejected", - "Rejected", - Q(resolution=Defect.REJECTED)) - exec_is_duplicate = TableFilterActionToggle( - "duplicate", - "Duplicate", - Q(resolution=Defect.DUPLICATE)) - is_resolution.add_action(exec_is_unresolved) - is_resolution.add_action(exec_is_fixed) - is_resolution.add_action(exec_is_will_not_fix) - is_resolution.add_action(exec_is_withdrawn) - is_resolution.add_action(exec_is_rejected) - is_resolution.add_action(exec_is_duplicate) + for resolution in range(len(Defect.Resolution)): + is_resolution.add_action(TableFilterActionToggle( + Defect.Resolution[resolution][1].lower().replace(' ','_'), + Defect.Resolution[resolution][1], + Q(resolution=Defect.Resolution[resolution][0])) + ) self.add_filter(is_resolution) # Product filter #(name="Wind River Linux",version="LTS-17") is_product = TableFilter(name="is_product", title="Filter defects by 'Product'") - for p in Product.objects.all(): + for product in Product.objects.all(): + _log("PRODUCTS:%s,%s"% (product.defect_prefix,product.long_name)) is_product.add_action( TableFilterActionToggle( - p.defect_prefix, - p.long_name(), - Q(product=p)) ) + product.defect_prefix, + product.long_name, + Q(product=product)) + ) self.add_filter(is_product) @@ -601,16 +553,15 @@ class AllDefectTable(ToasterTable): def setup_columns(self, *args, **kwargs): name_link_template = ''' - <a href="http://defect.wrs.com/browse/{{data.name}}" target="_blank"> - {{data.name}} - </a> + <a href="{% url 'defect' data.id %}" id="dataid_{{data.id}}">{{data.name}}</a> ''' self.add_column(title="Name", hideable=False, orderable=True, field_name="name", static_data_name="name", - static_data_template=name_link_template) + static_data_template=name_link_template, + ) self.add_column(title="Summary", field_name="summary", @@ -622,25 +573,25 @@ class AllDefectTable(ToasterTable): orderable=True, filter_name="is_priority", static_data_name="priority", - static_data_template='{{data.get_priority_text}}' + static_data_template='{{data.get_priority_text}}', ) - + self.add_column(title="Status", hideable=False, field_name="status", orderable=True, filter_name="is_status", static_data_name="status", - static_data_template='{{data.get_status_text}}' + static_data_template='{{data.get_status_text}}', ) - - self.add_column(title="resolution", + + self.add_column(title="Resolution", hideable=False, field_name="resolution", orderable=True, filter_name="is_resolution", static_data_name="resolution", - static_data_template='{{data.get_resolution_text}}' + static_data_template='{{data.get_resolution_text}}', ) self.add_column(title="Release Version", @@ -649,53 +600,64 @@ class AllDefectTable(ToasterTable): field_name="release_version", ) - self.add_column(title="Publish OLS", + self.add_column(title="Publish", hideable=True, orderable=True, - field_name="publishOLS", + field_name="publish", + ) + + url_link_template = ''' + <a href="{{data.url}}" target="_blank">{{data.url}}</a> + ''' + self.add_column(title="URL", + field_name="url", + hideable=True, + hidden=True, + static_data_name="url", + static_data_template=url_link_template, ) #date_created = models.DateField(null=True, blank=True) #date_updated = models.DateField(null=True, blank=True) investigations_link_template = ''' - {% for ji in data.defect_to_investigation.all %} - {% if not forloop.first %}| {% endif %}<a href="{% url 'investigation' ji.investigation.id %}" target="_blank">{{ji.investigation.name}} </a> - {% endfor %} + {% for ji in data.defect_to_investigation.all %} + {% if not forloop.first %}| {% endif %}<a href="{% url 'investigation' ji.investigation.id %}" target="_blank">{{ji.investigation.name}} </a> + {% endfor %} ''' self.add_column(title="Investigation", hideable=True, # orderable=True, # multiple investigations static_data_name="investigation", - static_data_template=investigations_link_template + static_data_template=investigations_link_template, ) # !!! HACK: 'data.product' is returning '%s' when it is supposed to be null !!! product_link_template = ''' - {% if data.product != '%s' %} - <a href="{% url 'products'%}"> - {{data.product.long_name}} - </a> - {% endif %} + {% if data.product != '%s' %} + <a href="{% url 'product' data.product.id %}"> + {{data.product.long_name}} + </a> + {% endif %} ''' self.add_column(title="Product", hideable=True, orderable=True, filter_name="is_product", static_data_name="product", - static_data_template=product_link_template + static_data_template=product_link_template, ) -class AllCweTable(ToasterTable): +class CwesTable(ToasterTable): """Table of All CWE's in SRTool""" def __init__(self, *args, **kwargs): - super(AllCweTable, self).__init__(*args, **kwargs) + super(CwesTable, self).__init__(*args, **kwargs) self.default_orderby = "name_sort" def get_context_data(self, **kwargs): - context = super(AllCweTable, self).get_context_data(**kwargs) + context = super(CwesTable, self).get_context_data(**kwargs) return context @@ -719,28 +681,22 @@ class AllCweTable(ToasterTable): def setup_columns(self, *args, **kwargs): - name_link_template = ''' - <a href="cwelink_{{data.name}}"></a> - {{data.name}} - ''' self.add_column(title="Name", + field_name="name", hideable=False, orderable=True, - field_name="name_sort", - static_data_name="name_sort", - static_data_template=name_link_template) + ) href_link_template = ''' - <a href="{{data.href}}" target="_blank"> - {{data.href}} - </a> + <a href="{{data.href}}" id="dataid_{{data.id}} target="_blank">{{data.href}}</a> ''' self.add_column(title="Link", hideable=False, orderable=False, field_name="href", static_data_name="href", - static_data_template=href_link_template) + static_data_template=href_link_template, + ) self.add_column(title="Summary", field_name="summary", @@ -758,23 +714,23 @@ class AllCweTable(ToasterTable): static_data_name="cves", static_data_template='{{data.vulnerable_count}}', ) - + # self.add_column(title="CVE's", # hidden=False, # static_data_name="cves", # static_data_template='{{data.cwe2cve.all.count}}', # ) - -class AllCpeTable(ToasterTable): + +class CpesTable(ToasterTable): """Table of All CPE's in SRTool""" def __init__(self, *args, **kwargs): - super(AllCpeTable, self).__init__(*args, **kwargs) + super(CpesTable, self).__init__(*args, **kwargs) self.default_orderby = "vulnerable" def get_context_data(self, **kwargs): - context = super(AllCpeTable, self).get_context_data(**kwargs) + context = super(CpesTable, self).get_context_data(**kwargs) return context def setup_filters(self, *args, **kwargs): @@ -808,20 +764,13 @@ class AllCpeTable(ToasterTable): # cpeMatchString = models.TextField(blank=True) # cpe23Uri = models.TextField(blank=True) - vulnerable_link_template = ''' - {% if data.vulnerable %} - Yes - {% else %} - No - {% endif %} - ''' self.add_column(title="Vulnerable", field_name="vulnerable", hideable=False, orderable=True, filter_name="is_vulnerable", static_data_name="vulnerable", - static_data_template=vulnerable_link_template + static_data_template="{% if data.vulnerable %}Yes{% else %}No{% endif %}", ) self.add_column(title="CPE 2.3", @@ -845,16 +794,17 @@ class AllCpeTable(ToasterTable): ) cve_link_template = ''' - {% for pv in data.cpe2cve.all %} - {% if not forloop.first %}| {% endif %}<a href="{% url 'cve' pv.cve.id %}">{{pv.cve.name}} </a> - {% endfor %} + {% for pv in data.cpe2cve.all %} + {% if not forloop.first %}| {% endif %}<a href="{% url 'cve' pv.cve.id %}">{{pv.cve.name}} </a> + {% endfor %} ''' self.add_column(title="CVE", hideable=False, orderable=True, field_name="cveName", static_data_name="cveName", - static_data_template=cve_link_template) + static_data_template=cve_link_template, + ) class ManageCpeTable(ToasterTable): @@ -918,7 +868,7 @@ class ManageCpeTable(ToasterTable): field_name="Select", hideable=False, static_data_name="select", - static_data_template='<input type="checkbox" name="check_{{data.key_prime}}_{{data.key_sub}}" />' + static_data_template='<input type="checkbox" name="check_{{data.key_prime}}_{{data.key_sub}}" />', ) @@ -928,7 +878,7 @@ class ManageCpeTable(ToasterTable): orderable=True, filter_name="is_status", static_data_name="status", - static_data_template="{{data.get_status_text}}" + static_data_template="{{data.get_status_text}}", ) self.add_column(title="Company", @@ -943,22 +893,15 @@ class ManageCpeTable(ToasterTable): hideable=False, orderable=True, static_data_name="key_sub", - static_data_template="{% if data.key_sub %}{{data.key_sub}}{% else %}(company){% endif %}" + static_data_template="{% if data.key_sub %}{{data.key_sub}}{% else %}(company){% endif %}", ) - automatic_link_template = ''' - {% if data.automatic %} - Yes - {% else %} - No - {% endif %} - ''' self.add_column(title="Automatic", field_name="automatic", hideable=False, orderable=True, static_data_name="automatic", - static_data_template=automatic_link_template + static_data_template="{% if data.automatic %}Yes{% else %}No{% endif %}", ) manage_link_template = ''' @@ -970,7 +913,7 @@ class ManageCpeTable(ToasterTable): hideable=False, orderable=True, static_data_name="manage", - static_data_template=manage_link_template + static_data_template=manage_link_template, ) @@ -1015,14 +958,15 @@ class ProductsTable(ToasterTable): def setup_columns(self, *args, **kwargs): name_link_template = ''' - {{data.name}} + <a href="{% url 'product' data.id %}" id="dataid_{{data.id}}">{{data.name}}<a> ''' self.add_column(title="Name", hideable=False, orderable=True, field_name="name", static_data_name="name", - static_data_template=name_link_template) + static_data_template=name_link_template, + ) self.add_column(title="Version", field_name="version", @@ -1042,8 +986,8 @@ class ProductsTable(ToasterTable): self.add_column(title="SRT CPE", field_name="srt_cpe", - hideable=False, - ) + hideable=True, + ) self.add_column(title="Defect Prefix", field_name="defect_prefix", @@ -1052,25 +996,25 @@ class ProductsTable(ToasterTable): investigations_link_template = ''' - {% if data.product_investigation.all.count %} - <a href="{% url 'investigations' %}?filter=is_product:{{data.defect_prefix}}&"> - {{data.product_investigation.all.count}} - </a> - {% else %}0{% endif %} + {% if data.product_investigation.all.count %} + <a href="{% url 'investigations' %}?filter=is_product:{{data.defect_prefix}}&"> + {{data.product_investigation.all.count}} + </a> + {% else %}0{% endif %} ''' self.add_column(title="Investigations", field_name="investigations", hidden=False, static_data_name="investigations", - static_data_template=investigations_link_template + static_data_template=investigations_link_template, ) defects_link_template = ''' - {% if data.product_defect.all.count %} - <a href="{% url 'all-defects' %}?filter=is_product:{{data.defect_prefix}}&"> - {{data.product_defect.all.count}} - </a> - {% else %}0{% endif %} + {% if data.product_defect.all.count %} + <a href="{% url 'defects' %}?filter=is_product:{{data.defect_prefix}}&"> + {{data.product_defect.all.count}} + </a> + {% else %}0{% endif %} ''' self.add_column(title="Defects", field_name="defects", @@ -1080,15 +1024,15 @@ class ProductsTable(ToasterTable): ) -class AllVulnerabilitiesTable(ToasterTable): +class VulnerabilitiesTable(ToasterTable): """Table of All Vulnerabilities in SRTool""" def __init__(self, *args, **kwargs): - super(AllVulnerabilitiesTable, self).__init__(*args, **kwargs) + super(VulnerabilitiesTable, self).__init__(*args, **kwargs) self.default_orderby = "name" def get_context_data(self, **kwargs): - context = super(AllVulnerabilitiesTable, self).get_context_data(**kwargs) + context = super(VulnerabilitiesTable, self).get_context_data(**kwargs) return context @@ -1163,7 +1107,7 @@ class AllVulnerabilitiesTable(ToasterTable): Vulnerability.objects.all() # filter out hidden records - userAccess = Access() + userAccess = Access(self.request.session.get('srt_user_id', '0')) if not userAccess.is_admin(): self.queryset = self.queryset.exclude(public = False) @@ -1172,37 +1116,37 @@ class AllVulnerabilitiesTable(ToasterTable): def setup_columns(self, *args, **kwargs): id_link_template = ''' - <a href="{% url 'vulnerability' data.id %}"> - {{data.name}} - </a> + <a href="{% url 'vulnerability' data.id %}" id="dataid_{{data.id}}">{{data.name}}</a> ''' self.add_column(title="ID", hideable=False, orderable=True, field_name="name", static_data_name="name", - static_data_template=id_link_template) + static_data_template=id_link_template, + ) cve_link_template = ''' - {% for vc in data.vulnerability_to_cve.all %} - {% if not forloop.first %}| {% endif %} <a href="{% url 'cve' vc.cve.pk %}"> - {{vc.cve.name}} - </a> - {% endfor %} + {% for vc in data.vulnerability_to_cve.all %} + {% if not forloop.first %}| {% endif %} <a href="{% url 'cve' vc.cve.pk %}"> + {{vc.cve.name}} + </a> + {% endfor %} ''' self.add_column(title="CVE", hideable=False, orderable=False, field_name="cve__name", static_data_name="cve__name", - static_data_template=cve_link_template) + static_data_template=cve_link_template, + ) self.add_column(title="Status", field_name="status", hideable=False, filter_name="is_status", static_data_name="status", - static_data_template="{{data.get_status_text}}" + static_data_template="{{data.get_status_text}}", ) self.add_column(title="Outcome", @@ -1210,7 +1154,7 @@ class AllVulnerabilitiesTable(ToasterTable): hideable=False, filter_name="is_outcome", static_data_name="outcome", - static_data_template="{{data.get_outcome_text}}" + static_data_template="{{data.get_outcome_text}}", ) self.add_column(title="Severity", @@ -1218,7 +1162,7 @@ class AllVulnerabilitiesTable(ToasterTable): hideable=False, filter_name="is_severity", static_data_name="severity", - static_data_template="{{data.get_severity_text}}" + static_data_template="{{data.get_severity_text}}", ) self.add_column(title="Comments", @@ -1226,62 +1170,82 @@ class AllVulnerabilitiesTable(ToasterTable): hideable=True, ) - userAccess = Access() + userAccess = Access(self.request.session.get('srt_user_id', '0')) if userAccess.is_admin(): self.add_column(title="Comments Private", field_name="comments_private", hideable=True, ) + investigate_link_template = ''' + {% for investigation in data.vulnerability_investigation.all %} + {% if not forloop.first %}| {% endif %}<a href="{% url 'investigation' investigation.id %}" target="_blank">{{investigation.name}}</a> + {% endfor %} + ''' + self.add_column(title="Investigations", + static_data_name="vulnerability_investigation", + static_data_template=investigate_link_template, + hidden=False, + ) + + defect_link_template = ''' + {% for investigation in data.vulnerability_investigation.all %} + {% for id in investigation.investigation_to_defect.all %} + {% if forloop.counter == 1 %}| {% endif %}<a href="{% url 'defect' id.defect.id %}" target="_blank">{{id.defect.name}}</a> + {% endfor %} + {% endfor %} + ''' + self.add_column(title="Defects", + static_data_name="investigation_to_defect", + static_data_template=defect_link_template, + hidden=False, + ) + + # VulnerabilityProduct.AFFECTED = 0 product_link_template = ''' {% for vp in data.affected_products.all %} - {% if vp.relation = 0 %}{% if not forloop.first %}| {% endif %}<a href="{% url 'products'%}" target="_blank">{{vp.product.defect_prefix}}</a>{% endif %} - {% endfor %} + {% if vp.product != '%s' %} + {% if vp.relation == 0 %}{% if not forloop.first %}| {% endif %}<a href="{% url 'product' vp.product.id %}" target="_blank">{{vp.product.defect_prefix}}</a>{% endif %} + {% endfor %} + {% endif %} ''' self.add_column(title="Products", static_data_name="investigation_products", static_data_template=product_link_template, hidden=False, ) - if False: - self.add_column(title="Defects", - static_data_name="investigation_defects", - static_data_template='\ - 0', - hidden=False, - ) -class AllInvestigationsTable(ToasterTable): +class InvestigationsTable(ToasterTable): """Table of All Investigations in SRTool""" def __init__(self, *args, **kwargs): - super(AllInvestigationsTable, self).__init__(*args, **kwargs) + super(InvestigationsTable, self).__init__(*args, **kwargs) self.default_orderby = "name" def get_context_data(self, **kwargs): - context = super(AllInvestigationsTable, self).get_context_data(**kwargs) + context = super(InvestigationsTable, self).get_context_data(**kwargs) return context def setup_filters(self, *args, **kwargs): # Status filter is_status = TableFilter(name="is_status", title="Filter Investigations by 'Status'") - exec_is_investigate = TableFilterActionToggle( + is_status.add_action(TableFilterActionToggle( "investigate", "Investigate", Q(status=Investigation.INVESTIGATE)) - exec_is_vulnerable = TableFilterActionToggle( + ) + is_status.add_action(TableFilterActionToggle( "vulnerable", "Is Vulnerable", Q(status=Investigation.VULNERABLE)) - exec_is_not_vulnerable = TableFilterActionToggle( + ) + is_status.add_action(TableFilterActionToggle( "not_vulnerable", "Not Vulnerable", Q(status=Investigation.NOT_VULNERABLE)) - is_status.add_action(exec_is_investigate) - is_status.add_action(exec_is_vulnerable) - is_status.add_action(exec_is_not_vulnerable) + ) self.add_filter(is_status) # Outcome filter @@ -1335,7 +1299,7 @@ class AllInvestigationsTable(ToasterTable): for p in Product.objects.all(): is_product.add_action( TableFilterActionToggle( p.defect_prefix, - p.long_name(), + p.long_name, Q(product=p)) ) self.add_filter(is_product) @@ -1345,7 +1309,7 @@ class AllInvestigationsTable(ToasterTable): Investigation.objects.all() # filter out hidden records - userAccess = Access() + userAccess = Access(self.request.session.get('srt_user_id', '0')) if not userAccess.is_admin(): self.queryset = self.queryset.exclude(public = False) @@ -1354,27 +1318,26 @@ class AllInvestigationsTable(ToasterTable): def setup_columns(self, *args, **kwargs): id_link_template = ''' - <a href="{% url 'investigation' data.id %}"> - {{data.name}} - </a> + <a href="{% url 'investigation' data.id %}" id="dataid_{{data.id}}">{{data.name}}</a> ''' self.add_column(title="ID", hideable=False, orderable=True, field_name="name", static_data_name="name", - static_data_template=id_link_template) + static_data_template=id_link_template, + ) defect_link_template = ''' {% for ij in data.investigation_to_defect.all %} - {% if not forloop.first %}| {% endif %}<a href="http://defect.wrs.com/browse/{{ij.defect.name}}" target="_blank">{{ij.defect.name}} </a> + {% if not forloop.first %}| {% endif %}<a href="{% url 'defect' ij.defect.id %}">{{ij.defect.name}} </a> {% endfor %} ''' self.add_column(title="Defects", field_name="defect", hideable=False, static_data_name="defect", - static_data_template=defect_link_template + static_data_template=defect_link_template, ) self.add_column(title="Status", @@ -1382,7 +1345,7 @@ class AllInvestigationsTable(ToasterTable): hideable=True, filter_name="is_status", static_data_name="status", - static_data_template="{{data.get_status_text}}" + static_data_template="{{data.get_status_text}}", ) self.add_column(title="Outcome", @@ -1390,15 +1353,20 @@ class AllInvestigationsTable(ToasterTable): hideable=False, filter_name="is_outcome", static_data_name="outcome", - static_data_template="{{data.get_outcome_text}}" + static_data_template="{{data.get_outcome_text}}", ) + release_version_template = ''' + {% for ij in data.investigation_to_defect.all %} + {% if not forloop.first %}| {% endif %}<a href="{{ij.defect.url}}" target="_blank">{{ij.defect.release_version}} </a> + {% endfor %} + ''' self.add_column(title="Release Version", # field_name="release_version", orderable=True, hideable=False, - static_data_name="outcome", - static_data_template="{{data.defect.release_version}}" + static_data_name="release_version", + static_data_template=release_version_template, ) self.add_column(title="Severity", @@ -1406,7 +1374,7 @@ class AllInvestigationsTable(ToasterTable): filter_name="is_severity", hideable=False, static_data_name="severity", - static_data_template="{{data.get_severity_text}}" + static_data_template="{{data.get_severity_text}}", ) self.add_column(title="Comments", @@ -1414,7 +1382,7 @@ class AllInvestigationsTable(ToasterTable): hideable=True, ) - userAccess = Access() + userAccess = Access(self.request.session.get('srt_user_id', '0')) if userAccess.is_admin(): self.add_column(title="Comments Private", field_name="comments_private", @@ -1432,7 +1400,7 @@ class AllInvestigationsTable(ToasterTable): # orderable=True, filter_name="is_product", static_data_name="investigation_products", - static_data_template="<a href=\"{% url 'products' %}\">{{data.product.long_name}}</a>", + static_data_template="<a href=\"{% url 'product' data.product.id %}\">{{data.product.long_name}}</a>", ) @@ -1468,7 +1436,7 @@ class KeywordsTable(ToasterTable): Keywords.objects.all() # # filter out hidden records -# userAccess = Access() +# userAccess = Access(self.request.session.get('srt_user_id', '0')) # if not userAccess.is_admin(): # self.queryset = self.queryset.exclude(public = False) @@ -1504,5 +1472,278 @@ class KeywordsTable(ToasterTable): hideable=False, orderable=False, static_data_name="manage", - static_data_template=manage_link_template) + static_data_template=manage_link_template, + ) + + +class SourcesTable(ToasterTable): + """Table of All Data Sources in SRTool""" + + def __init__(self, *args, **kwargs): + super(SourcesTable, self).__init__(*args, **kwargs) + self.default_orderby = "data" + + def get_context_data(self, **kwargs): + context = super(SourcesTable, self).get_context_data(**kwargs) + return context + + def setup_queryset(self, *args, **kwargs): + self.queryset = \ + DataSource.objects.all() + + self.queryset = self.queryset.order_by(self.default_orderby) + + def setup_columns(self, *args, **kwargs): + + self.add_column(title="Data", + hideable=False, + orderable=True, + field_name="data", + static_data_name="data", + static_data_template='<span id="dataid_{{data.id}}">{{data.data}}</span>', + ) + + self.add_column(title="Source", + hideable=False, + orderable=True, + field_name="source", + ) + + self.add_column(title="Type", + hideable=False, + orderable=True, + field_name="type", + ) + + self.add_column(title="Description", + hideable=False, + orderable=False, + field_name="description", + ) + + self.add_column(title="File Path", + hideable=True, + hidden=True, + orderable=False, + field_name="file_path", + ) + + self.add_column(title="URL", + hideable=True, + hidden=True, + orderable=False, + field_name="url", + ) + + self.add_column(title="Meta URL", + hideable=True, + hidden=True, + orderable=False, + field_name="meta_url", + ) + + self.add_column(title="Data Modified", + hideable=False, + orderable=True, + field_name="lastModifiedDate", + ) + + self.add_column(title="Updates", + hideable=False, + orderable=True, + field_name="update_frequency", + static_data_name="update_frequency", + static_data_template="{{data.get_frequency_text}}", + ) + + self.add_column(title="Updated", + hideable=False, + orderable=True, + field_name="update_time", + ) + + self.add_column(title="Command", + hideable=True, + orderable=False, + field_name="command", + ) + + +class SelectPublishTable(ToasterTable): + """Table of Publishable CVE's in SRTool""" + + def __init__(self, *args, **kwargs): + super(SelectPublishTable, self).__init__(*args, **kwargs) + self.default_orderby = "name" + _log("SelectPublishTable:__init") + + def get_context_data(self,**kwargs): + _log("SelectPublishTable:get_context_data") + context = super(SelectPublishTable, self).get_context_data(**kwargs) + return context + + def setup_filters(self, *args, **kwargs): + _log("SelectPublishTable:setup_filters") + # Status filter + is_status = TableFilter(name="is_status", + title="Filter CVE's by 'Status") + is_status.add_action(TableFilterActionToggle( + "new", + "New", + Q(status=Cve.NEW)) + ) + is_status.add_action(TableFilterActionToggle( + "investigate", + "Investigate", + Q(status=Cve.INVESTIGATE)) + ) + is_status.add_action(TableFilterActionToggle( + "vulnerable", + "Is Vulnerable", + Q(status=Cve.VULNERABLE)) + ) + is_status.add_action(TableFilterActionToggle( + "not_vulnerable", + "Not Vulnerable", + Q(status=Cve.NOT_VULNERABLE)) + ) + self.add_filter(is_status) + + def setup_queryset(self, *args, **kwargs): + _log("SelectPublishTable:setup_queryset") + self.queryset = \ + Cve.objects.filter(publish_state = Cve.PUBLISH_REQUEST) | \ + Cve.objects.filter(publish_state = Cve.PUBLISH_UPDATE) + + _log("SelectPublishTable1:%s" % len(self.queryset)) + + # filter out hidden records !!! ALL NEW ONES SHOULD BE PUBLIC +# userAccess = Access(self.request.session.get('srt_user_id', '0')) +# if not userAccess.is_admin(): +# self.queryset = self.queryset.exclude(public = False) + + self.queryset = self.queryset.order_by(self.default_orderby) + + def setup_columns(self, *args, **kwargs): + + _log("SelectPublishTable:setup_columns") + + self.add_column(title="Select", + field_name="Select", + hideable=False, + static_data_name="select", + static_data_template='<input type="checkbox" name="{{data.name}}" />', + ) + + self.add_column(title="Status", + field_name="status", + hideable=False, + orderable=True, + filter_name="is_status", + static_data_name="status", + static_data_template="{{data.get_status_text}}", + ) + + id_link_template = ''' + <a href="{% url 'cve' data.id %}" id="dataid_{{data.id}}" target="_blank">{{data.name}}</a> + ''' + self.add_column(title="Name", + hideable=False, + orderable=True, + field_name="name", + static_data_name="name", + static_data_template=id_link_template, + ) + + self.add_column(title="Publish Request", + field_name="publish_state", + hideable=False, + static_data_name="publish_state", + static_data_template="{{data.get_publish_text}}", + ) + + self.add_column(title="Request Date", + field_name="publish_date", + hideable=False, + static_data_name="publish_date", + static_data_template="{% if data.publish_date %}{{data.publish_date}}{% else %}ASAP{% endif %}", + ) + + severity_v3_template = ''' + {{data.cvssV3_baseScore}} {{data.cvssV3_baseSeverity}} + ''' + self.add_column(title="Severity (V3)", + help_text="Severity of the CVE (v3)", + hideable=False, + orderable=False, + static_data_name='severity_v3', + static_data_template=severity_v3_template, + ) + + self.add_column(title="Description", + field_name="description", + hideable=False, + ) + +class UpdatePublishedTable(ToasterTable): + """Table of Publish requested CVE's in SRTool""" + + def __init__(self, *args, **kwargs): + super(UpdatePublishedTable, self).__init__(*args, **kwargs) + self.default_orderby = "date" + + def get_context_data(self,**kwargs): + context = super(UpdatePublishedTable, self).get_context_data(**kwargs) + return context + + def setup_queryset(self, *args, **kwargs): + self.queryset = PublishPending.objects.all() + self.queryset = self.queryset.order_by(self.default_orderby) + + def setup_columns(self, *args, **kwargs): + + self.add_column(title="Select", + field_name="Select", + hideable=False, + static_data_name="select", + static_data_template='<input type="checkbox" name="{{data.cve.name}}" />', + ) + + self.add_column(title="Status", + field_name="status", + hideable=False, + orderable=True, + static_data_name="status", + static_data_template="{{data.cve.get_status_text}}", + ) + + id_link_template = ''' + <a href="{% url 'cve' data.cve.id %}" id="dataid_{{data.cve.id}}" target="_blank">{{data.cve.name}}</a> + ''' + self.add_column(title="Name", + hideable=False, + orderable=True, + field_name="name", + static_data_name="name", + static_data_template=id_link_template, + ) + + self.add_column(title="Publish Request", + field_name="publish_state", + hideable=False, + static_data_name="publish_state", + static_data_template="{{data.cve.get_publish_text}}", + ) + + self.add_column(title="Publish Request Date", + field_name="date", + hideable=False, + orderable=True, + ) + + if False: + self.add_column(title="Note", + field_name="note", + hideable=False, + ) diff --git a/lib/srtgui/templates/base.html b/lib/srtgui/templates/base.html index 3cc98a49..527d6b9b 100644 --- a/lib/srtgui/templates/base.html +++ b/lib/srtgui/templates/base.html @@ -148,9 +148,15 @@ window.onclick = function(event) { </div> <div class="collapse navbar-collapse" id="global-nav"> <ul class="nav navbar-nav"> + <li id="navbar-home" {% if request.resolver_match.url_name == 'landing' %}class="active"{% endif %}> + <a href="{% url 'landing' %}"> + <i class="glyphicon glyphicon-tasks"></i> + Home + </a> + </li> {% if access.is_guest %} {% else %} - <li id="navbar-manage" class="active"> + <li id="navbar-manage" {% if request.resolver_match.url_name == 'manage' %}class="active"{% endif %}> <a href="{% url 'manage' %}"> <i class="glyphicon glyphicon-tasks"></i> Management @@ -158,18 +164,18 @@ window.onclick = function(event) { </li> {% endif %} {% if request.resolver_match.url_name != 'landing' %} - <li id="navbar-all-builds" - {% if request.resolver_match.url_name == 'all-cves' %} + <li id="navbar-all-cves" + {% if request.resolver_match.url_name == 'cves' %} class="active" {% endif %}> - <a href="{% url 'all-cves' %}"> + <a href="{% url 'cves' %}"> <i class="glyphicon glyphicon-tasks"></i> All CVE's </a> </li> {% endif %} <li id="navbar-docs"> - <a href="{% url 'guided_tour' %}"> <i class="glyphicon glyphicon-book"></i> Documentation </a> + <a href="{% url 'guided_tour' %}"> <i class="glyphicon glyphicon-book"></i> Documentation ({{srt_user_id}},{{access.current_user_access}})</a> <!-- <a target="_blank" href="https://knowledge.windriver.com/en-us/000_Products/000/010/050"> <i class="glyphicon glyphicon-book"></i> @@ -180,7 +186,7 @@ window.onclick = function(event) { </ul> {% if access.is_guest %} - <a class="btn btn-default navbar-btn navbar-right" id="login-button" href="{% url 'login_admin' %}">Login (Guest)</a> + <a class="btn btn-default navbar-btn navbar-right" id="login-button" href="{% url 'login' %}">Login (Guest)</a> {% else %} <a class="btn btn-default navbar-btn navbar-right" id="login-button" href="{% url 'login_guest' %}">Logout ({{access.user_name}})</a> {% endif %} @@ -189,19 +195,9 @@ window.onclick = function(event) { <button onclick="myFunction()" class="dropbtn ">Tools</button> <div id="myDropdown" class="dropdown-content"> {% if request.resolver_match.url_name == 'landing' %} - {% if access.is_creator %} - <a href="{% url 'users' %}">Users</a> - <a href="{% url 'sources' %}">Sources</a> - <a href="{% url 'manage' %}">Management</a> - {% endif %} - <a href="{% url 'export' request.resolver_match.url_name %}">Report ...</a> - <a href="{% url 'export' request.resolver_match.url_name %}">Export ...</a> + <a href="{% url 'report' request.resolver_match.url_name %}">Report/Export ...</a> {% else %} - {% if access.is_creator %} - <a href="">Edit ...</a> - {% endif %} - <a href="{% url 'export' request.resolver_match.url_name %}">Report ...</a> - <a href="{% url 'export' request.resolver_match.url_name %}">Export ...</a> + <a id="report_link" href="{% url 'report' request.resolver_match.url_name %}">Report/Export ...</a> {% endif %} </div> </div> diff --git a/lib/srtgui/templates/cpes-toastertable.html b/lib/srtgui/templates/cpes-toastertable.html index 58101e2a..8c92c012 100644 --- a/lib/srtgui/templates/cpes-toastertable.html +++ b/lib/srtgui/templates/cpes-toastertable.html @@ -29,7 +29,8 @@ <h1 class="top-air" data-role="page-title"></h1> </div> - {% url 'cpes' as xhr_table_url %} + {# xhr_table_url is just the current url so leave it blank #} + {% url '' as xhr_table_url %} {% include 'toastertable.html' %} </div> </div> diff --git a/lib/srtgui/templates/cve.html b/lib/srtgui/templates/cve.html index b1df1819..a70448f8 100644 --- a/lib/srtgui/templates/cve.html +++ b/lib/srtgui/templates/cve.html @@ -8,357 +8,452 @@ <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">→</span> - <li><a href="{% url 'all-cves' %}">CVE's</a></li><span class="divider">→</span> - <li>{{cve_list_table.0.0.name}}</li> - <li><a class="btn btn-default navbar-btn " id="cve-prev" href="{% url 'cve' cve_prev %}">Prev</a></li> - <li><a class="btn btn-default navbar-btn " id="cve-next" href="{% url 'cve' cve_next %}">Next</a></li> - </ul> - </div> + <div class="col-md-12"> + <ul class="breadcrumb" id="breadcrumb"> + <li><a href="{% url 'landing' %}">Home</a></li><span class="divider">→</span> + <li><a href="{% url 'cves' %}">CVE's</a></li><span class="divider">→</span> + <li>{{cve_list_table.0.0.name}}</li> + <li><a class="btn btn-default navbar-btn " id="cve-prev" href="{% url 'cve' cve_prev %}">Prev</a></li> + <li><a class="btn btn-default navbar-btn " id="cve-next" href="{% url 'cve' cve_next %}">Next</a></li> + </ul> + </div> </div> <!-- Begin container --> + <div class="row"> <div class="col-md-12"> <div class="page-header build-data"> - <h1>{{cve_list_table.0.0.name}} Detail {% if not cve_list_table.0.0.public %} <font color="red">[PRIVATE]</font> {% endif %}</h1> + <h1 style="display:inline-block;vertical-align: middle;">{{cve_list_table.0.0.name}} Detail {% if not cve_list_table.0.0.public %} <font color="red">[PRIVATE]</font> {% endif %}</h1> + {% if access.is_creator %} + <span style="padding-left:30px;"><button id="select-quickedit" class="btn btn-default" type="button">Edit SRTool Status...</button></span> + <!--<span style="padding-left:30px;"><button id="select-quickedit" class="btn btn-default" type="button">Edit CVE Data ...</button></span>--> + {% endif %} </div> </div> </div> + <div class="row" style="padding-left: 25px;"> <UL> - <LI> + <LI> + <i>SRTool Priority:</i> {{cve_list_table.0.0.get_priority_text}} + </LI> + <LI> <i>SRTool Status:</i> {{cve_list_table.0.0.get_status_text}} - </LI> - <LI> + </LI> + <LI> <i>SRTool Notes:</i> '{{cve_list_table.0.0.comments}}' - {% if access.is_creator %} - </LI> - <LI> - <i>SRTool Private Notes:</i> '{{cve_list_table.0.0.comments_private}}' - {% endif %} - </LI> - <LI> - {% if access.is_creator %} - <i>Publish</i> = - <select name="Publish"> - <option value="Undetermined">Undetermined</option> - <option value="Automatic Publish Date">Automatic Publish Date</option> - <option value="Request Publish Date">Request Publish Date</option> - <option value="Published">Published</option> - <option value="Do Not Published">Do Not Published</option> - </select> - <i>Publish Date</i> = {{cve_list_table.0.0.publish_date}} - <a class="btn btn-default " id="login-button" href="">Publish Now</a> - <!--<a class="btn btn-default navbar-btn " id="login-button" href="">Publish Now</a> --> - {% else %} - <i>Publish</i> = {{cve_list_table.0.0.get_publish_text}}, <i>Publish Date</i> = {{cve_list_table.0.0.publish_date}} - {% endif %} - </LI> + {% if access.is_creator %} + </LI> + <LI> + <i>SRTool Private Notes:</i> '{{cve_list_table.0.0.comments_private}}' + {% endif %} + </LI> + <LI> + {% if access.is_creator %} + <i>Publish = {{cve_list_table.0.0.get_publish_text}}</i> + <!--<a class="btn btn-default navbar-btn " id="login-button" href="">Publish Now</a> --> + {% else %} + <i>Publish</i> = {{cve_list_table.0.0.get_publish_text}}, <i>Publish Date</i> = {{cve_list_table.0.0.publish_date}} + {% endif %} + </LI> </UL> + + +</div> +<!-- Quick Edit --> +<div id="details-quickedit" style="display:none;"> + <p><p> + <button class="execute" id="submit-quickedit"> Submit Changes </button> + <p><i>Priority</i> = + <select name="Priority" id="select-priority-state"> + <option value="0" {% if 0 == cve_list_table.0.0.priority %}selected{% endif %}></option> + <option value="1" {% if 1 == cve_list_table.0.0.priority %}selected{% endif %}>Minor</option> + <option value="2" {% if 2 == cve_list_table.0.0.priority %}selected{% endif %}>Low</option> + <option value="3" {% if 3 == cve_list_table.0.0.priority %}selected{% endif %}>Medium</option> + <option value="4" {% if 4 == cve_list_table.0.0.priority %}selected{% endif %}>High</option> + </select> + <p><input type="text" placeholder="Edit Note" id="text-note" size="40" value="{{cve_list_table.0.0.comments}}"></p> + <p><input type="text" placeholder="Edit Private Note" id="text-private-note" size="40" value="{{cve_list_table.0.0.comments_private}}"></p> + <i>Publish</i> = + <select name="Publish" id="select-publish-state"> + <option value="0" {% if 0 == cve_list_table.0.0.publish_state %}selected{% endif %}>Unpublished</option> + <option value="1" {% if 1 == cve_list_table.0.0.publish_state %}selected{% endif %}>Not to be Published</option> + <option value="2" {% if 2 == cve_list_table.0.0.publish_state %}selected{% endif %}>Published</option> + <option value="3" {% if 3 == cve_list_table.0.0.publish_state %}selected{% endif %}>Publish Request (New)</option> + <option value="4" {% if 4 == cve_list_table.0.0.publish_state %}selected{% endif %}>Publish Request (Update)</option> + <option value="5" {% if 5 == cve_list_table.0.0.publish_state %}selected{% endif %}>Publish Submitted</option> + </select> + <i>Publish Date</i> = <input type="text" placeholder="Auto Publish Date" id="select-publish-date" size="40" value="{{cve_list_table.0.0.publish_date}}"><p> + <p><p> </div> <div class="row"> <div class="col-md-8 tabbable"> <ul class="nav nav-tabs"> - {% for object,state,id in cve_list_table %} + {% for object,details,state,id in cve_list_table %} <li class="{{state}}"> <a href="#{{id}}" data-toggle="tab"> <span class="glyphicon glyphicon-question-sign get-help" title="{{id}}"></span> {{id}} </a> </li> - {% endfor %} + {% endfor %} </ul> <div class="tab-content"> - {% for object,state,id in cve_list_table %} - <div class="tab-pane {{state}}" id="{{id}}"> + {% for object,details,state,id in cve_list_table %} + <div class="tab-pane {{state}}" id="{{id}}"> <!-- vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv --> - <div class="row"> - <div class="col-md-5"> - <div> - <h3>Decription</h3> - {{object.description}} - </div> - <p/> - <div> - <B>Source:</B> {{object.cve_data_format}} <B>Last Modified:</B> {{object.lastModifiedDate}} - </div> - </div> - <div class="col-md-5"> - <div class="well"> - <h2>Quick Info</h2> - - - <dl class="dl-horizontal"> - <dt>CVE Dictionary Entry:</dt> - <dd>{{object.name}}</dd> - - <dt>Original release date:</dt> - <dd>{{object.publishedDate}}</dd> - - <dt>Last revised:</dt> - <dd>{{object.lastModifiedDate}}</dd> - - <dt>Source:</dt> - <dd>{{object.cve_data_format}}</dd> - - {% if id == "Summary" %} - <dt>SRTool Vulnerability:</dt> - <dd> - {% if object.cve_to_vulnerability.all %} - {% for cv in object.cve_to_vulnerability.all %} - {% if not forloop.first %}| {% endif %}<a href="{% url 'vulnerability' cv.vulnerability.pk %}">{{cv.vulnerability.name}}</a> - {% endfor %} - {% else %} - No vulnerability record found - {% endif %} - {% endif %} - </dd> - </dl> - </div> - </div> - </div> - - <div class="row"> - <h3>Impact</h3> - <div class="col-md-4"> - <h3>CVSS Severity (version 3.0):</h3> - <dl class="dl-horizontal"> - <dt>CVSS v3 Base Score:</dt> - <dd>{{object.cvssV3_baseScore}} {{object.cvssV3_baseSeverity}}</dd> - - <dt>Vector:</dt> - <dd>{{object.cvssV3_vectorString}}</dd> - - <dt>Impact Score:</dt> - <dd>{{object.cvssV3_impactScore}}</dd> - - <dt>Exploitability Score:</dt> - <dd>{{object.cvssV3_exploitabilityScore}}</dd> - </dl> - <h3>CVSS Version 3 Metrics:</h3> - <dl class="dl-horizontal"> - <dt>Attack Vector (AV):</dt> - <dd>{{object.cvssV3_attackVector}}</dd> - - <dt>Attack Complexity (AC):</dt> - <dd>{{object.cvssV3_attackComplexity}}</dd> - - <dt>Privileges Required (PR):</dt> - <dd>{{object.cvssV3_privilegesRequired}}</dd> - - <dt>User Interaction (UI):</dt> - <dd>{{object.cvssV3_userInteraction}}</dd> - - <dt>Scope (S):</dt> - <dd>{{object.cvssV3_scope}}</dd> - - <dt>Confidentiality (C):</dt> - <dd>{{object.cvssV3_confidentialityImpact}}</dd> - - <dt>Integrity (I):</dt> - <dd>{{object.cvssV3_integrityImpact}}</dd> - - <dt>Availability (A):</dt> - <dd>{{object.cvssV3_availabilityImpact}}</dd> - </dl> - </div> - <div class="col-md-4"> - <h3>CVSS Severity (version 2.0):</h3> - <dl class="dl-horizontal"> - <dt>CVSS v2 Base Score:</dt> - <dd>{{object.cvssV2_baseScore}} {{object.cvssV2_severity}}</dd> - - <dt>Vector:</dt> - <dd>{{object.cvssV2_vectorString}}</dd> - - <dt>Impact Subscore:</dt> - <dd>{{object.cvssV2_impactScore}}</dd> - - <dt>Exploitability Subscore:</dt> - <dd>{{object.cvssV2_exploitabilityScore}}</dd> - - </dl> - <h3>CVSS Version 2 Metrics:</h3> - <dl class="dl-horizontal"> - <dt>Access Vector (AV):</dt> - <dd>{{object.cvssV2_accessVector}}</dd> - - <dt>Access Complexity (AC):</dt> - <dd>{{object.cvssV2_accessComplexity}}</dd> - - <dt>Authentication:</dt> - <dd>{{object.cvssV2_authentication}}</dd> - - <dt>Impact Type:</dt> - <dd>???</dd> - - </dl> - </div> - </div> - - <div class="row"> - <h3>References to Advisories, Solutions, and Tools</h3> - <p> - By selecting these links, you will be leaving NIST webspace. We have provided these links to other web sites - because they may have information that would be of interest to you. No inferences should be drawn on account - of other sites being referenced, or not, from this page. There may be other web sites that are more appropriate - for your purpose. NIST does not necessarily endorse the views expressed, or concur with the facts presented on - these sites. Further, NIST does not endorse any commercial products that may be mentioned on these sites. Please - address comments about this page to nvd@nist.gov. - </p> - <table class="table table-striped table-condensed" data-testid="vuln-hyperlinks-table"> - <thead> - <tr> - <th>Hyperlink</th> - <th>Resource</th> - <th>Type</th> - <th>Source</th> - <th>Name</th> - </tr> - </thead> - {% if object.references.all %} - {% for ref in object.references.all %} - <tr> - <td><a href="{{ref.hyperlink}}" target="_blank">{{ ref.hyperlink }}</a></td> - <td>{{ ref.resource }}</td> - <td>{{ ref.type }}</td> - <td>{{ ref.source }}</td> - <td>{{ ref.name }}</td> - </tr> - {% endfor %} - {% else %} - <tr> - <td>No references</td> - </tr> - {% endif %} - </table> - </div> - - <div class="row"> - <h3>Technical Details</h3> - <p> - <h4>Vulnerability Type<a href="{% url 'all-cwes' %}"> (View All)</a></h4> - <p> - <table class="table table-striped table-condensed" data-testid="cve2cwe-hyperlinks-table"> - <thead> - <tr> - <th>Name</th> - <th>Summary</th> - </tr> - </thead> - {% if object.cve2cwe.all %} - {% for ref in object.cve2cwe.all %} - <tr> - <td>{{ ref.cwe.name }}</td> - <td>{{ ref.cwe.summary }}</td> - </tr> - {% endfor %} - {% else %} - <tr> - <td>No CWE references</td> - </tr> - {% endif %} - </table> - </div> - - <div class="row"> - <h3>Vulnerable software and versions</h3> - <div > <!--style="padding-left: 25px;" --> - {% if object.get_cpe_list %} - {% for cpe in object.get_cpe_list %} - {% if not cpe %} - {% elif not cpe.0 %} - {% elif '<config' in cpe.0 %} - <div style="padding-left: 25px;"> - <h4>• Configuration </h3> - {% elif '<and>' == cpe.0 %} - <div style="padding-left: 25px;"> - <h4>• AND</h3> - {% elif '<or>' == cpe.0 %} - <div style="padding-left: 25px;"> - <h4>• OR</h3> - <table class="table table-striped table-condensed" data-testid="configs-hyperlinks-table"> - <thead> - <tr> - <th>Vulnerable</th> - <th>CPE 2.3</th> - <th>CPE 2.2</th> - <th><!--<span class="glyphicon glyphicon-question-sign get-help" title="Version End Including"></span>-->Version End</th> - </tr> - </thead> - {% elif '</or>' == cpe.0 %} - </table> - </div> - {% elif '</and>' == cpe.0 %} - </div> - {% elif '</config>' == cpe.0 %} - </div> - {% else %} - <tr> - <td>{{ cpe.0 }}</td> - <td>{{ cpe.1 }}</td> - <td>{{ cpe.2 }}</td> - <td>{{ cpe.3 }}</td> - </tr> - {% endif %} - {% endfor %} - {% else %} - No CPE configurations - {% endif %} - </div> - <p/> - </div> - - - <div class="row"> - <h3>History - </h3> - - <table class="table table-striped table-condensed" data-testid="vuln-hyperlinks-table"> - <thead> - <tr> - <th>Comment</th> - <th>Date</th> - <th>Author</th> - </tr> - </thead> - - {% if object.cve_history.all %} - {% for c in object.cve_history.all %} - <tr> - <td>{{ c.comment }}</td> - <td>{{ c.date }}</td> - <td>{{ c.author }}</td> - </tr> - {% endfor %} - {% else %} - <tr> - <td>No history found</td> - </tr> - {% endif %} - </table> - - </div> + <!-- Row: Description and Quick Info --> + <div class="row"> + <div class="col-md-5"> + <div> + <h3>Decription</h3> + {{object.description}} + </div> + <p/> + <div> + <B>Source:</B> {{object.cve_data_format}} <B>Last Modified:</B> {{object.lastModifiedDate}} + </div> + </div> + <div class="col-md-5"> + <div class="well"> + <h2>Quick Info</h2> + + + <dl class="dl-horizontal"> + <dt>CVE Dictionary Entry:</dt> + <dd>{{object.name}}</dd> + + <dt>Original release date:</dt> + <dd>{{object.publishedDate}}</dd> + + <dt>Last revised:</dt> + <dd>{{object.lastModifiedDate}}</dd> + + <dt>Source:</dt> + <dd>{{object.cve_data_format}}</dd> + + {% if id == "Summary" %} + <dt>SRTool Vulnerability:</dt> + <dd> + {% if object.cve_to_vulnerability.all %} + {% for cv in object.cve_to_vulnerability.all %} + {% if not forloop.first %}| {% endif %}<a href="{% url 'vulnerability' cv.vulnerability.pk %}">{{cv.vulnerability.name}}</a> + {% endfor %} + {% else %} + No vulnerability record found + {% endif %} + {% endif %} + </dd> + + <dt>NIST Link:</dt> + <dd><a href="https://nvd.nist.gov/vuln/detail/{{object.name}}" target="_blank">{{object.name}}</a></dd> + + </dl> + </div> + </div> + </div> <!-- /Description --> + + <!-- Row: CVSS --> + <div class="row" style="padding-left: 25px;"> + <h3>Impact</h3> + <div class="col-md-4"> + <h3>CVSS Severity (version 3.0):</h3> + <dl class="dl-horizontal"> + <dt>CVSS v3 Base Score:</dt> + <dd>{{object.cvssV3_baseScore}} {{object.cvssV3_baseSeverity}}</dd> + + <dt>Vector:</dt> + <dd>{{details.cvssV3_vectorString}}</dd> + + <dt>Impact Score:</dt> + <dd>{{details.cvssV3_impactScore}}</dd> + + <dt>Exploitability Score:</dt> + <dd>{{details.cvssV3_exploitabilityScore}}</dd> + </dl> + <h3>CVSS Version 3 Metrics:</h3> + <dl class="dl-horizontal"> + <dt>Attack Vector (AV):</dt> + <dd>{{details.cvssV3_attackVector}}</dd> + + <dt>Attack Complexity (AC):</dt> + <dd>{{details.cvssV3_attackComplexity}}</dd> + + <dt>Privileges Required (PR):</dt> + <dd>{{details.cvssV3_privilegesRequired}}</dd> + + <dt>User Interaction (UI):</dt> + <dd>{{details.cvssV3_userInteraction}}</dd> + + <dt>Scope (S):</dt> + <dd>{{details.cvssV3_scope}}</dd> + + <dt>Confidentiality (C):</dt> + <dd>{{details.cvssV3_confidentialityImpact}}</dd> + + <dt>Integrity (I):</dt> + <dd>{{details.cvssV3_integrityImpact}}</dd> + + <dt>Availability (A):</dt> + <dd>{{details.cvssV3_availabilityImpact}}</dd> + </dl> + </div> + <div class="col-md-4"> + <h3>CVSS Severity (version 2.0):</h3> + <dl class="dl-horizontal"> + <dt>CVSS v2 Base Score:</dt> + <dd>{{object.cvssV2_baseScore}} {{details.cvssV2_severity}}</dd> + + <dt>Vector:</dt> + <dd>{{details.cvssV2_vectorString}}</dd> + + <dt>Impact Subscore:</dt> + <dd>{{details.cvssV2_impactScore}}</dd> + + <dt>Exploitability Subscore:</dt> + <dd>{{details.cvssV2_exploitabilityScore}}</dd> + + </dl> + <h3>CVSS Version 2 Metrics:</h3> + <dl class="dl-horizontal"> + <dt>Access Vector (AV):</dt> + <dd>{{details.cvssV2_accessVector}}</dd> + + <dt>Access Complexity (AC):</dt> + <dd>{{details.cvssV2_accessComplexity}}</dd> + + <dt>Authentication:</dt> + <dd>{{details.cvssV2_authentication}}</dd> + + <dt>Impact Type:</dt> + <dd>???</dd> + + </dl> + </div> + </div> <!-- /CVSS --> + + <!-- Row: References --> + <div class="row" style="padding-left: 25px;"> + <h3>References to Advisories, Solutions, and Tools</h3> + <p> + By selecting these links, you will be leaving NIST webspace. We have provided these links to other web sites + because they may have information that would be of interest to you. No inferences should be drawn on account + of other sites being referenced, or not, from this page. There may be other web sites that are more appropriate + for your purpose. NIST does not necessarily endorse the views expressed, or concur with the facts presented on + these sites. Further, NIST does not endorse any commercial products that may be mentioned on these sites. Please + address comments about this page to nvd@nist.gov. + </p> + <table class="table table-striped table-condensed" data-testid="vuln-hyperlinks-table"> + <thead> + <tr> + <th>Hyperlink</th> + <th>Resource</th> + </tr> + </thead> + {% if object.references.all %} + {% for ref in object.references.all %} + <tr> + <td><a href="{{ref.hyperlink}}" target="_blank">{{ ref.hyperlink }}</a></td> + <td>{{ ref.resource }}</td> + </tr> + {% endfor %} + {% else %} + <tr> + <td>No references</td> + </tr> + {% endif %} + </table> + </div> <!-- /References --> + + <!-- Row: CWE --> + <div class="row" style="padding-left: 25px;"> + <h3>Technical Details</h3> + <p> + <h4>Vulnerability Type<a href="{% url 'cwes' %}"> (View All)</a></h4> + <p> + <table class="table table-striped table-condensed" data-testid="cve2cwe-hyperlinks-table"> + <thead> + <tr> + <th>Name</th> + <th>Summary</th> + </tr> + </thead> + {% if object.cve2cwe.all %} + {% for ref in object.cve2cwe.all %} + <tr> + <td>{{ ref.cwe.name }}</td> + <td>{{ ref.cwe.summary }}</td> + </tr> + {% endfor %} + {% else %} + <tr> + <td>No CWE references</td> + </tr> + {% endif %} + </table> + </div> <!-- /CWE --> + + <!-- Row: CPE --> + <div class="row" style="padding-left: 25px;"> + <h3>Vulnerable software and versions</h3> + <div > <!--style="padding-left: 25px;" --> + {% if details.get_cpe_list %} + {% for cpe in details.get_cpe_list %} + {% if not cpe %} + {% elif not cpe.0 %} + {% elif '[config' in cpe.0 %} + <div style="padding-left: 25px;"> + <h4>• Configuration </h3> + {% elif '[and]' == cpe.0 %} + <div style="padding-left: 25px;"> + <h4>• AND</h3> + {% elif '[or]' == cpe.0 %} + <div style="padding-left: 25px;"> + <h4>• OR</h3> + <table class="table table-striped table-condensed" data-testid="configs-hyperlinks-table"> + <thead> + <tr> + <th>Vulnerable</th> + <th>CPE 2.3</th> + <th>CPE 2.2</th> + <th><!--<span class="glyphicon glyphicon-question-sign get-help" title="Version End Including"></span>-->Version End</th> + </tr> + </thead> + {% elif '[/or]' == cpe.0 %} + </table> + </div> + {% elif '[/and]' == cpe.0 %} + </div> + {% elif '[/config]' == cpe.0 %} + </div> + {% else %} + <tr> + <td>{{ cpe.0 }}</td> + <td>{{ cpe.1 }}</td> + <td>{{ cpe.2 }}</td> + <td>{{ cpe.3 }}</td> + </tr> + {% endif %} + {% endfor %} + {% else %} + No CPE configurations + {% endif %} + </div> + <p> + </div> <!-- /CPE --> + <!-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --> - </div> <!-- end tab-pane --> + </div> <!-- end tab-pane --> {% endfor %} </div> <!-- end tab-content --> </div> <!-- end tabbable --> </div> <!-- end row --> +<div class="row" style="padding-left: 25px;"> + <h3>History</h3> + + <table class="table table-striped table-condensed" data-testid="vuln-hyperlinks-table"> + <thead> + <tr> + <th>Comment</th> + <th>Date</th> + <th>Author</th> + </tr> + </thead> + + {% if cve_list_table.1.0.cve_history.all %} + {% for c in cve_list_table.1.0.cve_history.all %} + <tr> + <td>{{ c.comment }}</td> + <td>{{ c.date }}</td> + <td>{{ c.author }}</td> + </tr> + {% endfor %} + {% else %} + <tr> + <td>No history found</td> + </tr> + {% endif %} + </table> +</div> + <HR ALIGN="center" WIDTH="100%"> +<script> + var selected_quickedit=false; + + $(document).ready(function() { + function onCommitAjaxSuccess(data, textstatus) { + 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 + ")"); + } + + /* ensure cookie exists {% csrf_token %} */ + function postCommitAjaxRequest(reqdata) { + reqdata['cve_id'] = {{ object.id }}; + var ajax = $.ajax({ + type:"POST", + data: reqdata, + url:"{% url 'xhr_cve_commit' %}", + headers: { 'X-CSRFToken': $.cookie("csrftoken")}, + success: onCommitAjaxSuccess, + error: onCommitAjaxError, + }) + } + + $('#select-quickedit').click(function(){ + if (selected_quickedit) { + selected_quickedit=false; + $("#details-quickedit").slideUp(); + } else { + selected_quickedit=true; + $("#details-quickedit").slideDown(); + } + }); + + $('#submit-quickedit').click(function(){ + var note=$('#text-note').val().trim(); + var private_note=$('#text-private-note').val().trim(); + var priority=$('#select-priority-state').val(); + var publish_state=$('#select-publish-state').val(); + var publish_date=$('#select-publish-date').val(); + postCommitAjaxRequest({ + "action" : 'submit-quickedit', + "priority" : priority, + "note" : note, + "private_note" : private_note, + "publish_state" : publish_state, + "publish_date" : publish_date, + }); + }); + + /* Set the report link */ + $('#report_link').attr('href',"{% url 'report' request.resolver_match.url_name %}?record_list={{cve_list_table.0.0.id}}"); + }); +</script> {% endblock %} - diff --git a/lib/srtgui/templates/cves-select-toastertable.html b/lib/srtgui/templates/cves-select-toastertable.html index 8e2445b0..3399332e 100644 --- a/lib/srtgui/templates/cves-select-toastertable.html +++ b/lib/srtgui/templates/cves-select-toastertable.html @@ -111,7 +111,8 @@ <h1 class="top-air" data-role="page-title"></h1> </div> - {% url 'cpes' as xhr_table_url %} + {# xhr_table_url is just the current url so leave it blank #} + {% url '' as xhr_table_url %} {% include 'toastertable.html' %} </div> </div> @@ -338,6 +339,17 @@ titleElt.text(title); cve_total = total; update_vulnerable_status(); + + /* Set the report link */ + var record_list="" + $(".name > a").each(function(){ + var this_id=$(this).prop('id'); + if (this_id.startsWith("dataid_")) { + record_list +=this_id.replace(/dataid_/,"") + ","; + } + }); + $('#report_link').attr('href',"{% url 'report' request.resolver_match.url_name %}?record_list="+record_list); + }); }); </script> diff --git a/lib/srtgui/templates/cves-toastertable.html b/lib/srtgui/templates/cves-toastertable.html index fa83e5df..5fc37fdf 100644 --- a/lib/srtgui/templates/cves-toastertable.html +++ b/lib/srtgui/templates/cves-toastertable.html @@ -30,7 +30,8 @@ <h1 class="top-air" data-role="page-title"></h1> </div> - {% url 'cves' as xhr_table_url %} + {# xhr_table_url is just the current url so leave it blank #} + {% url '' as xhr_table_url %} {% include 'toastertable.html' %} </div> </div> @@ -53,6 +54,17 @@ } titleElt.text(title); + + /* Set the report link */ + var record_list="" + $(".name > a").each(function(){ + var this_id=$(this).prop('id'); + if (this_id.startsWith("dataid_")) { + record_list +=this_id.replace(/dataid_/,"") + ","; + } + }); + $('#report_link').attr('href',"{% url 'report' request.resolver_match.url_name %}?record_list="+record_list); + }); }); </script> diff --git a/lib/srtgui/templates/cwes-toastertable.html b/lib/srtgui/templates/cwes-toastertable.html index 06369343..cbd62a90 100644 --- a/lib/srtgui/templates/cwes-toastertable.html +++ b/lib/srtgui/templates/cwes-toastertable.html @@ -29,7 +29,8 @@ <h1 class="top-air" data-role="page-title"></h1> </div> - {% url 'cwes' as xhr_table_url %} + {# xhr_table_url is just the current url so leave it blank #} + {% url '' as xhr_table_url %} {% include 'toastertable.html' %} </div> </div> @@ -52,6 +53,17 @@ } titleElt.text(title); + + /* Set the report link */ + var record_list="" + $(".href > a").each(function(){ + var this_id=$(this).prop('id'); + if (this_id.startsWith("dataid_")) { + record_list +=this_id.replace(/dataid_/,"") + ","; + } + }); + $('#report_link').attr('href',"{% url 'report' request.resolver_match.url_name %}?record_list="+record_list); + }); }); </script> diff --git a/lib/srtgui/templates/defect.html b/lib/srtgui/templates/defect.html new file mode 100644 index 00000000..aa13b0dd --- /dev/null +++ b/lib/srtgui/templates/defect.html @@ -0,0 +1,86 @@ +{% extends "base.html" %} + +{% load projecttags %} + +{% block title %} {{object.name}} - SRTool {% 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">→</span> + <li><a href="{% url 'defects' %}">Defects</a></li><span class="divider">→</span> + <li>{{object.name}}</li> + </ul> + </div> +</div> + +<!-- Begin container --> + +<div class="row"> + <div class="col-md-12"> + <div class="page-header build-data"> + <h1>Defect {{object.name}} Detail</h1> + </div> + </div> +</div> + +<div class="row"> + <div class="col-md-5"> + <div class="well"> + <h2>Quick Info</h2> + <dl class="dl-horizontal"> + <dt>Summary:</dt> + <dd>{{object.summary}}</dd> + + <dt>URL:</dt> + <dd><a href="{{object.url}}" id="dataid_{{object.id}}" target="_blank">{{object.url}}</a></dd> + + <dt>Priority:</dt> + <dd>{{object.get_priority_text}}</dd> + + <dt>Status:</dt> + <dd>{{object.get_status_text}}</dd> + + <dt>Resolution:</dt> + <dd>{{object.get_resolution_text}}</dd> + + <dt>Publish:</dt> + <dd>{{object.publish}}</dd> + + <dt>Release Version:</dt> + <dd>{{object.release_version}}</dd> + + <dt>Investigations:</dt> + <dd> + {% for ji in object.defect_to_investigation.all %} + {% if not forloop.first %}| {% endif %}<a href="{% url 'investigation' ji.investigation.id %}" target="_blank">{{ji.investigation.name}} </a> + {% endfor %} + </dd> + + <dt>Product:</dt> + <dd><a href="{% url 'product' object.product.id %}">{{object.product.long_name}}</a></dd> + + <dt>date_created:</dt> + <dd>{{object.date_created}}</dd> + + <dt>date_updated:</dt> + <dd>{{object.date_updated}}</dd> + + </dl> + </div> + </div> +</div> + + +<!-- Javascript support --> +<script> + $(document).ready(function() { + /* Set the report link */ + $('#report_link').attr('href',"{% url 'report' request.resolver_match.url_name %}?record_list={{object.id}}"); + }); +</script> + +{% endblock %} diff --git a/lib/srtgui/templates/defects-toastertable.html b/lib/srtgui/templates/defects-toastertable.html index 82a6c5ee..1d699909 100644 --- a/lib/srtgui/templates/defects-toastertable.html +++ b/lib/srtgui/templates/defects-toastertable.html @@ -29,7 +29,8 @@ <h1 class="top-air" data-role="page-title"></h1> </div> - {% url 'cpes' as xhr_table_url %} + {# xhr_table_url is just the current url so leave it blank #} + {% url '' as xhr_table_url %} {% include 'toastertable.html' %} </div> </div> @@ -52,6 +53,17 @@ } titleElt.text(title); + + /* Set the report link */ + var record_list="" + $(".name > a").each(function(){ + var this_id=$(this).prop('id'); + if (this_id.startsWith("dataid_")) { + record_list +=this_id.replace(/dataid_/,"") + ","; + } + }); + $('#report_link').attr('href',"{% url 'report' request.resolver_match.url_name %}?record_list="+record_list); + }); }); </script> diff --git a/lib/srtgui/templates/guided_tour.html b/lib/srtgui/templates/guided_tour.html index 648bde14..fe5fd60e 100644 --- a/lib/srtgui/templates/guided_tour.html +++ b/lib/srtgui/templates/guided_tour.html @@ -74,7 +74,7 @@ <div class="row" style="padding-left: 25px;"> <h3><a id="public"></a>Public View</h3> <ul> - <li> <b><a href="{% url 'all-cves' %}">CVE's</a></b> </li> + <li> <b><a href="{% url 'cves' %}">CVE's</a></b> </li> <ul> <li> The CVE page is based on the NIST public page, and includes the V3 and V2 severities information, download links, and CPE's</li> <li> There are tabs to see (a) the original source data and (b) the SRTool edits (if any)</li> @@ -104,7 +104,7 @@ </ul> </ul> <ul> - <li> <b><a href="{% url 'all-defects' %}">Defects</a></b> </li> + <li> <b><a href="{% url 'defects' %}">Defects</a></b> </li> <ul> <li> This table lists all of the Jira defects being tracked by the Vulnerabilities and Investigations </li> <li> A quick status overview of the defects is provided </li> @@ -118,14 +118,14 @@ </ul> </ul> <ul> - <li> <b><a href="{% url 'all-cpes' %}">CPE's</a></b> </li> + <li> <b><a href="{% url 'cpes' %}">CPE's</a></b> </li> <ul> <li> The 'Common Product Enumerations' (CPE) found in the vulnerable CVE records </li> <li> This data can help track CPE's that result in vulnerabilities, to help improve triaging CVE's </li> </ul> </ul> <ul> - <li> <b><a href="{% url 'all-cwes' %}">CWE's</a></b> </li> + <li> <b><a href="{% url 'cwes' %}">CWE's</a></b> </li> <ul> <li> The 'Common Weakness Enumerations' (CWE) found in the vulnerable CVE records </li> </ul> diff --git a/lib/srtgui/templates/investigation.html b/lib/srtgui/templates/investigation.html index 77cb8fbd..bf0a5672 100644 --- a/lib/srtgui/templates/investigation.html +++ b/lib/srtgui/templates/investigation.html @@ -52,7 +52,7 @@ <h2>Quick Info</h2> <dl class="dl-horizontal"> <dt>Product:</dt> - <dd>{{object.product.name}} {{ object.product.version }} {{ object.product.profile }}</dd> + <dd><a href="{% url 'product' object.product.id %}">{{object.product.long_name}}</a></dd> <dt>Investigation:</dt> <dd><a href="{% url 'vulnerability' object.vulnerability.id %}">{{object.vulnerability.get_long_name}}</a></dd> @@ -60,15 +60,42 @@ <dt>Defect:</dt> <dd> {% for ij in object.investigation_to_defect.all %} - {% if not forloop.first %}| {% endif %}<a href="http://defect.wrs.com/browse/{{ij.defect.name}}" target="_blank">{{ij.defect.name}} </a> + {% if not forloop.first %}| {% endif %}<a href="{% url 'defect' ij.defect.id %}" target="_blank">{{ij.defect.name}} </a> {% endfor %} <p> {% if access.is_creator %} - <a class="btn btn-default navbar-btn " id="new-investigation-attachement" href="">Attach Defect</a> - <a class="btn btn-default navbar-btn " id="new-investigation-attachement" href="">Create Defect</a> + <a class="btn btn-default navbar-btn " id="select-attachdefect">Attach Defect</a> + <a class="btn btn-default navbar-btn " id="select-createdefect">Create Defect</a> + <a class="btn btn-default navbar-btn " id="select-trashdefect">Detach Defect</a> {% endif %} </dd> + <div id="details-attachdefect" style="display:none; border: 1px solid; padding:20px; width:400px; margin-bottom:25px; margin-left:150px"> + <p><b><label>Select Defect by Key:</label></b> + <div id="input-attachdefect" style="padding-left: 50px"> + <p><input type="text" id="text-attachdefect" placeholder="Issue Key (ex. {{defect_example}})" size="20"> <button class="execute" id="submit-attachdefect"> Submit </button></p> + </div> + </div> + + <div id="details-trashdefect" style="display:none; border: 1px solid; padding:20px; width:400px; margin-bottom:25px; margin-left:150px"> + <p><b><label>Select Defect(s):</label></b> + <div id="select-trashdefectlist" style="padding-left: 50px"> + <div class="row"> + <div id="all-investigation-to-defects" class="scrolling" style="width: 300px;"> + {% for obj in investigation_to_defect %} + <div class="checkbox"> + <label> + <input class="checkbox-defects" name="{{obj.defect.pk}}" type="checkbox">{{obj.defect.name}} + </label> + <p> + </div> + {% endfor %} + </div> + <button class="execute" id="submit-trashdefect"> Detach </button> + </div> + </div> + </div> + <dt>Status:</dt> <dd>{{object.get_status_text}}</dd> @@ -81,7 +108,7 @@ <dt>Release Version:</dt> <dd> {% for ij in object.investigation_to_defect.all %} - {% if not forloop.first %}| {% endif %}<a href="http://defect.wrs.com/browse/{{ij.defect.name}}" target="_blank">{{ij.defect.release_version}} </a> + {% if not forloop.first %}| {% endif %}<a href="{{ij.defect.url}}" target="_blank">{{ij.defect.release_version}} </a> {% endfor %} </dd> </dl> @@ -91,14 +118,18 @@ <hr> -<div class="row"> +<div class="row" style="padding-left: 25px;"> <h3>Comments - {% if access.is_creator %} - <a class="btn btn-default navbar-btn " id="new-investigation-attachement" href="{% url 'login' %}">Add comment</a> - {% endif %} + {% if access.is_creator %} + <button id="select-newcomment" class="btn btn-default" type="button">Add comment ...</button> + {% endif %} </h3> - <table class="table table-striped table-condensed" data-testid="inv-hyperlinks-table"> + <div id="input-newcomment" style="padding-left: 50px; display:none;"> + <p><input type="text" id="text-newcomment" size="40"> <button class="execute" id="submit-newcomment"> Submit </button></p> + </div> + + <table class="table table-striped table-condensed" data-testid="vuln-hyperlinks-table"> <thead> <tr> <th>Comment</th> @@ -116,11 +147,11 @@ <td>{{ c.comment }}</td> <td>{{ c.date }}</td> <td>{{ c.author }}</td> - {% if access.is_creator %} + {% if access.is_creator or c.author == current_user %} <td> <span id="config_var_entry_'+configvars_sorted[i][2]+'" class="js-config-var-name"></span> - <span class="glyphicon glyphicon-edit js-icon-pencil-config_var" id="affected_edit_'+{{c.id}}+'" x-data="'+{{c.id}}+'"></span> - <span class="glyphicon glyphicon-trash js-icon-trash-config_var" id="comment_trash_'+{{c.id}}+'" x-data="'+{{c.id}}+'"></span> + <span class="glyphicon glyphicon-edit edit-comment" id="affected_edit_'+{{c.id}}+'" x-data="{{c.id}}"></span> + <span class="glyphicon glyphicon-trash trash-comment" id="comment_trash_'+{{c.id}}+'" x-data="{{c.id}}"></span> </td> {% endif %} </tr> @@ -134,13 +165,25 @@ </div> -<div class="row"> +<div class="row" style="padding-left: 25px;"> <h3>Attachments - {% if access.is_creator %} - <a class="btn btn-default navbar-btn " id="new-investigation-attachement" href="{% url 'login' %}">Add attachment</a> - {% endif %} + {% if access.is_creator %} + <a class="btn btn-default navbar-btn " id="select-addattachment">Add attachment ... </a> + {% endif %} </h3> + <div id="details-addattachment" style="padding-left: 50px; display:none;"> + <p><p> + <div class="row"> + <form id="uploadbanner" enctype="multipart/form-data" method="post">{% csrf_token %} + <input id="fileDescription" name="fileDescription" type="text" placeholder="Enter Description" /> + <input id="fileUpload" name="fileUpload" type="file" /> + <input type="hidden" id="action" name="action" value="upload"> + <input type="submit" value="submit file" id="submit-addattachment" /> + </form> + </div> + </div> + <table class="table table-striped table-condensed" data-testid="vuln-hyperlinks-table"> <thead> <tr> @@ -157,17 +200,20 @@ {% for u in object.investigation_uploads.all %} <tr> <td>{{ u.description }}</td> - <td>{{ u.path }}</td> + <td>{{ u.path|basename }}</td> <td>{{ u.size }}</td> <td>{{ u.date }}</td> <td>{{ u.author }}</td> <td> <span id="attachment_entry_'+{{u.id}}+'" class="js-config-var-name"></span> - <span class="glyphicon glyphicon-download-alt get-help" title="Download document"></span> - {% if access.is_creator %} - <span class="glyphicon glyphicon-edit js-icon-pencil-config_var" id="affected_edit_'+{{u.id}}+'" x-data="'+{{u.id}}+'"></span> - <span class="glyphicon glyphicon-trash js-icon-trash-config_var" id="attachment_trash_'+{{u.id}}+'" x-data="'+{{u.id}}+'"></span> - {% endif %} + <form id="downloadbanner" enctype="multipart/form-data" method="post" >{% csrf_token %} + <input type="hidden" id="action" name="action" value="download"> + <input type="hidden" id="record_id" name="record_id" value={{u.id}}> + <span class="glyphicon glyphicon-download-alt submit-downloadattachment" id="attachment_download_'+{{u.id}}+'" x-data="{{u.id}}"></span> + {% if access.is_creator %} + <span class="glyphicon glyphicon-trash trash-attachment" id="attachment_trash_'+{{u.id}}+'" x-data="{{u.id}}"></span> + {% endif %} + </form> </td> </tr> {% endfor %} @@ -177,18 +223,37 @@ </tr> {% endif %} </table> - </div> <br/> <hr/> {% if access.is_creator %} - <div class="row"> + <div class="row" style="padding-left: 25px;"> <h3>Change Notifications - <a class="btn btn-default navbar-btn " id="new-investigation-notify" href="{% url 'login' %}">Add user notification</a> + {% if access.is_creator %} + <button id="select-addusernotify" class="btn btn-default" type="button">Add user notification ...</button> + {% endif %} </h3> + <div id="details-addusernotify" style="padding-left: 50px; display:none;"> + <p><p> + <button class="execute" id="submit-addusernotify"> Submit </button> + <div class="row"> + <p> + <div id="all-users" class="scrolling" style="width: 300px;"> + {% for user in users %} + <div class="checkbox"> + <label> + <input class="checkbox-users" name="{{user.pk}}" type="checkbox">{{user.name}} + </label> + <p> + </div> + {% endfor %} + </div> + </div> + </div> + <table class="table table-striped table-condensed" data-testid="vuln-hyperlinks-table"> <thead> <tr> @@ -200,16 +265,16 @@ </tr> </thead> - {% if object.investigation_users.all %} - {% for u in object.investigation_users.all %} + {% if object.investigation_notification.all %} + {% for u in object.investigation_notification.all %} <tr> <td>{{ u.user.name }}</td> <td>{{ u.user.email }}</td> - {% if access.is_creator %} + {% if access.is_creator or u.user.name == current_user %} <td> <span id="attachment_entry_'+{{u.id}}+'" class="js-config-var-name"></span> - <span class="glyphicon glyphicon-edit js-icon-pencil-config_var" id="affected_edit_'+{{u.id}}+'" x-data="'+{{u.id}}+'"></span> - <span class="glyphicon glyphicon-trash js-icon-trash-config_var" id="attachment_trash_'+{{u.id}}+'" x-data="'+{{u.id}}+'"></span> + <span class="glyphicon glyphicon-edit edit-usernotification" id="affected_edit_'+{{u.id}}+'" x-data="{{u.id}}"></span> + <span class="glyphicon glyphicon-trash trash-usernotification" id="attachment_trash_'+{{u.id}}+'" x-data="{{u.id}}"></span> </td> {% endif %} </tr> @@ -223,11 +288,31 @@ </div> - <div class="row"> + <div class="row" style="padding-left: 25px;"> <h3>User Access - <a class="btn btn-default navbar-btn " id="new-investigation-access" href="{% url 'login' %}">Add user access</a> + {% if access.is_creator %} + <button id="select-adduseraccess" class="btn btn-default" type="button">Add user access ...</button> + {% endif %} </h3> + <div id="details-adduseraccess" style="padding-left: 50px; display:none;"> + <p><p> + <button class="execute" id="submit-adduseraccess"> Submit </button> + <div class="row"> + <p> + <div id="all-users" class="scrolling" style="width: 300px;"> + {% for user in users %} + <div class="checkbox"> + <label> + <input class="checkbox-users" name="{{user.pk}}" type="checkbox">{{user.name}} + </label> + <p> + </div> + {% endfor %} + </div> + </div> + </div> + <table class="table table-striped table-condensed" data-testid="vuln-hyperlinks-table"> <thead> <tr> @@ -253,8 +338,8 @@ {% if access.is_creator %} <td> <span id="attachment_entry_'+{{u.id}}+'" class="js-config-var-name"></span> - <span class="glyphicon glyphicon-edit js-icon-pencil-config_var" id="affected_edit_'+{{u.id}}+'" x-data="'+{{u.id}}+'"></span> - <span class="glyphicon glyphicon-trash js-icon-trash-config_var" id="attachment_trash_'+{{u.id}}+'" x-data="'+{{u.id}}+'"></span> + <span class="glyphicon glyphicon-edit edit-useraccess" id="affected_edit_'+{{u.id}}+'" x-data="{{u.id}}"></span> + <span class="glyphicon glyphicon-trash trash-useraccess" id="attachment_trash_'+{{u.id}}+'" x-data="{{u.id}}"></span> </td> {% endif %} </tr> @@ -271,7 +356,7 @@ </div> {% endif %} -<div class="row"> +<div class="row" style="padding-left: 25px;"> <h3>History</h3> <table class="table table-striped table-condensed" data-testid="vuln-hyperlinks-table"> <thead> @@ -297,5 +382,287 @@ </table> </div> +<script> + var selected_newcomment=false; + var selected_addusernotify=false; + var selected_adduseraccess=false; + var selected_addattachment=false; + var selected_attachdefect=false; + var selected_trashdefect=false; + + $(document).ready(function() { + function onCommitAjaxSuccess(data, textstatus) { + 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 + ")"); + } + + /* ensure cookie exists {% csrf_token %} */ + function postCommitAjaxRequest(reqdata) { + reqdata["investigation_id"] = {{ object.id }} + var ajax = $.ajax({ + type:"POST", + data: reqdata, + url:"{% url 'xhr_investigation_commit' %}", + headers: { 'X-CSRFToken': $.cookie("csrftoken")}, + success: onCommitAjaxSuccess, + error: onCommitAjaxError, + }); + } + + /* (De-)Select management */ + + $('#select-these').click(function(){ + $(':checkbox').each(function(){ + $(this).prop('checked', true); + }); + }); + + $('#unselect-these').click(function(){ + $(':checkbox').each(function(){ + $(this).prop('checked', false); + }); + }); + + /* Defect management */ + + $('#select-attachdefect').click(function(){ + if (selected_attachdefect) { + selected_attachdefect=false; + $("#details-attachdefect").slideUp(); + } else { + if (selected_trashdefect) { + selected_trashdefect = false; + } + selected_attachdefect=true; + $("#details-trashdefect").slideUp(); + $("#details-attachdefect").slideDown(); + } + }) + + $('#select-trashdefect').click(function(){ + if (selected_trashdefect) { + selected_trashdefect=false; + $("#details-trashdefect").slideUp(); + } else { + if (selected_attachdefect) { + selected_attachdefect = false; + } + selected_trashdefect=true; + $("#details-trashdefect").slideDown(); + $("#details-attachdefect").slideUp(); + } + }) + + $('#submit-attachdefectlist').click(function(){ + var defect_list="" + $(':checkbox').each(function(){ + if ($(this).is(':checked')) { + defect_list += $(this).prop('name') + ","; + } + }); + if ("" == defect_list) { + alert("No users were selected"); + return; + } + postCommitAjaxRequest({ + "action" : 'submit-attachdefectlist', + "defects" : defect_list, + }); + }); + + $('#submit-attachdefect').click(function(){ + var query=$('#text-attachdefect').val().trim() + if (query=="") { + alert("No query given"); + return; + } + postCommitAjaxRequest({ + "action" : 'submit-attachdefect', + "query" : query, + }) + }); + + $('#submit-trashdefect').click(function(){ + var defect_list="" + $(':checkbox').each(function(){ + if ($(this).is(':checked')) { + defect_list += $(this).prop('name') + ","; + } + }); + if ("" == defect_list) { + alert("No users were selected"); + return; + } + postCommitAjaxRequest({ + "action" : 'submit-trashdefect', + "defects" : defect_list, + }); + }); + + /* Comment management */ + + $('#select-newcomment').click(function(){ + if (selected_newcomment) { + selected_newcomment=false; + $("#input-newcomment").slideUp(); + } else { + selected_newcomment=true; + $("#input-newcomment").slideDown(); + } + }); + + $('#submit-newcomment').click(function(){ + var comment=$('#text-newcomment').val().trim() + if (comment=="") { + alert("No comment was written"); + return; + } + postCommitAjaxRequest({ + "action" : 'submit-newcomment', + "comment" : comment, + }) + }); + + $('.trash-comment').click(function(){ + var result = confirm("Are you sure?"); + if (result){ + postCommitAjaxRequest({ + "action" : 'submit-trashcomment', + "record_id" : $(this).attr('x-data'), + }); + } + }) + + /* Attachement management */ + + $('#select-addattachment').click(function() { + if (selected_addattachment) { + selected_addattachment=false; + $("#details-addattachment").slideUp(); + } else { + selected_addattachment=true; + $("#details-addattachment").slideDown(); + } + }); + + $('.submit-downloadattachment').click(function() { + $("#downloadbanner").submit(); + }); + + $('.trash-attachment').click(function() { + var result = confirm("Are you sure?"); + if (result){ + postCommitAjaxRequest({ + "action" : 'submit-trashattachment', + "record_id" : $(this).attr('x-data'), + }); + } + }); + + /* User notify management */ + + $('#select-addusernotify').click(function(){ + if (selected_addusernotify) { + selected_addusernotify=false; + $("#select-these").removeAttr("disabled"); + $("#unselect-these").removeAttr("disabled"); + $("#details-addusernotify").slideUp(); + } else { + selected_addusernotify=true; + $("#select-these").attr("disabled","disabled"); + $("#unselect-these").attr("disabled","disabled"); + $("#details-addusernotify").slideDown(); + } + }); + + $('#submit-addusernotify').click(function(){ + var user_list="" + $(':checkbox').each(function(){ + if ($(this).is(':checked')) { + user_list += $(this).prop('name') + ","; + } + }); + if ("" == user_list) { + alert("No users were selected"); + return; + } + postCommitAjaxRequest({ + "action" : 'submit-addusernotify', + "users" : user_list, + }); + }); + + $('.trash-usernotification').click(function(){ + var result = confirm("Are you sure?"); + if (result){ + postCommitAjaxRequest({ + "action" : 'submit-trashusernotification', + "record_id" : $(this).attr('x-data'), + }); + } + }) + + /* User access management */ + + $('#select-adduseraccess').click(function(){ + if (selected_adduseraccess) { + selected_adduseraccess=false; + $("#select-these").removeAttr("disabled"); + $("#unselect-these").removeAttr("disabled"); + $("#details-adduseraccess").slideUp(); + } else { + selected_adduseraccess=true; + $("#select-these").attr("disabled","disabled"); + $("#unselect-these").attr("disabled","disabled"); + $("#details-adduseraccess").slideDown(); + } + }); + + $('#submit-adduseraccess').click(function(){ + var user_list="" + $(':checkbox').each(function(){ + if ($(this).is(':checked')) { + user_list += $(this).prop('name') + ","; + } + }); + if ("" == user_list) { + alert("No users were selected"); + return; + } + postCommitAjaxRequest({ + "action" : 'submit-adduseraccess', + "users" : user_list, + }); + }); + + $('.trash-useraccess').click(function(){ + var result = confirm("Are you sure?"); + if (result){ + postCommitAjaxRequest({ + "action" : 'submit-trashuseraccess', + "record_id" : $(this).attr('x-data'), + }); + } + }) + + /* Set the report link */ + $('#report_link').attr('href',"{% url 'report' request.resolver_match.url_name %}?record_list={{object.id}}"); + }); +</script> {% endblock %} diff --git a/lib/srtgui/templates/investigations-toastertable.html b/lib/srtgui/templates/investigations-toastertable.html index 2aee7f7e..384d7141 100644 --- a/lib/srtgui/templates/investigations-toastertable.html +++ b/lib/srtgui/templates/investigations-toastertable.html @@ -30,7 +30,8 @@ <h1 class="top-air" data-role="page-title"></h1> </div> - {% url 'cves' as xhr_table_url %} + {# xhr_table_url is just the current url so leave it blank #} + {% url '' as xhr_table_url %} {% include 'toastertable.html' %} </div> </div> @@ -53,6 +54,17 @@ } titleElt.text(title); + + /* Set the report link */ + var record_list="" + $(".name > a").each(function(){ + var this_id=$(this).prop('id'); + if (this_id.startsWith("dataid_")) { + record_list +=this_id.replace(/dataid_/,"") + ","; + } + }); + $('#report_link').attr('href',"{% url 'report' request.resolver_match.url_name %}?record_list="+record_list); + }); }); </script> diff --git a/lib/srtgui/templates/landing.html b/lib/srtgui/templates/landing.html index 52f24482..e0950bd8 100644 --- a/lib/srtgui/templates/landing.html +++ b/lib/srtgui/templates/landing.html @@ -25,7 +25,7 @@ </tr> </thead> - {% if access.is_admin %} + {% if access.is_creator %} <tr> <td><a class="btn btn-info btn-lg" href="{% url 'manage' %}">Management</a></td> <td>Triage CVE's, Create Vulnerabilities, Manage Users</td> @@ -33,7 +33,7 @@ {% endif %} <tr> - <td><a class="btn btn-info btn-lg" href="{% url 'all-cves' %}">CVE's</a></td> + <td><a class="btn btn-info btn-lg" href="{% url 'cves' %}">CVE's</a></td> <td>Common Vulnerblility Enumeration</td> </tr> @@ -48,7 +48,7 @@ </tr> <tr> - <td><a class="btn btn-info btn-lg" href="{% url 'all-defects' %}">Defects</a></td> + <td><a class="btn btn-info btn-lg" href="{% url 'defects' %}">Defects</a></td> <td>SRTool Defects</td> </tr> @@ -58,12 +58,12 @@ </tr> <tr> - <td><a class="btn btn-info btn-lg" href="{% url 'all-cpes' %}">CPE's</a></td> + <td><a class="btn btn-info btn-lg" href="{% url 'cpes' %}">CPE's</a></td> <td>Common Platform Enumeration</td> </tr> <tr> - <td><a class="btn btn-info btn-lg" href="{% url 'all-cwes' %}">CWE's</a></td> + <td><a class="btn btn-info btn-lg" href="{% url 'cwes' %}">CWE's</a></td> <td>Common Weakness Enumeration</td> </tr> diff --git a/lib/srtgui/templates/login.html b/lib/srtgui/templates/login.html new file mode 100644 index 00000000..be597992 --- /dev/null +++ b/lib/srtgui/templates/login.html @@ -0,0 +1,40 @@ +{% extends "base.html" %} + +{% load static %} +{% load projecttags %} +{% load humanize %} + +{% block title %} Login Page {% endblock %} +{% block pagecontent %} + <div class="row"> + <div class="col-md-7" style="padding-left: 50px;"> + <h1>Login Page</h1> + </div> + </div> + + <form method="POST">{% csrf_token %} + <div class="row" style="padding-left: 100px;"> + <h2>Select User:</h2> + {% if object.all %} + <select name="username" size="10" required> + {% for user in object.all %} + <option>{{user.name}} ({{user.get_access_text}})</option> + {% endfor %} + </select> + {% else %} + <p>No Users Found</p> + {% endif%} + </div> + + <div class="row" style="padding-left: 100px;"> + <h2>Password:</h2> + <input type="password" class="form-control" style="width: 150px;" name="password" placeholder="(enter anything)"> + </div> + + <div class="top-air" style="padding-left: 100px;"> + <input type="submit" id="login-button" class="btn btn-primary btn-lg" value="Submit"/> + <a class="btn btn-info btn-lg" href="{% url 'landing' %}">Cancel</a> + </div> + </form> + +{% endblock %} diff --git a/lib/srtgui/templates/manage-cpes-toastertable.html b/lib/srtgui/templates/manage-cpes-toastertable.html index dc4bb800..13fdff80 100644 --- a/lib/srtgui/templates/manage-cpes-toastertable.html +++ b/lib/srtgui/templates/manage-cpes-toastertable.html @@ -36,7 +36,8 @@ <h1 class="top-air" data-role="page-title"></h1> </div> - {% url 'cpes' as xhr_table_url %} + {# xhr_table_url is just the current url so leave it blank #} + {% url '' as xhr_table_url %} {% include 'toastertable.html' %} </div> </div> @@ -60,6 +61,17 @@ } titleElt.text(title); + + /* Set the report link */ + var record_list="" + $(".name > a").each(function(){ + var this_id=$(this).prop('id'); + if (this_id.startsWith("dataid_")) { + record_list +=this_id.replace(/dataid_/,"") + ","; + } + }); + $('#report_link').attr('href',"{% url 'report' request.resolver_match.url_name %}?record_list="+record_list); + }); }); </script> diff --git a/lib/srtgui/templates/management.html b/lib/srtgui/templates/management.html index 2e53ee88..cfdd10d4 100644 --- a/lib/srtgui/templates/management.html +++ b/lib/srtgui/templates/management.html @@ -35,8 +35,8 @@ </tr> <tr> - <td><a class="btn btn-info btn-lg" href="{% url 'publish' %}">Publish Vulnerabilities</a></td> - <td>Process the items that are ready to be published</td> + <td><a class="btn btn-info btn-lg" href="{% url 'publish' %}">Publish Request</a></td> + <td>Process the items that are ready to be published from SRTool</td> </tr> <tr> @@ -44,6 +44,7 @@ <td>Report on the over all response system status</td> </tr> + {% if access.is_admin %} <tr> <td><a class="btn btn-info btn-lg" href="{% url 'users' %}">Manage Users</a></td> <td>Add, edit, and remove users</td> @@ -53,6 +54,7 @@ <td><a class="btn btn-info btn-lg" href="{% url 'sources' %}">Manage Sources</a></td> <td>Manage source list, perform manual pulls</td> </tr> + {% endif %} </table> </div> @@ -65,16 +67,16 @@ <dl class="dl-horizontal"> <dt>CVE's: Total Count =</dt> <dd> - <a href="{% url 'all-cves' %}"> {{cve_total}} </a> + <a href="{% url 'cves' %}"> {{cve_total}} </a> </dd> <dt>Pending triaged =</dt> <dd> - <a href="{% url 'all-cves' %}?limit=25&page=1&orderby=name&filter=is_status:new&default_orderby=name&filter_value=on&"> {{cve_new}} </a> + <a href="{% url 'cves' %}?limit=25&page=1&orderby=name&filter=is_status:new&default_orderby=name&filter_value=on&"> {{cve_new}} </a> </dd> <!-- <dt>Open =</dt> <dd> - <a href="{% url 'all-cves' %}?limit=25&page=1&orderby=name&filter=is_status:open&default_orderby=name&filter_value=on&"> {{cve_open}} </a> + <a href="{% url 'cves' %}?limit=25&page=1&orderby=name&filter=is_status:open&default_orderby=name&filter_value=on&"> {{cve_open}} </a> </dd> --> <dt>Vulnerabilities: Total Count =</dt> @@ -125,7 +127,7 @@ <dt>Defects: Total Count =</dt> <dd> - <a href="{% url 'all-defects' %}" %}> {{defect_total}} </a> + <a href="{% url 'defects' %}" %}> {{defect_total}} </a> </dd> </dl> diff --git a/lib/srtgui/templates/product.html b/lib/srtgui/templates/product.html new file mode 100644 index 00000000..e26bb2fe --- /dev/null +++ b/lib/srtgui/templates/product.html @@ -0,0 +1,84 @@ +{% extends "base.html" %} + +{% load projecttags %} + +{% block title %} {{object.name}} - SRTool {% 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">→</span> + <li><a href="{% url 'products' %}">Products</a></li><span class="divider">→</span> + <li>{{object.name}}</li> + </ul> + </div> +</div> + +<!-- Begin container --> + +<div class="row"> + <div class="col-md-12"> + <div class="page-header build-data"> + <h1>Product {{object.long_name}}Detail</h1> + </div> + </div> +</div> + +<div class="row"> + <div class="col-md-5"> + <div class="well"> + <h2>Quick Info</h2> + <dl class="dl-horizontal"> + <dt>Name:</dt> + <dd>{{object.name}}</dd> + + <dt>Version:</dt> + <dd>{{object.version}}</dd> + + <dt>Profile:</dt> + <dd>{{object.profile}}</dd> + + <dt>Cpe:</dt> + <dd>{{object.cpe}}</dd> + + <dt>Srt Cpe:</dt> + <dd>{{object.srt_cpe}}</dd> + + <dt>Defect Prefix:</dt> + <dd>{{object.defect_prefix }}</dd> + + <dt>Investigations Count/Link:</dt> + <dd> + {% if object.product_investigation.all.count %} + <a href="{% url 'investigations' %}?filter=is_product:{{object.defect_prefix}}&" class="btn btn-info" > + {{object.product_investigation.all.count}} + </a> + {% else %}0{% endif %} + </dd> + + <dt>Defects Count/Link:</dt> + <dd> + {% if object.product_defect.all.count %} + <a href="{% url 'defects' %}?filter=is_product:{{object.defect_prefix}}&" class="btn btn-info" > + {{object.product_defect.all.count}} + </a> + {% else %}0{% endif %} + </dd> + + </dl> + </div> + </div> +</div> + +<!-- Javascript support --> +<script> + $(document).ready(function() { + /* Set the report link */ + $('#report_link').attr('href',"{% url 'report' request.resolver_match.url_name %}?record_list={{object.id}}"); + }); +</script> + +{% endblock %} diff --git a/lib/srtgui/templates/products-toastertable.html b/lib/srtgui/templates/products-toastertable.html index 65602be7..7e12b96b 100644 --- a/lib/srtgui/templates/products-toastertable.html +++ b/lib/srtgui/templates/products-toastertable.html @@ -30,7 +30,8 @@ <h1 class="top-air" data-role="page-title"></h1> </div> - {% url 'cves' as xhr_table_url %} + {# xhr_table_url is just the current url so leave it blank #} + {% url '' as xhr_table_url %} {% include 'toastertable.html' %} </div> </div> @@ -53,6 +54,17 @@ } titleElt.text(title); + + /* Set the report link */ + var record_list="" + $(".name > a").each(function(){ + var this_id=$(this).prop('id'); + if (this_id.startsWith("dataid_")) { + record_list +=this_id.replace(/dataid_/,"") + ","; + } + }); + $('#report_link').attr('href',"{% url 'report' request.resolver_match.url_name %}?record_list="+record_list); + }); }); </script> diff --git a/lib/srtgui/templates/publish-select-toastertable.html b/lib/srtgui/templates/publish-select-toastertable.html new file mode 100644 index 00000000..5a439c63 --- /dev/null +++ b/lib/srtgui/templates/publish-select-toastertable.html @@ -0,0 +1,248 @@ +{% extends 'base.html' %} +{% load static %} + + +{% block extraheadcontent %} + <link rel="stylesheet" href="{% static 'css/jquery-ui.min.css' %}" type='text/css'> + <link rel="stylesheet" href="{% static 'css/jquery-ui.structure.min.css' %}" type='text/css'> + <link rel="stylesheet" href="{% static 'css/jquery-ui.theme.min.css' %}" type='text/css'> + <script src="{% static 'js/jquery-ui.min.js' %}"> + </script> + {% if access.is_creator %}{% else %}<meta http-equiv="refresh" content="0; url=/" />{% endif %} + + <style> + /* Style the execution buttons */ + button.execute { height:50px;width:210px;background-color:#4CAF50;text-align:center; border:2px #f69c55;border-radius: 12px; } + + button:disabled { + cursor: not-allowed; + } + + /* Create two equal columns that floats next to each other */ + .column { + float: left; + width: 350px; + padding: 10px; + } + + /* Clear floats after the columns */ + .row:after { + content: ""; + display: table; + clear: both; + } + </style> + +{% endblock %} + +{% block title %} Publish CVE's - SRTool {% 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">→</span> + <li><a href="{% url 'manage' %}">Management</a></li><span class="divider">→</span> + <li><a href="{% url 'publish' %}">Publish</a></li><span class="divider">→</span> + <li>Publish CVE's</li> + </ul> + </div> + </div> + + <div id="change-publish-state" > <!--class="form-inline" --> + <b><big>Actions: </big></b> + <button id="select-these" class="btn btn-default" type="button">Select these</button> + <button id="unselect-these" class="btn btn-default" type="button">Un-select these</button> + <button id="select-publish" class="btn btn-default" type="button">Update ...</button> + <!--<button id="collect-publish" class="btn btn-default" type="button">Collect Publishable CVEs</button> --> + </div> + + <div id="details-publish" style="display:none;"> + <p><p> + <button class="execute" id="submit-publish"> Update Publish CVE State </button> + <p><b><big>New Publish State: </big></b> + <select name="Publish" id="select-publish-state"> + <option value="0" >Unpublished</option> + <option value="1" >Not to be Published</option> + <option value="2" >Published</option> + <option value="3" >Publish Request (New)</option> + <option value="4" >Publish Request (Update)</option> + <option value="5" selected>Publish Submitted</option> + </select> + </div> + + <div class="row"> + <div class="col-md-12"> + <div class="page-header"> + <h1 class="top-air" data-role="page-title"></h1> + </div> + + {% url '' as xhr_table_url %} + {% include 'toastertable.html' %} + </div> + </div> + + <!-- Javascript support --> + <script> + + //# sourceURL=somename.js + + // global variables + var selected_publish=false; + var cve_total=0; + + $(document).ready(function() { + + function onCommitAjaxSuccess(data, textstatus) { + 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 + ")"); + } + + /* ensure cookie exists {% csrf_token %} */ + function postCommitAjaxRequest(reqdata) { + var ajax = $.ajax({ + type:"POST", + data: reqdata, + url:"{% url 'xhr_cve_publish_commit'%}", + headers: { 'X-CSRFToken': $.cookie("csrftoken")}, + success: onCommitAjaxSuccess, + error: onCommitAjaxError, + }) + } + + function update_publish_status() { + var cve_count = 0; + var cve_checked_count = 0; + $('#selectpublishtable input').each(function(){ + cve_count = cve_count + 1; + if ($(this).is(':checked')) { + cve_checked_count = cve_checked_count + 1; + } + }); + if (cve_total > cve_count) { + document.getElementById("select-these").innerText = "Select "+(cve_count-cve_checked_count)+" unchecked ("+(cve_total-cve_count)+" offpage)"; + } else { + document.getElementById("select-these").innerText = "Select "+(cve_count-cve_checked_count)+" unchecked"; + } + document.getElementById("unselect-these").innerText = "Un-select "+cve_checked_count+" checked"; + if (0 == cve_checked_count) { + //$("#submit-publish").attr("disabled","disabled"); + document.getElementById("submit-publish").disabled = true; + } else { + //$("#submit-publish").removeAttr("disabled"); + document.getElementById("submit-publish").disabled = false; + } + } + + $('#select-these').click(function(){ + $('#selectpublishtable input').each(function(){ + $(this).prop('checked', true); + }); + update_publish_status(); + }); + + $('#unselect-these').click(function(){ + $('#selectpublishtable input').each(function(){ + $(this).prop('checked', false); + }); + update_publish_status(); + }); + + // Open Publish Action + $('#select-publish').click(function(){ + if (selected_publish) { + selected_publish=false; + $("#select-these").removeAttr("disabled"); + $("#unselect-these").removeAttr("disabled"); + $("#details-publish").slideUp(); + } else { + selected_publish=true; + $("#select-these").attr("disabled","disabled"); + $("#unselect-these").attr("disabled","disabled"); + update_publish_status(); + $("#details-publish").slideDown(); + } + }); + + $('#submit-publish').click(function(){ + var cve_list=""; + $('#selectpublishtable input').each(function(){ + if ($(this).is(':checked')) { + cve_list +=$(this).prop('name') + ","; + } + }); + if ("" == cve_list) { + alert("No CVE's were selected"); + return; + } + publish_state=$('#select-publish-state').val(); + postCommitAjaxRequest({ + "publish_state" : publish_state, + "cve_list" : cve_list, + }); + + }); + + // When change in product selections, update labels and enables + $(document).on("change", "#all-products :checkbox", function() { + update_publish_status(); + }); + + // When change in CVE selections, update labels and enables + $(document).on("change", "#selectpublishtable :checkbox", function() { + update_publish_status(); + }); + + + // Standard Toaster Table enablement + + var tableElt = $("#{{table_name}}"); + var titleElt = $("[data-role='page-title']"); + + tableElt.on("table-done", function (e, total, tableParams) { + var title = "Publish CVE's"; + + if (tableParams.search || tableParams.filter) { + if (total === 0) { + title = "No CVE's found"; + } + else if (total > 0) { + title = total + " CVE" + (total > 1 ? "'s" : '') + " found"; + } + } + + titleElt.text(title); + cve_total = total; + update_publish_status(); + + /* Set the report link */ + var record_list="" + $(".name > a").each(function(){ + var this_id=$(this).prop('id'); + if (this_id.startsWith("dataid_")) { + record_list +=this_id.replace(/dataid_/,"") + ","; + } + }); + $('#report_link').attr('href',"{% url 'report' request.resolver_match.url_name %}?record_list="+record_list); + }); + + }); + </script> +{% endblock %} diff --git a/lib/srtgui/templates/publish.html b/lib/srtgui/templates/publish.html index 2b65e81b..b1f3d83f 100644 --- a/lib/srtgui/templates/publish.html +++ b/lib/srtgui/templates/publish.html @@ -7,25 +7,38 @@ {% block title %} Publish Requests {% endblock %} {% block pagecontent %} - <div class="row"> - <div class="col-md-7" style="padding-left: 50px;"> +<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">→</span> <li><a href="{% url 'manage' %}">Management</a></li><span class="divider">→</span> - <h1>Publish (Proposals)</h1> - </div> - </div> + <li>Publish (Proposals)</li> + </ul> + </div> +</div> <h2> Manage Publish Requests</h2> <ul> - <li>This interface can review and accept the list of vulnerabilities that have readhed their publication date</li> - <li>This is preferable over automatic publishing for when the defect and/or official publish date are not very certain</li> + <li>The SRTool supports an external publishing tool, for example a business table or the vendor's public website</li> + <li>These tools can be used to (a) submit CVEs to that tool, and (b) update the CVEs when they have been published</li> </ul> <h2> Publishing Actions</h2> <ul> - <li>Format (and hopefully automatically register) the Vulnerability information to the company website</li> - <li>Format a notice for TechPubs</li> - <li>Format a notice for the respective product leads</li> + <tr> + <td><a class="btn btn-info btn-lg" href="{% url 'select-publish' %}">Publish Request</a></td> + <td>Process the items that are ready to be published from SRTool</td> + </tr> + + <br> + <br> + <br> + + <tr> + <td><a class="btn btn-info btn-lg" href="{% url 'update-published' %}">Published Update</a></td> + <td>Process the items that have been published</td> + </tr> </ul> {% endblock %} diff --git a/lib/srtgui/templates/published-select-toastertable.html b/lib/srtgui/templates/published-select-toastertable.html new file mode 100644 index 00000000..df89d142 --- /dev/null +++ b/lib/srtgui/templates/published-select-toastertable.html @@ -0,0 +1,247 @@ +{% extends 'base.html' %} +{% load static %} + + +{% block extraheadcontent %} + <link rel="stylesheet" href="{% static 'css/jquery-ui.min.css' %}" type='text/css'> + <link rel="stylesheet" href="{% static 'css/jquery-ui.structure.min.css' %}" type='text/css'> + <link rel="stylesheet" href="{% static 'css/jquery-ui.theme.min.css' %}" type='text/css'> + <script src="{% static 'js/jquery-ui.min.js' %}"> + </script> + {% if access.is_creator %}{% else %}<meta http-equiv="refresh" content="0; url=/" />{% endif %} + + <style> + /* Style the execution buttons */ + button.execute { height:50px;width:210px;background-color:#4CAF50;text-align:center; border:2px #f69c55;border-radius: 12px; } + + button:disabled { + cursor: not-allowed; + } + + /* Create two equal columns that floats next to each other */ + .column { + float: left; + width: 350px; + padding: 10px; + } + + /* Clear floats after the columns */ + .row:after { + content: ""; + display: table; + clear: both; + } + </style> + +{% endblock %} + +{% block title %} Publish Reqested CVE's - SRTool {% 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">→</span> + <li><a href="{% url 'manage' %}">Management</a></li><span class="divider">→</span> + <li><a href="{% url 'publish' %}">Publish</a></li><span class="divider">→</span> + <li>Publish Requested Update CVE's</li> + </ul> + </div> + </div> + + <div id="change-publish-state" > <!--class="form-inline" --> + <b><big>Actions: </big></b> + <button id="select-these" class="btn btn-default" type="button">Select these</button> + <button id="unselect-these" class="btn btn-default" type="button">Un-select these</button> + <button id="select-publish" class="btn btn-default" type="button">Update ...</button> + <!--<button id="collect-publish" class="btn btn-default" type="button">Collect Publishable CVEs</button> --> + </div> + + <div id="details-publish" style="display:none;"> + <p><p> + <button class="execute" id="submit-publish"> Update Publish CVE State </button> + <p><b><big>New Publish State: </big></b> + <select name="Publish" id="select-publish-state"> + <option value="0" >Unpublished</option> + <option value="1" >Not to be Published</option> + <option value="2" selected>Published</option> + <option value="3" >Publish Request (New)</option> + <option value="4" >Publish Request (Update)</option> + <option value="5" >Publish Submitted</option> + </select> + </div> + + <div class="row"> + <div class="col-md-12"> + <div class="page-header"> + <h1 class="top-air" data-role="page-title"></h1> + </div> + + {% url '' as xhr_table_url %} + {% include 'toastertable.html' %} + </div> + </div> + + <!-- Javascript support --> + <script> + + //# sourceURL=somename.js + + // global variables + var selected_publish=false; + var cve_total=0; + + $(document).ready(function() { + + function onCommitAjaxSuccess(data, textstatus) { + 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 + ")"); + } + + /* ensure cookie exists {% csrf_token %} */ + function postCommitAjaxRequest(reqdata) { + var ajax = $.ajax({ + type:"POST", + data: reqdata, + url:"{% url 'xhr_cve_publish_commit' %}", + headers: { 'X-CSRFToken': $.cookie("csrftoken")}, + success: onCommitAjaxSuccess, + error: onCommitAjaxError, + }) + } + + function update_publish_status() { + var cve_count = 0; + var cve_checked_count = 0; + $('#updatepublishedtable input').each(function(){ + cve_count = cve_count + 1; + if ($(this).is(':checked')) { + cve_checked_count = cve_checked_count + 1; + } + }); + if (cve_total > cve_count) { + document.getElementById("select-these").innerText = "Select "+(cve_count-cve_checked_count)+" unchecked ("+(cve_total-cve_count)+" offpage)"; + } else { + document.getElementById("select-these").innerText = "Select "+(cve_count-cve_checked_count)+" unchecked"; + } + document.getElementById("unselect-these").innerText = "Un-select "+cve_checked_count+" checked"; + if (0 == cve_checked_count) { + //$("#submit-publish").attr("disabled","disabled"); + document.getElementById("submit-publish").disabled = true; + } else { + //$("#submit-publish").removeAttr("disabled"); + document.getElementById("submit-publish").disabled = false; + } + } + + $('#select-these').click(function(){ + $('#updatepublishedtable input').each(function(){ + $(this).prop('checked', true); + }); + update_publish_status(); + }); + + $('#unselect-these').click(function(){ + $('#updatepublishedtable input').each(function(){ + $(this).prop('checked', false); + }); + update_publish_status(); + }); + + // Open Publish Action + $('#select-publish').click(function(){ + if (selected_publish) { + selected_publish=false; + $("#select-these").removeAttr("disabled"); + $("#unselect-these").removeAttr("disabled"); + $("#details-publish").slideUp(); + } else { + selected_publish=true; + $("#select-these").attr("disabled","disabled"); + $("#unselect-these").attr("disabled","disabled"); + update_publish_status(); + $("#details-publish").slideDown(); + } + }); + + $('#submit-publish').click(function(){ + var cve_list=""; + $('#updatepublishedtable input').each(function(){ + if ($(this).is(':checked')) { + cve_list +=$(this).prop('name') + ","; + } + }); + if ("" == cve_list) { + alert("No CVE's were selected"); + return; + } + publish_state=$('#select-publish-state').val(); + postCommitAjaxRequest({ + "publish_state" : publish_state, + "cve_list" : cve_list, + }); + +// $.post("/srtgui/export/publish_cve", {parent_page:'publish_cve',cve_list:cve_list,report_type:$('#report_type').val(),report_format:$('#report_format').val(),csrfmiddlewaretoken:'{{ csrf_token }}'}, function(result){ +// alert(result); +// }); + + }); + + // When change in CVE selections, update labels and enables + $(document).on("change", "#updatepublishedtable :checkbox", function() { + update_publish_status(); + }); + + + // Standard Toaster Table enablement + + var tableElt = $("#{{table_name}}"); + var titleElt = $("[data-role='page-title']"); + + tableElt.on("table-done", function (e, total, tableParams) { + var title = "Publish Requested CVE's"; + + if (tableParams.search || tableParams.filter) { + if (total === 0) { + title = "No CVE's found"; + } + else if (total > 0) { + title = total + " CVE" + (total > 1 ? "'s" : '') + " found"; + } + } + + titleElt.text(title); + cve_total = total; + update_publish_status(); + + /* Set the report link */ + var record_list="" + $(".name > a").each(function(){ + var this_id=$(this).prop('id'); + if (this_id.startsWith("dataid_")) { + record_list += this_id.replace(/dataid_/,"") + ","; + } + }); + $('#report_link').attr('href',"{% url 'report' request.resolver_match.url_name %}?record_list="+record_list); + + }); + }); + </script> +{% endblock %} diff --git a/lib/srtgui/templates/report.html b/lib/srtgui/templates/report.html new file mode 100644 index 00000000..d4d27f76 --- /dev/null +++ b/lib/srtgui/templates/report.html @@ -0,0 +1,73 @@ +{% extends "base.html" %} + +{% load static %} +{% load projecttags %} +{% load humanize %} + +{% block title %} Report/Export {% endblock %} + +{% block pagecontent %} + <div class="row"> + <div class="col-md-7" style="padding-left: 50px;"> + <h1>Report/Export: {{title}}</h1> + </div> + </div> + + <form method="POST">{% csrf_token %} + <input type="hidden" name="parent_page" value="{{parent_page}}"> + <input type="hidden" name="record_list" value="{{record_list}}"> + + {% if report_type_list %} + <hr> + Report Type:<br> + <select name="report_type"> + {{report_type_list|safe}} + </select> + {% else %} + <hr> + Note: There is no report defined for this page.<br> + {% endif %} + + {% if report_get_title %} + <hr> + Title:<br> + <input type="text" name="title"> + <br> + {% endif %} + <hr> + + {% if report_recordrange_list %} + Record Range:<br> + {{report_recordrange_list|safe}} + <br> + {% endif %} + {% if report_columnrange_list %} + Column Range:<br> + {{report_columnrange_list|safe}} + {% endif %} + <hr> + + {% if report_format_list %} + Export Format:<br> + {{report_format_list|safe}} + <hr> + {% endif %} + + {% if report_custom_list %} + Page Specific Settings:<br> + {{report_custom_list|safe}} + <hr> + {% endif %} + + {% if report_enable_submit %} + <input type="submit" id="submit-report-button" class="btn btn-primary btn-lg" value="Generate and Download Report"/> + <a class="btn btn-info btn-lg" id="report-done" href="{% url 'landing' %}">Done</a> + {% else %} + <a class="btn btn-info btn-lg" id="report-cancel" href="{% url 'landing' %}">Cancel</a> + {% endif %} + + </form> + +<br> +{% endblock %} + diff --git a/lib/srtgui/templates/sources-toastertable.html b/lib/srtgui/templates/sources-toastertable.html new file mode 100644 index 00000000..1721e3b0 --- /dev/null +++ b/lib/srtgui/templates/sources-toastertable.html @@ -0,0 +1,72 @@ +{% extends 'base.html' %} +{% load static %} + +{% block extraheadcontent %} + <link rel="stylesheet" href="{% static 'css/jquery-ui.min.css' %}" type='text/css'> + <link rel="stylesheet" href="{% static 'css/jquery-ui.structure.min.css' %}" type='text/css'> + <link rel="stylesheet" href="{% static 'css/jquery-ui.theme.min.css' %}" type='text/css'> + <script src="{% static 'js/jquery-ui.min.js' %}"> + </script> +{% endblock %} + +{% block title %} Data Sources - SRTool {% 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">→</span> + <li><a href="{% url 'manage' %}">Management</a></li><span class="divider">→</span> + <li>Data Sources</li> + </ul> + </div> +</div> + + +<div class="row"> + <div class="col-md-12"> + <div class="page-header"> + <h1 class="top-air" data-role="page-title"></h1> + </div> + + {# xhr_table_url is just the current url so leave it blank #} + {% url '' as xhr_table_url %} + {% include 'toastertable.html' %} + </div> +</div> + + <script> + $(document).ready(function () { + var tableElt = $("#{{table_name}}"); + var titleElt = $("[data-role='page-title']"); + + tableElt.on("table-done", function (e, total, tableParams) { + var title = "Data Sources"; + + if (tableParams.search || tableParams.filter) { + if (total === 0) { + title = "No Data Sources found"; + } + else if (total > 0) { + title = total + " Data Source" + (total > 1 ? 's' : '') + " found"; + } + } + + titleElt.text(title); + + /* Set the report link */ + var record_list="" + $(".data > span").each(function(){ + var this_id=$(this).prop('id'); + if (this_id.startsWith("dataid_")) { + record_list +=this_id.replace(/dataid_/,"") + ","; + } + }); + $('#report_link').attr('href',"{% url 'report' request.resolver_match.url_name %}?record_list="+record_list); + + }); + }); + </script> +{% endblock %} diff --git a/lib/srtgui/templates/triage_cves.html b/lib/srtgui/templates/triage_cves.html index 7325dce8..1df170ab 100644 --- a/lib/srtgui/templates/triage_cves.html +++ b/lib/srtgui/templates/triage_cves.html @@ -22,12 +22,18 @@ </ul> <a class="btn btn-info btn-lg" href="{% url 'keywords' %}">Manage Keyword Filters</a></td> -<h2> Guided Bulk Triage </h2> +<h2> New CVEs Triage </h2> <ul> - <li>Select a range of similar CVE's via scoring, string matches, and filters</li> + <li>Select a range of similar new incoming CVE's via scoring, string matches, and filters</li> <li>You can then (a) review/sample them, (b) select all or some, and then (c) click a button to bulk assign them</li> </ul> <a class="btn btn-info btn-lg" href="{% url 'select-cves' %}">Incoming CVE Triage Page</a> + +<h2> Updated CVEs Triage</h2> +<ul> + <li>Select a range of similar updated CVE's via scoring, string matches, and filters</li> + <li>You can then (a) review/sample them, (b) select all or some, and then (c) click a button to bulk assign them</li> +</ul> <a class="btn btn-info btn-lg" href="{% url 'tbd' %}">Updated CVE Triage Page</a> <h2> Guided Manual Triage </h2> diff --git a/lib/srtgui/templates/users.html b/lib/srtgui/templates/users.html index f934e552..3837b1fb 100644 --- a/lib/srtgui/templates/users.html +++ b/lib/srtgui/templates/users.html @@ -42,7 +42,7 @@ </div> </div> -<div class="row"> +<div class="row" style="padding-left: 25px;"> <h3>User List <a class="btn btn-default navbar-btn " id="new-investigation-attachement" href="{% url 'login' %}">Add user</a> </h3> @@ -59,17 +59,17 @@ </thead> {% if object.all %} - {% for p in object.all %} + {% for user in object.all %} <tr> - <td>{{ p.name }} </td> - <td>{{ p.email }} </td> - <td>{{ p.role }} </td> - <td>{{ p.get_access_text }} </td> + <td>{{ user.name }} </td> + <td>{{ user.email }} </td> + <td>{{ user.role }} </td> + <td>{{ user.get_access_text }} </td> <td> - {% if p.name != "Guest" %} - <span id="user_'+{{p.id}}+'" class="js-user-name"></span> - <span class="glyphicon glyphicon-edit js-icon-pencil-config_var" id="user_'+{{p.id}}+'" x-data="'+{{p.id}}+'"></span> - <span class="glyphicon glyphicon-trash js-icon-trash-config_var" id="user_'+{{p.id}}+'" x-data="'+{{p.id}}+'"></span> + {% if not user.builtin %} + <span id="user_'+{{user.id}}+'" class="js-user-name"></span> + <span class="glyphicon glyphicon-edit js-icon-pencil-config_var" id="user_'+{{user.id}}+'" x-data="'+{{user.id}}+'"></span> + <span class="glyphicon glyphicon-trash js-icon-trash-config_var" id="user_'+{{user.id}}+'" x-data="'+{{user.id}}+'"></span> {% endif %} </td> diff --git a/lib/srtgui/templates/vulnerabilities-toastertable.html b/lib/srtgui/templates/vulnerabilities-toastertable.html index 27d8bea4..5a5bae50 100644 --- a/lib/srtgui/templates/vulnerabilities-toastertable.html +++ b/lib/srtgui/templates/vulnerabilities-toastertable.html @@ -30,7 +30,8 @@ <h1 class="top-air" data-role="page-title"></h1> </div> - {% url 'cves' as xhr_table_url %} + {# xhr_table_url is just the current url so leave it blank #} + {% url '' as xhr_table_url %} {% include 'toastertable.html' %} </div> </div> @@ -53,7 +54,21 @@ } titleElt.text(title); + + /* Set the report link */ + var record_list="" + $(".name > a").each(function(){ + /* if ('id' in $(this)) { */ + var this_id=$(this).prop('id'); + if (this_id.startsWith("dataid_")) { + record_list +=this_id.replace(/dataid_/,"") + ","; + } + /* } */ + }); + $('#report_link').attr('href',"{% url 'report' request.resolver_match.url_name %}?record_list="+record_list); + }); }); </script> {% endblock %} + diff --git a/lib/srtgui/templates/vulnerability.html b/lib/srtgui/templates/vulnerability.html index b238f7fc..ec0df78b 100644 --- a/lib/srtgui/templates/vulnerability.html +++ b/lib/srtgui/templates/vulnerability.html @@ -1,9 +1,32 @@ {% extends "base.html" %} - {% load projecttags %} -{% block title %} {{object.name}} - SRTool {% endblock %} +{% block extraheadcontent %} + <style> + /* Style the execution buttons */ + /*button.execute { height:50px;width:210px;background-color:#4CAF50;text-align:center; border:2px #f69c55;border-radius: 12px; }*/ + + button:disabled { + cursor: not-allowed; + } + + /* Create two equal columns that floats next to each other */ + .column { + float: left; + width: 350px; + padding: 10px; + } + + /* Clear floats after the columns */ + .row:after { + content: ""; + display: table; + clear: both; + } + </style> +{% endblock %} +{% block title %} {{object.name}} - SRTool {% endblock %} {% block pagecontent %} <div class="row"> @@ -56,9 +79,10 @@ </div> <div class="col-md-5"> <div class="well"> - <h2>Quick Info</h2> - - + <h2>Quick Info + {% if access.is_creator %} + <button id="select-quickedit" class="btn btn-default" type="button">Edit ...</button> + {% endif %}</h2> <dl class="dl-horizontal"> <dt>CVE Dictionary Entry:</dt> <dd> @@ -98,18 +122,104 @@ <dd> {{object.get_severity_text}} </dd> + <dt>Note:</dt> + <dd> + {{object.comments}} + </dd> + <dt>Private Note:</dt> + <dd> + {{object.comments_private}} + </dd> + + <div id="details-quickedit" style="display:none;"> + <p><p> + <button class="execute" id="submit-quickedit"> Submit Changes </button> + <p><input type="text" placeholder="Edit Note" id="text-note" size="40" value="{{object.comments}}"></p> + <p><input type="text" placeholder="Edit Private Note" id="text-private-note" size="40" value="{{object.comments_private}}"></p> + <p><p> + <div class="row"> + <div class="column"> + <p><b><label id="set_status">Set Status:</label></b> + <div id="status-list" class="scrolling" style="width: 300px;"> + <div class="checkbox"> <label> + <input type="radio" name="status" value="2" type="checkbox" {%if 2 == object.status %}checked="checked"{% endif %}> Vulnerable + </label><p></div> + <div class="checkbox"> <label> + <input type="radio" name="status" value="1" type="checkbox" {%if 1 == object.status %}checked="checked"{% endif %}> Not Vulnerable + </label><p></div> + <div class="checkbox"> <label> + <input type="radio" name="status" value="0" type="checkbox" {%if 0 == object.status %}checked="checked"{% endif %}> Investiage + </label><p></div> + </div> + </div> + <div class="column"> + <p><b><label id="set_outcome">Set Outcome:</label></b> + <div id="outcome-list" class="scrolling" style="width: 300px;"> + <div class="checkbox"> <label> + <input type="radio" name="outcome" value="3" type="checkbox" {%if 3 == object.outcome %}checked="checked"{% endif %}> Not Fix + </label><p></div> + <div class="checkbox"> <label> + <input type="radio" name="outcome" value="2" type="checkbox" {%if 2 == object.outcome %}checked="checked"{% endif %}> Closed (Not Vulnerable) + </label><p></div> + <div class="checkbox"> <label> + <input type="radio" name="outcome" value="1" type="checkbox" {%if 1 == object.outcome %}checked="checked"{% endif %}> Closed (Fixed) + </label><p></div> + <div class="checkbox"> <label> + <input type="radio" name="outcome" value="0" type="checkbox" {%if 0 == object.outcome %}checked="checked"{% endif %}> Open + </label><p></div> + </div> + </div> + <div class="column"> + <p><b><label id="set_severity">Set Severity:</label></b> + <div id="severity-list" class="scrolling" style="width: 300px;"> + <div class="checkbox"> <label> + <input type="radio" name="severity" value="4" type="checkbox" {%if 4 == object.severity %}checked="checked"{% endif %}> High + </label><p></div> + <div class="checkbox"> <label> + <input type="radio" name="severity" value="3" type="checkbox" {%if 3 == object.severity %}checked="checked"{% endif %}> Medium + </label><p></div> + <div class="checkbox"> <label> + <input type="radio" name="severity" value="2" type="checkbox" {%if 2 == object.severity %}checked="checked"{% endif %}> Low + </label><p></div> + <div class="checkbox"> <label> + <input type="radio" name="severity" value="1" type="checkbox" {%if 0 == object.severity %}checked="checked"{% endif %}> Minor + </label><p></div> + <div class="checkbox"> <label> + <input type="radio" name="severity" value="0" type="checkbox" {%if 0 == object.severity %}checked="checked"{% endif %}> Undefined + </label><p></div> + </div> + </div> + </div> + </div> </dl> </div> </div> </div> -<div class="row"> +<div class="row" style="padding-left: 25px;"> <h3>Affected Products {% if access.is_creator %} - <a class="btn btn-default navbar-btn " id="new-investigation-attachement" href="{% url 'login' %}">Add product</a> + <button id="select-addaffectedproduct" class="btn btn-default" type="button">Add product ...</button> {% endif %} </h3> + <div id="details-addaffectedproduct" style="padding-left: 50px; display:none;"> + <p><p> + <button class="execute" id="submit-addaffectedproduct"> Submit </button> + <div class="row"> + <div id="all-affectedproducts" class="scrolling" style="width: 300px;"> + {% for product in products %} + <div class="checkbox"> + <label> + <input class="checkbox-products" name="{{product.pk}}" type="checkbox">{{product.long_name}} + </label> + <p> + </div> + {% endfor %} + </div> + </div> + </div> + <table class="table table-striped table-condensed" data-testid="vuln-hyperlinks-table"> <thead> <tr> @@ -128,19 +238,20 @@ {% if object.get_affected_list.all %} {% for p in object.get_affected_list.all %} <tr> - <td>{{ p.product.name }} {{ p.product.version }} {{ p.product.profile }} </td> + {% if p.product != '%s' %} <!-- hack for null records --> + <td><a href="{% url 'product' p.product.id %}">{{ p.product.long_name }}<a></td> {% if p.investigation %} <td><a href="{% url 'investigation' p.investigation.id %}">{{ p.investigation.name }}<a></td> <td>{{ p.investigation.get_status_text }}</td> <td>{{ p.investigation.get_outcome_text }}</td> <td> {% for ij in p.investigation.investigation_to_defect.all %} - {% if not forloop.first %}| {% endif %}<a href="http://defect.wrs.com/browse/{{ij.defect.name}}" target="_blank">{{ij.defect.name}} </a> + {% if not forloop.first %}| {% endif %}<a href="{% url 'defect' ij.defect.id %}">{{ij.defect.name}} </a> {% endfor %} </td> <td> {% for ij in p.investigation.investigation_to_defect.all %} - {% if not forloop.first %}| {% endif %}<a href="http://defect.wrs.com/browse/{{ij.defect.name}}" target="_blank">{{ij.defect.release_version}} </a> + {% if not forloop.first %}| {% endif %}<a href="{% url 'defect' ij.defect.id %}">{{ij.defect.release_version}} </a> {% endfor %} </td> {% else %} @@ -153,10 +264,10 @@ {% if access.is_creator %} <td> <span id="affected_entry_'+{{p.id}}+'" class="js-config-var-name"></span> - <span class="glyphicon glyphicon-edit js-icon-pencil-config_var" id="affected_edit_'+{{p.id}}+'" x-data="'+{{p.id}}+'"></span> - <span class="glyphicon glyphicon-trash js-icon-trash-config_var" id="affected_trash_'+{{p.id}}+'" x-data="'+{{p.id}}+'"></span> + <span class="glyphicon glyphicon-trash trash-affected" id="affected_trash_'+{{p.id}}+'" x-data="{{p.id}}"></span> </td> {% endif %} + {% endif %} </tr> {% endfor %} {% else %} @@ -168,13 +279,30 @@ </div> -<div class="row"> +<div class="row" style="padding-left: 25px;"> <h3>Related Products {% if access.is_creator %} - <a class="btn btn-default navbar-btn " id="new-investigation-attachement" href="{% url 'login' %}">Add product</a> + <button id="select-addrelatedproduct" class="btn btn-default" type="button">Add product ...</button> {% endif %} </h3> + <div id="details-addrelatedproduct" style="padding-left: 50px; display:none;"> + <p><p> + <button class="execute" id="submit-addrelatedproduct"> Submit </button> + <div class="row"> + <div id="all-relatedproducts" class="scrolling" style="width: 300px;"> + {% for product in products %} + <div class="checkbox"> + <label> + <input class="checkbox-products" name="{{product.pk}}" type="checkbox">{{product.long_name}} + </label> + <p> + </div> + {% endfor %} + </div> + </div> + </div> + <table class="table table-striped table-condensed" data-testid="vuln-hyperlinks-table"> <thead> <tr> @@ -188,15 +316,16 @@ {% if object.get_related_list.all %} {% for p in object.get_related_list.all %} <tr> - <td>{{ p.product.name }} {{ p.product.version }} {{ p.product.profile }} </td> + {% if p.product != '%s' %} <!-- hack for null records --> + <td><a href="{ % url 'product' p.product.id % }">{{p.product.long_name}}</a></td> <td>Not vulnerable</td> {% if access.is_creator %} <td> <span id="related_var_entry_'+{{p.id}}+'" class="js-config-var-name"></span> - <span class="glyphicon glyphicon-edit js-icon-pencil-config_var" id="affected_edit_'+{{p.id}}+'" x-data="'+{{p.id}}+'"></span> - <span class="glyphicon glyphicon-trash js-icon-trash-config_var" id="related_trash_'+{{p.id}}+'" x-data="'+{{p.id}}+'"></span> + <span class="glyphicon glyphicon-trash trash-related" id="related_trash_'+{{p.id}}+'" x-data="{{p.id}}"></span> </td> {% endif %} + {% endif %} </tr> {% endfor %} {% else %} @@ -209,13 +338,17 @@ </div> -<div class="row"> +<div class="row" style="padding-left: 25px;"> <h3>Comments {% if access.is_creator %} - <a class="btn btn-default navbar-btn " id="new-investigation-comment" href="{% url 'login' %}">Add comment</a> + <button id="select-newcomment" class="btn btn-default" type="button">Add comment ...</button> {% endif %} </h3> +<div id="input-newcomment" style="padding-left: 50px; display:none;"> + <p><input type="text" id="text-newcomment" size="40"> <button class="execute" id="submit-newcomment"> Submit </button></p> +</div> + <table class="table table-striped table-condensed" data-testid="vuln-hyperlinks-table"> <thead> <tr> @@ -234,11 +367,10 @@ <td>{{ c.comment }}</td> <td>{{ c.date }}</td> <td>{{ c.author }}</td> - {% if access.is_creator %} + {% if access.is_creator or c.author == access.current_user_name %} <td> <span id="config_var_entry_'+configvars_sorted[i][2]+'" class="js-config-var-name"></span> - <span class="glyphicon glyphicon-edit js-icon-pencil-config_var" id="comment_edit_'+{{c.id}}+'" x-data="'+{{c.id}}+'"></span> - <span class="glyphicon glyphicon-trash js-icon-trash-config_var" id="comment_trash_'+{{c.id}}+'" x-data="'+{{c.id}}+'"></span> + <span class="glyphicon glyphicon-trash trash-comment" id="comment_trash_'+{{c.id}}+'" x-data="{{c.id}}"></span> </td> {% endif %} </tr> @@ -252,13 +384,25 @@ </div> -<div class="row"> +<div class="row" style="padding-left: 25px;"> <h3>Attachments {% if access.is_creator %} - <a class="btn btn-default navbar-btn " id="new-investigation-attachement" href="{% url 'login' %}">Add attachment</a> + <a class="btn btn-default navbar-btn " id="select-addattachment">Add attachment ... </a> {% endif %} </h3> + <div id="details-addattachment" style="padding-left: 50px; display:none;"> + <p><p> + <div class="row"> + <form id="uploadbanner" enctype="multipart/form-data" method="post">{% csrf_token %} + <input id="fileDescription" name="fileDescription" type="text" placeholder="Enter Description" /> + <input id="fileUpload" name="fileUpload" type="file" /> + <input type="hidden" id="action" name="action" value="upload"> + <input type="submit" value="submit file" id="submit-addattachment" /> + </form> + </div> + </div> + <table class="table table-striped table-condensed" data-testid="vuln-hyperlinks-table"> <thead> <tr> @@ -275,17 +419,20 @@ {% for u in object.vulnerability_uploads.all %} <tr> <td>{{ u.description }}</td> - <td>{{ u.path }}</td> + <td>{{ u.path|basename }}</td> <td>{{ u.size }}</td> <td>{{ u.date }}</td> <td>{{ u.author }}</td> <td> <span id="attachment_entry_'+{{u.id}}+'" class="js-config-var-name"></span> - <span class="glyphicon glyphicon-download-alt get-help" title="Download document"></span> - {% if access.is_creator %} - <span class="glyphicon glyphicon-edit js-icon-pencil-config_var" id="affected_edit_'+{{u.id}}+'" x-data="'+{{u.id}}+'"></span> - <span class="glyphicon glyphicon-trash js-icon-trash-config_var" id="attachment_trash_'+{{u.id}}+'" x-data="'+{{u.id}}+'"></span> - {% endif %} + <form id="downloadbanner" enctype="multipart/form-data" method="post" >{% csrf_token %} + <input type="hidden" id="action" name="action" value="download"> + <input type="hidden" id="record_id" name="record_id" value={{u.id}}> + <span class="glyphicon glyphicon-download-alt submit-downloadattachment" id="attachment_download_'+{{u.id}}+'" x-data="{{u.id}}"></span> + {% if access.is_creator %} + <span class="glyphicon glyphicon-trash trash-attachment" id="attachment_trash_'+{{u.id}}+'" x-data="{{u.id}}"></span> + {% endif %} + </form> </td> </tr> {% endfor %} @@ -295,18 +442,35 @@ </tr> {% endif %} </table> - </div> <br/> -<div class="row"> +<div class="row" style="padding-left: 25px;"> <h3>Change Notifications {% if access.is_creator %} - <a class="btn btn-default navbar-btn " id="new-investigation-notify" href="{% url 'login' %}">Add user notification</a> + <button id="select-addusernotify" class="btn btn-default" type="button">Add user notification ...</button> {% endif %} </h3> + <div id="details-addusernotify" style="padding-left: 50px; display:none;"> + <p><p> + <button class="execute" id="submit-addusernotify"> Submit </button> + <div class="row"> + <p> + <div id="all-users" class="scrolling" style="width: 300px;"> + {% for user in users %} + <div class="checkbox"> + <label> + <input class="checkbox-users" name="{{user.pk}}" type="checkbox">{{user.name}} + </label> + <p> + </div> + {% endfor %} + </div> + </div> + </div> + <table class="table table-striped table-condensed" data-testid="vuln-hyperlinks-table"> <thead> <tr> @@ -318,16 +482,15 @@ </tr> </thead> - {% if object.vulnerability_users.all %} - {% for u in object.vulnerability_users.all %} + {% if object.vulnerability_notification.all %} + {% for u in object.vulnerability_notification.all %} <tr> <td>{{ u.user.name }}</td> <td>{{ u.user.email }}</td> - {% if access.is_creator %} + {% if access.is_creator or u.user.name == access.current_user_name %} <td> <span id="attachment_entry_'+{{u.id}}+'" class="js-config-var-name"></span> - <span class="glyphicon glyphicon-edit js-icon-pencil-config_var" id="affected_edit_'+{{u.id}}+'" x-data="'+{{u.id}}+'"></span> - <span class="glyphicon glyphicon-trash js-icon-trash-config_var" id="attachment_trash_'+{{u.id}}+'" x-data="'+{{u.id}}+'"></span> + <span class="glyphicon glyphicon-trash trash-usernotification" id="attachment_trash_'+{{u.id}}+'" x-data="{{u.id}}"></span> </td> {% endif %} </tr> @@ -341,15 +504,34 @@ </div> -{% if access.is_creator %} +{% if not object.public %} + {% if access.is_creator %} - <div class="row"> + <div class="row" style="padding-left: 25px;"> <h3>User Access {% if access.is_creator %} - <a class="btn btn-default navbar-btn " id="new-investigation-access" href="{% url 'login' %}">Add user access</a> + <button id="select-adduseraccess" class="btn btn-default" type="button">Add user access ...</button> {% endif %} </h3> + <div id="details-adduseraccess" style="padding-left: 50px; display:none;"> + <p><p> + <button class="execute" id="submit-adduseraccess"> Submit </button> + <div class="row"> + <p> + <div id="all-users" class="scrolling" style="width: 300px;"> + {% for user in users %} + <div class="checkbox"> + <label> + <input class="checkbox-users" name="{{user.pk}}" type="checkbox">{{user.name}} + </label> + <p> + </div> + {% endfor %} + </div> + </div> + </div> + <table class="table table-striped table-condensed" data-testid="vuln-hyperlinks-table"> <thead> <tr> @@ -372,8 +554,7 @@ <td>{{ u.user.name }}</td> <td> <span id="attachment_entry_'+{{u.id}}+'" class="js-config-var-name"></span> - <span class="glyphicon glyphicon-edit js-icon-pencil-config_var" id="affected_edit_'+{{u.id}}+'" x-data="'+{{u.id}}+'"></span> - <span class="glyphicon glyphicon-trash js-icon-trash-config_var" id="attachment_trash_'+{{u.id}}+'" x-data="'+{{u.id}}+'"></span> + <span class="glyphicon glyphicon-trash trash-useraccess" id="attachment_trash_'+{{u.id}}+'" x-data="{{u.id}}"></span> </td> </tr> {% endfor %} @@ -387,9 +568,10 @@ </table> </div> + {% endif %} {% endif %} -<div class="row"> +<div class="row" style="padding-left: 25px;"> <h3>History</h3> <table class="table table-striped table-condensed" data-testid="vuln-hyperlinks-table"> <thead> @@ -415,5 +597,339 @@ </table> </div> +<!-- Javascript support --> +<script> + var selected_addrelatedproduct=false; + var selected_addaffectedproduct=false; + var selected_newcomment=false; + var selected_addusernotify=false; + var selected_adduseraccess=false; + var selected_quickedit=false; + var selected_addattachment=false; + + window.onload = function() { + $("input[name=status][value=" + {{ object.status }} + "]").prop('checked', true); + $("input[name=outcome][value=" + {{ object.outcome }} + "]").prop('checked', true); + $("input[name=severity][value=" + {{ object.severity }} + "]").prop('checked', true); + } + + $(document).ready(function() { + function onCommitAjaxSuccess(data, textstatus) { + 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 + ")"); + } + + /* ensure cookie exists {% csrf_token %} */ + function postCommitAjaxRequest(reqdata) { + reqdata["vulnerability_id"] = {{ object.id }} + var ajax = $.ajax({ + type:"POST", + data: reqdata, + url:"{% url 'xhr_vulnerability_commit' %}", + headers: { 'X-CSRFToken': $.cookie("csrftoken")}, + success: onCommitAjaxSuccess, + error: onCommitAjaxError, + }) + } + + $('#select-these').click(function(){ + $(':checkbox').each(function(){ + $(this).prop('checked', true); + }); + }); + + $('#unselect-these').click(function(){ + $(':checkbox').each(function(){ + $(this).prop('checked', false); + }); + }); + + $('#select-quickedit').click(function(){ + if (selected_quickedit) { + selected_quickedit=false; + $("#select-these").removeAttr("disabled"); + $("#unselect-these").removeAttr("disabled"); + $("#details-quickedit").slideUp(); + } else { + selected_quickedit=true; + $("#select-these").attr("disabled","disabled"); + $("#unselect-these").attr("disabled","disabled"); + $("#details-quickedit").slideDown(); + } + }); + + $('#submit-quickedit').click(function(){ + var note=$('#text-note').val().trim() + var private_note=$('#text-private-note').val().trim() + var status=0 + $('#status-list input').each(function(){ + if ($(this).is(':checked')) { + status = $(this).prop('value'); + } + }); + var outcome=0 + $('#outcome-list input').each(function(){ + if ($(this).is(':checked')) { + outcome = $(this).prop('value'); + } + }); + var severity=0 + $('#severity-list input').each(function(){ + if ($(this).is(':checked')) { + severity = $(this).prop('value'); + } + }); + + postCommitAjaxRequest({ + "action" : 'submit-quickedit', + "note" : note, + "private_note" : private_note, + "status" : status, + "outcome" : outcome, + "severity" : severity, + }); + }); + + $('#select-addrelatedproduct').click(function(){ + if (selected_addrelatedproduct) { + selected_addrelatedproduct=false; + $("#select-these").removeAttr("disabled"); + $("#unselect-these").removeAttr("disabled"); + $("#details-addrelatedproduct").slideUp(); + } else { + selected_addrelatedproduct=true; + $("#select-these").attr("disabled","disabled"); + $("#unselect-these").attr("disabled","disabled"); + $("#details-addrelatedproduct").slideDown(); + } + }); + + $('#submit-addrelatedproduct').click(function(){ + var product_list="" + $(':checkbox').each(function(){ + if ($(this).is(':checked')) { + product_list += $(this).prop('name') + ","; + } + }); + if ("" == product_list) { + alert("No products were selected"); + return; + } + postCommitAjaxRequest({ + "action" : 'submit-addrelatedproduct', + "products" : product_list, + }); + }); + + $('.trash-related').click(function() { + var result = confirm("Are you sure?"); + if (result){ + postCommitAjaxRequest({ + "action" : 'submit-trashrelated', + "record_id" : $(this).attr('x-data'), + }); + } + }); + + $('#select-addaffectedproduct').click(function(){ + if (selected_addaffectedproduct) { + selected_addaffectedproduct=false; + $("#select-these").removeAttr("disabled"); + $("#unselect-these").removeAttr("disabled"); + $("#details-addaffectedproduct").slideUp(); + } else { + selected_addaffectedproduct=true; + $("#select-these").attr("disabled","disabled"); + $("#unselect-these").attr("disabled","disabled"); + $("#details-addaffectedproduct").slideDown(); + } + }); + + $('#submit-addaffectedproduct').click(function(){ + var product_list="" + $(':checkbox').each(function(){ + if ($(this).is(':checked')) { + product_list += $(this).prop('name') + ","; + } + }); + if ("" == product_list) { + alert("No products were selected"); + return; + } + postCommitAjaxRequest({ + "action" : 'submit-addaffectedproduct', + "products" : product_list, + }); + }); + + $('.trash-affected').click(function() { + var result = confirm("Are you sure?"); + if (result){ + postCommitAjaxRequest({ + "action" : 'submit-trashaffected', + "record_id" : $(this).attr('x-data'), + }); + } + }); + + $('#select-newcomment').click(function(){ + if (selected_newcomment) { + selected_newcomment=false; + $("#input-newcomment").slideUp(); + } else { + selected_newcomment=true; + $("#input-newcomment").slideDown(); + } + }); + + $('#submit-newcomment').click(function(){ + var comment=$('#text-newcomment').val().trim() + if (comment=="") { + alert("No comment was written"); + return; + } + postCommitAjaxRequest({ + "action" : 'submit-newcomment', + "comment" : comment, + }) + }); + + $('.trash-comment').click(function(){ + var result = confirm("Are you sure?"); + if (result){ + postCommitAjaxRequest({ + "action" : 'submit-trashcomment', + "record_id" : $(this).attr('x-data'), + }); + } + }) + + $('#select-addattachment').click(function() { + if (selected_addattachment) { + selected_addattachment=false; + $("#details-addattachment").slideUp(); + } else { + selected_addattachment=true; + $("#details-addattachment").slideDown(); + } + }); + + $('.submit-downloadattachment').click(function() { + $("#downloadbanner").submit(); + }); + + $('.trash-attachment').click(function() { + var result = confirm("Are you sure?"); + if (result){ + postCommitAjaxRequest({ + "action" : 'submit-trashattachment', + "record_id" : $(this).attr('x-data'), + }); + } + }); + + // Open AddUserNotify Action + $('#select-addusernotify').click(function(){ + if (selected_addusernotify) { + selected_addusernotify=false; + $("#select-these").removeAttr("disabled"); + $("#unselect-these").removeAttr("disabled"); + $("#details-addusernotify").slideUp(); + } else { + selected_addusernotify=true; + $("#select-these").attr("disabled","disabled"); + $("#unselect-these").attr("disabled","disabled"); + $("#details-addusernotify").slideDown(); + } + }); + + $('#submit-addusernotify').click(function(){ + var user_list="" + $(':checkbox').each(function(){ + if ($(this).is(':checked')) { + user_list += $(this).prop('name') + ","; + } + }); + if ("" == user_list) { + alert("No users were selected"); + return; + } + postCommitAjaxRequest({ + "action" : 'submit-addusernotify', + "users" : user_list, + }); + }); + + $('.trash-usernotification').click(function(){ + var result = confirm("Are you sure?"); + if (result){ + postCommitAjaxRequest({ + "action" : 'submit-trashusernotification', + "record_id" : $(this).attr('x-data'), + }); + } + }) + + $('#select-adduseraccess').click(function(){ + if (selected_adduseraccess) { + selected_adduseraccess=false; + $("#select-these").removeAttr("disabled"); + $("#unselect-these").removeAttr("disabled"); + $("#details-adduseraccess").slideUp(); + } else { + selected_adduseraccess=true; + $("#select-these").attr("disabled","disabled"); + $("#unselect-these").attr("disabled","disabled"); + $("#details-adduseraccess").slideDown(); + } + }); + + $('#submit-adduseraccess').click(function(){ + var user_list="" + $(':checkbox').each(function(){ + if ($(this).is(':checked')) { + user_list += $(this).prop('name') + ","; + } + }); + if ("" == user_list) { + alert("No users were selected"); + return; + } + postCommitAjaxRequest({ + "action" : 'submit-adduseraccess', + "users" : user_list, + }); + }); + + $('.trash-useraccess').click(function(){ + var result = confirm("Are you sure?"); + if (result){ + postCommitAjaxRequest({ + "action" : 'submit-trashuseraccess', + "record_id" : $(this).attr('x-data'), + }); + } + }) + + /* Set the report link */ + $('#report_link').attr('href',"{% url 'report' request.resolver_match.url_name %}?record_list={{object.id}}"); + }); +</script> + {% endblock %} diff --git a/lib/srtgui/templatetags/projecttags.py b/lib/srtgui/templatetags/projecttags.py index b9afebcd..ac35fa0a 100644 --- a/lib/srtgui/templatetags/projecttags.py +++ b/lib/srtgui/templatetags/projecttags.py @@ -27,6 +27,7 @@ from django.utils import timezone from django.template.defaultfilters import filesizeformat import json as JsonLib from django.utils.safestring import mark_safe +import os register = template.Library() @@ -308,3 +309,8 @@ def recommend_display(recommend): return '>= 3' else: return '%s' % recommend + +@register.filter(name='basename') +def basename(value): + return os.path.basename(value) + diff --git a/lib/srtgui/urls.py b/lib/srtgui/urls.py index e86acbbc..e13939c9 100644 --- a/lib/srtgui/urls.py +++ b/lib/srtgui/urls.py @@ -1,7 +1,7 @@ # -# BitBake Toaster Implementation +# Security Response Tool Implementation # -# Copyright (C) 2013-2017 Intel Corporation +# Copyright (C) 2017-2018 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 @@ -30,81 +30,100 @@ urlpatterns = [ # landing page url(r'^landing/$', views.landing, name='landing'), - url(r'^manage/$', views.management, name='manage'), - url(r'^manage_cpes/$', - tables.ManageCpeTable.as_view(template_name="manage-cpes-toastertable.html"), - name='manage_cpes'), - 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'^login/$', views.login, name='login'), - url(r'^users/$', views.users, name='users'), - - # TEST CODE url(r'^login_guest/$', views.login_guest, name='login_guest'), - url(r'^login_admin/$', views.login_admin, name='login_admin'), - + url(r'^cve/(?P<cve_pk>\d+)$', views.cve, name="cve"), + url(r'^cve/(?P<cve_pk>[^\d].+)$', views.cve), + url(r'^cve/(?P<cve_pk>\d+)/active_tab/(?P<active_tab>\d{1})$', views.cve, name="cve"), url(r'^cves/$', - tables.AllCveTable.as_view(template_name="cves-toastertable.html"), - name='all-cves'), - - url(r'^start-select-cves/$', views.start_select_cves, name='start-select-cves'), + tables.CvesTable.as_view(template_name="cves-toastertable.html"), + name='cves'), url(r'^select-cves/$', tables.SelectCveTable.as_view(template_name="cves-select-toastertable.html"), name='select-cves'), - url(r'^cve/(?P<cve_pk>\d+)/active_tab/(?P<active_tab>\d{1})$', views.cve, name="cve"), - url(r'^cve/(?P<cve_pk>\d+)$', views.cve, name="cve"), - + url(r'^defect/(?P<defect_pk>\d+)$', views.defect, name="defect"), + url(r'^defect/(?P<defect_pk>[^\d].+)$', views.defect), url(r'^defects/$', - tables.AllDefectTable.as_view(template_name="defects-toastertable.html"), - name='all-defects'), + tables.DefectsTable.as_view(template_name="defects-toastertable.html"), + name='defects'), url(r'^cpes/$', - tables.AllCpeTable.as_view(template_name="cpes-toastertable.html"), - name='all-cpes'), + tables.CpesTable.as_view(template_name="cpes-toastertable.html"), + name='cpes'), url(r'^cwes/$', - tables.AllCweTable.as_view(template_name="cwes-toastertable.html"), - name='all-cwes'), + tables.CwesTable.as_view(template_name="cwes-toastertable.html"), + name='cwes'), + url(r'^product/(?P<product_pk>\d+)$', views.product, name="product"), url(r'^products/$', tables.ProductsTable.as_view(template_name="products-toastertable.html"), name='products'), + url(r'^vulnerability/(?P<vulnerability_pk>\d+)$', views.vulnerability, name="vulnerability"), + url(r'^vulnerability/(?P<vulnerability_pk>[^\d].+)$', views.vulnerability), url(r'^vulnerabilities/$', - tables.AllVulnerabilitiesTable.as_view(template_name="vulnerabilities-toastertable.html"), + tables.VulnerabilitiesTable.as_view(template_name="vulnerabilities-toastertable.html"), name='vulnerabilities'), - url(r'^vulnerability/(?P<vulnerability_pk>\d+)$', views.vulnerability, name="vulnerability"), - + url(r'^investigation/(?P<investigation_pk>\d+)$', views.investigation, name="investigation"), + url(r'^investigation/(?P<investigation_pk>[^\d].+)$', views.investigation), url(r'^investigations/$', - tables.AllInvestigationsTable.as_view(template_name="investigations-toastertable.html"), + tables.InvestigationsTable.as_view(template_name="investigations-toastertable.html"), name='investigations'), - url(r'^investigation/(?P<investigation_pk>\d+)$', views.investigation, name="investigation"), - - url(r'^all-keywords/$', - tables.KeywordsTable.as_view(template_name="keywords-toastertable.html"), - name='all-keywords'), - url(r'^keywords/$', views.keywords, name='keywords'), + url(r'^keywords_table/$', + tables.KeywordsTable.as_view(template_name="keywords-toastertable.html"), + name='keywords_table'), - url(r'^sources/$', views.sources, name='sources'), + url(r'^select-publish/$', + tables.SelectPublishTable.as_view(template_name="publish-select-toastertable.html"), + name='select-publish'), - url(r'^export/(?P<page_name>\D+)$', views.export, name='export'), -# url(r'^export/$', views.export_null, name='export'), + url(r'^update-published/$', + tables.UpdatePublishedTable.as_view(template_name="published-select-toastertable.html"), + name='update-published'), - url(r'^guided_tour/$', views.guided_tour, name='guided_tour'), + url(r'^report/(?P<page_name>\D+)$', views.report, name='report'), - url(r'^quicklink/$', views.quicklink, name='quicklink'), url(r'^xhr_triage_commit/$', views.xhr_triage_commit, name='xhr_triage_commit'), + url(r'^xhr_cve_commit/$', views.xhr_cve_commit, + name='xhr_cve_commit'), + + url(r'^xhr_vulnerability_commit/$', views.xhr_vulnerability_commit, + name='xhr_vulnerability_commit'), + + url(r'^xhr_investigation_commit/$', views.xhr_investigation_commit, + name='xhr_investigation_commit'), + + url(r'^xhr_cve_publish_commit/$', views.xhr_cve_publish_commit, + name='xhr_cve_publish_commit'), + + + url(r'^manage/$', views.management, name='manage'), + url(r'^manage_cpes/$', + tables.ManageCpeTable.as_view(template_name="manage-cpes-toastertable.html"), + name='manage_cpes'), + 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'^guided_tour/$', views.guided_tour, name='guided_tour'), + + url(r'^quicklink/$', views.quicklink, name='quicklink'), + url(r'^tbd/$', views.tbd, name='tbd'), # default redirection diff --git a/lib/srtgui/views.py b/lib/srtgui/views.py index 48556b3b..6e215f33 100644 --- a/lib/srtgui/views.py +++ b/lib/srtgui/views.py @@ -2,9 +2,9 @@ # ex:ts=4:sw=4:sts=4:et # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- # -# BitBake Toaster Implementation +# Security Response Tool Implementation # -# Copyright (C) 2013 Intel Corporation +# Copyright (C) 2017-2018 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 @@ -25,18 +25,24 @@ import re from django.db.models import F, Q, Sum from django.db import IntegrityError from django.shortcuts import render, redirect, get_object_or_404 -from orm.models import Cve, CveHistory -from orm.models import Vulnerability, VulnerabilityHistory, CveToVulnerablility, VulnerabilityProduct -from orm.models import Investigation, InvestigationHistory, InvestigationToDefect +from orm.models import Cve, CveDetail, CveHistory +from orm.models import Vulnerability, VulnerabilityHistory, CveToVulnerablility, VulnerabilityProduct, VulnerabilityNotification, VulnerabilityAccess, VulnerabilityComments, VulnerabilityUploads +from orm.models import Investigation, InvestigationHistory, InvestigationToDefect, InvestigationComments, InvestigationNotification, InvestigationAccess, InvestigationUploads from orm.models import SrtSetting, CweTable, Product -from orm.models import Investigation, DataSource, User, Access -from orm.models import Defect +from orm.models import Investigation, DataSource, User, UserSafe, Access +from orm.models import Defect, PublishPending + +from srtgui.reports import ReportManager +from srtgui.api import readCveDetails from django.core.urlresolvers import reverse, resolve from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger -from django.http import HttpResponse, HttpResponseNotFound, JsonResponse +from django.core.files.uploadedfile import UploadedFile +from django.http import HttpResponse, HttpResponseNotFound, JsonResponse, HttpResponseRedirect from django.utils import timezone +from django import forms + from datetime import timedelta, datetime from srtgui.templatetags.projecttags import json as jsonfilter from decimal import Decimal @@ -44,16 +50,16 @@ import json import os from os.path import dirname import mimetypes -import datetime +import subprocess import logging +SRT_BASE_DIR = os.environ['SRT_BASE_DIR'] + logger = logging.getLogger("srt") -def _log(msg): - f1=open('/tmp/srt.log', 'a') - f1.write("|" + msg + "|\n" ) - f1.close() +# quick development/debugging support +from srtgui.api import _log class MimeTypeFinder(object): # setting this to False enables additional non-standard mimetypes @@ -72,25 +78,31 @@ class MimeTypeFinder(object): # single point to add global values into the context before rendering def toaster_render(request, page, context): -# context['project_enable'] = project_enable + # Share session's user name + srt_user_id = int(request.session.get('srt_user_id', '0')) + if 0 == srt_user_id: + srt_user_id = User.USER_GUEST + request.session['srt_user_id'] = srt_user_id + request.session.modified = True + context['srt_user_id'] = srt_user_id ### REMOVE + # Set normal access rights unless pre-empted by page + if not 'access' in context: + context['access'] = Access(srt_user_id) return render(request, page, context) # a context processor which runs on every request; this provides the # projects and non_cli_projects (i.e. projects created by the user) # variables referred to in templates, which used to determine the -# visibility of UI elements like the "New build" button +# visibility of UI elements like the "Management" button def managedcontextprocessor(request): ret = { # "non_cli_projects": projects.exclude(is_default=True), # "DEBUG" : srtmain.settings.DEBUG, # "TOASTER_BRANCH": srtmain.settings.TOASTER_BRANCH, # "TOASTER_REVISION" : srtmain.settings.TOASTER_REVISION, - 'access' : Access(), } return ret - -# all new sessions should come through the landing page; # determine in which mode we are running in, and redirect appropriately def landing(request): @@ -99,9 +111,7 @@ def landing(request): # user_projects = Project.objects.filter(is_default = False) # has_user_project = user_projects.count() > 0 - context = { - 'lvs_nos' : 0, - } + context = {} return toaster_render(request, 'landing.html', context) @@ -447,19 +457,18 @@ def _add_daterange_context(queryset_all, request, daterange_list): def management(request): # does this user have permission to see this record? - userAccess = Access() - if not userAccess.is_admin(): + userAccess = Access(int(request.session.get('srt_user_id', '0'))) + if not userAccess.is_creator(): return redirect(landing) context = { - 'lvs_nos' : 0, 'cve_total' : Cve.objects.all().count(), 'cve_new' : Cve.objects.filter(status=Cve.NEW).count(), 'cve_open' : Cve.objects.filter( Q(status=Cve.INVESTIGATE) & Q(status=Cve.VULNERABLE) ).count(), 'vulnerability_total' : Vulnerability.objects.all().count(), - + 'vulnerability_open' : Vulnerability.objects.filter(outcome=Vulnerability.OPEN).count(), 'vulnerability_high' : Vulnerability.objects.filter(severity=Vulnerability.HIGH).count(), 'vulnerability_medium' : Vulnerability.objects.filter(severity=Vulnerability.MEDIUM).count(), @@ -479,13 +488,19 @@ def management(request): import copy def cve(request, cve_pk, active_tab="1"): template = "cve.html" - if Cve.objects.filter(pk=cve_pk).count() == 0 : - return redirect(landing) - cve_object = Cve.objects.get(pk=cve_pk) + # CVE name or pk + try: + if cve_pk[0].isdigit(): + cve_object = Cve.objects.get(pk=cve_pk) + else: + cve_object = Cve.objects.get(name=cve_pk) + cve_pk = cve_object.id + except: + return redirect(landing) # does this user have permission to see this record? - userAccess = Access() + userAccess = Access(int(request.session.get('srt_user_id', '0'))) if (not cve_object.public) and (not userAccess.is_admin()): return redirect(landing) @@ -500,61 +515,214 @@ def cve(request, cve_pk, active_tab="1"): else: response_link = str(investigation_records[0].pk) + # fetch CVE's detail information + cve_object_detail = readCveDetails(cve_object) + _log("FOO:%s" % cve_object_detail) + cve_summary = copy.copy(cve_object) + cve_summary_detail = copy.copy(cve_object_detail) cve_summary.source = 'Summary' - - cve_local = Cve + + cve_local = Cve() + cve_local_detail = CveDetail() cve_local.source = 'Local' - + context = { - 'object' : { "name" : "CVE-1234"} , - 'cve_list_table' : [ - (cve_summary,tab_states['1'],"Summary"), - (cve_object,tab_states['2'],"NIST"), - (cve_local,tab_states['3'],"Local"), + 'object' : { "name" : "CVE-1234", + "id" : cve_object.id, + }, + 'cve_list_table' : [ + (cve_summary,cve_summary_detail,tab_states['1'],"Summary"), + (cve_object,cve_object_detail,tab_states['2'],"NIST"), + (cve_local,cve_local_detail,tab_states['3'],"Local"), ], 'tab_states' : tab_states, 'response_link' : response_link, 'cve_prev' : str(max(1,int(cve_pk)-1)), 'cve_next' : str(min(int(cve_pk)+1,Cve.objects.count()-1)), } - return render(request, template, context) + return toaster_render(request, template, context) def vulnerability(request, vulnerability_pk): - template = "vulnerability.html" - if Vulnerability.objects.filter(pk=vulnerability_pk).count() == 0 : - return redirect(landing) + if request.method == "GET": + template = "vulnerability.html" - vulnerability_object = Vulnerability.objects.get(pk=vulnerability_pk) + # Defect name or pk + try: + if vulnerability_pk[0].isdigit(): + vulnerability_object = Vulnerability.objects.get(pk=vulnerability_pk) + else: + vulnerability_object = Vulnerability.objects.get(name=vulnerability_pk) + vulnerability_pk = vulnerability_object.id + except: + return redirect(landing) + + products = Product.objects.all() + + # does this user have permission to see this record? + userAccess = Access(int(request.session.get('srt_user_id', '0'))) + if (not vulnerability_object.public) and (not userAccess.is_admin()): + return redirect(landing) + + context = { + 'object' : vulnerability_object, + 'users' : UserSafe.get_safe_userlist(True), + 'products': products, + } + return toaster_render(request, template, context) + elif request.method == "POST": + _log("EXPORT_POST:VULERNABILITY_POST: %s" % request) + + if request.POST["action"] == "upload": + if vulnerability_pk[0].isdigit(): + vulnerability_object = Vulnerability.objects.get(pk=vulnerability_pk) + else: + vulnerability_object = Vulnerability.objects.get(name=vulnerability_pk) + vulnerability_pk = vulnerability_object.id + path = os.path.join(SRT_BASE_DIR, "downloads/%s" % vulnerability_object.name) + # Catch the post against this page + try: + os.makedirs(path) + except: + pass + + try: + file = request.FILES['fileUpload'] + description = request.POST['fileDescription'] + except Exception as e: + _log("EXPORT_POST:'fileupload' does not exist: %s" % e) + + try: + with open(path + "/" + file.name, 'xb+') as destination: + for line in file: + destination.write(line) + + userAccess = Access(int(request.session.get('srt_user_id', '0'))) + VulnerabilityUploads.objects.get_or_create(vulnerability_id=vulnerability_object.id, description=description, path=path + "/" + file.name, size=file.size, date=datetime.today().strftime('%Y-%m-%d'), author=userAccess.current_user_name) + except Exception as e: + _log("EXPORT_POST:FILE ALREADY EXISTS: %s" % e) + return redirect(vulnerability,vulnerability_pk) + elif request.POST["action"] == "download": + record_id = request.POST['record_id'] + upload = VulnerabilityUploads.objects.get(id=record_id) + file_path = upload.path + 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 toaster_render(request, "unavailable_artifact.html") - # does this user have permission to see this record? - userAccess = Access() - if (not vulnerability_object.public) and (not userAccess.is_admin()): +def investigation(request, investigation_pk): + if request.method == "GET": + template = "investigation.html" + + # Investigation name or pk + try: + if investigation_pk[0].isdigit(): + investigation_object = Investigation.objects.get(pk=investigation_pk) + else: + investigation_object = Investigation.objects.get(name=investigation_pk) + investigation_pk = investigation_object.id + except: + return redirect(landing) + + defects = Defect.objects.all() + investigation_to_defect = investigation_object.investigation_to_defect.all() + context = { + 'object' : investigation_object, + 'users' : UserSafe.get_safe_userlist(True), + 'defects' : defects, + 'investigation_to_defect' : investigation_to_defect, + 'defect_example' : SrtSetting.objects.get(name='SRTOOL_DEFECT_SAMPLENAME').value, + } + return toaster_render(request, template, context) + elif request.method == "POST": + _log("EXPORT_POST:INVESTIGATION_POST: %s" % request) + + if request.POST["action"] == "upload": + if investigation_pk[0].isdigit(): + investigation_object = Investigation.objects.get(pk=investigation_pk) + else: + investigation_object = Investigation.objects.get(name=investigation_pk) + investigation_pk = investigation_object.id + path = os.path.join(SRT_BASE_DIR, "downloads/%s" % investigation_object.name) + # Catch the post against this page + try: + os.makedirs(path) + except: + pass + + try: + file = request.FILES['fileUpload'] + description = request.POST['fileDescription'] + except Exception as e: + _log("EXPORT_POST:'fileupload' does not exist: %s" % e) + + try: + with open(path + "/" + file.name, 'xb+') as destination: + for line in file: + destination.write(line) + userAccess = Access(int(request.session.get('srt_user_id', '0'))) + InvestigationUploads.objects.get_or_create(investigation_id=investigation_object.id, description=description, path=path + "/" + file.name, size=file.size, date=datetime.today().strftime('%Y-%m-%d'), author=userAccess.current_user_name) + except Exception as e: + _log("EXPORT_POST:FILE ALREADY EXISTS: %s" % e) + return redirect(investigation,investigation_pk) + elif request.POST["action"] == "download": + record_id = request.POST['record_id'] + upload = InvestigationUploads.objects.get(id=record_id) + file_path = upload.path + 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 toaster_render(request, "unavailable_artifact.html") + + +def defect(request, defect_pk): + template = "defect.html" + + # Defect name or pk + try: + if defect_pk[0].isdigit(): + defect_object = Defect.objects.get(pk=defect_pk) + else: + defect_object = Defect.objects.get(name=defect_pk) + defect_pk = defect_object.id + except: return redirect(landing) context = { - 'object' : vulnerability_object, -# 'cwe_table' : CweTable.objects.all(), + 'object' : defect_object, + 'users' : users, } - return render(request, template, context) + return toaster_render(request, template, context) -def investigation(request, investigation_pk): - template = "investigation.html" - if Investigation.objects.filter(pk=investigation_pk).count() == 0 : +def product(request, product_pk): + template = "product.html" + if Product.objects.filter(pk=product_pk).count() == 0 : return redirect(landing) - investigation_object = Investigation.objects.get(pk=investigation_pk) + product_object = Product.objects.get(pk=product_pk) context = { - 'object' : investigation_object, -# 'cwe_table' : CweTable.objects.all(), + 'object' : product_object, } - return render(request, template, context) + return toaster_render(request, template, context) def sources(request): # does this user have permission to see this record? - userAccess = Access() + userAccess = Access(int(request.session.get('srt_user_id', '0'))) if not userAccess.is_admin(): return redirect(landing) @@ -567,41 +735,46 @@ def sources(request): context = { 'object' : object, } - return render(request, template, context) + return toaster_render(request, template, context) def login(request): - template = "login.html" - context = { - 'object' : object, - } - return render(request, template, context) + if request.method == "GET": + template = "login.html" + object = User.objects.all() + context = { + 'object' : object, + 'user_count' : len(object), + } + return toaster_render(request, template, context) + elif request.method == "POST": + user_name = request.POST.get('username', '_anonuser') + user_name = re.sub(r"\(.*","",user_name).strip() + password = request.POST.get('password', 'nopass') + _log("LOGIN_POST:%s,%s" % (user_name,password)) -def _log(msg): - f1=open('/tmp/srt.log', 'a') - f1.write("|" + msg + "|\n" ) - f1.close() + try: + ### USER CONTROL + user = User.objects.get(name=user_name) + request.session['srt_user_id'] = user.id + request.session.modified = True + + except Exception as e: + _log("LOGIN_ERROR:%s,%s" % (user,e)) + pass + return redirect(landing) + #return landing(request) -# TEMP ACCESS TEST CODE + raise Exception("Invalid HTTP method for this page") + +# This is effectively the logout command def login_guest(request): - current_user = SrtSetting.objects.get_or_create(name='current_user')[0] - current_user.value = 1 - current_user.save() - current_user_access = SrtSetting.objects.get_or_create(name='current_user_access')[0] - current_user_access.value = User.READER - current_user_access.save() - return redirect(landing) -def login_admin(request): - current_user = SrtSetting.objects.get_or_create(name='current_user')[0] - current_user.value = 3 - current_user.save() - current_user_access = SrtSetting.objects.get_or_create(name='current_user_access')[0] - current_user_access.value = User.ADMIN - current_user_access.save() + request.session['srt_user_id'] = User.USER_GUEST + request.session.modified = True return redirect(landing) def users(request): # does this user have permission to see this record? - userAccess = Access() + userAccess = Access(int(request.session.get('srt_user_id', '0'))) if not userAccess.is_admin(): return redirect(landing) @@ -615,37 +788,55 @@ def users(request): context = { 'object' : object, } - return render(request, template, context) - -def export(request,page_name): - context = { - 'lvs_nos' : 0, - 'page' : '%s%s' % (page_name[0:1].upper(),page_name[1:]) - } - return toaster_render(request, 'export.html', context) + return toaster_render(request, template, context) + +def report(request,page_name): + if request.method == "GET": + context = ReportManager.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 toaster_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 = ReportManager.exec_report(parent_page,request=request) + + if 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 toaster_render(request, "unavailable_artifact.html", {}) -def export_null(request): - return export(request,"NuLl") + return redirect(landing) + raise Exception("Invalid HTTP method for this page") def triage_cves(request): # does this user have permission to see this record? - userAccess = Access() - if not userAccess.is_admin(): + userAccess = Access(int(request.session.get('srt_user_id', '0'))) + if not userAccess.is_creator(): return redirect(landing) - context = {'lvs_nos' : 0} + context = {} return toaster_render(request, 'triage_cves.html', context) - def keywords(request): # does this user have permission to see this record? - userAccess = Access() - if not userAccess.is_admin(): + userAccess = Access(int(request.session.get('srt_user_id', '0'))) + if not userAccess.is_creator(): return redirect(landing) - + for_list = SrtSetting.objects.get(name='keywords_for').value.split('|') against_list = SrtSetting.objects.get(name='keywords_against').value.split('|') - context = {'lvs_nos' : 0, + context = { 'for_list' : for_list, 'for_list_count' : len(for_list), 'against_list' : against_list, @@ -653,87 +844,36 @@ def keywords(request): } return toaster_render(request, 'keywords.html', context) - -def start_select_cves(request): - # pre-calculate the recommend values - queryset = \ - Cve.objects.filter(status = Cve.NEW,name__startswith = 'CVE-2018-') - - KeywordsFor = SrtSetting.objects.get(name='keywords_for').value.split('|') - KeywordsAgainst = SrtSetting.objects.get(name='keywords_against').value.split('|') - - for cve in queryset: - description = ' '+cve.description.lower()+' ' - total = 0 - list = '' - - for keypair in KeywordsFor: - #print("keypair='%s'" % keypair) - key,w = keypair.split(',') - weight = 1 -# if w: -# weight = int(w) - #if ' '+key+' ' in description: - if re.search(r'\b%s\b' % key, description): - list += ",+%s" % key - total += weight - - for keypair in KeywordsAgainst: - #print("keypair='%s'" % keypair) - key,w = keypair.split(',') - weight = 1 -# if w: -# weight = int(w) - # re.search(r'\bis\b', your_string) - #if ' '+key+' ' in description: - #if re.search(re.escape(r'\b%s\b' % key), description): - if re.search(r'\b%s\b' % key, description): - list += ",-%s" % key - total -= weight - - # set filter maximums - if total < -3: - total = -3 - if total > 3: - total = 3 - cve.recommend = total - cve.comments_private = list[1:] - cve.save() - - #_log("%s[%d]=%s" % (cve.name,cve.recommend,cve.comments_private)) - - return redirect("/srtgui/select-cves") - def create_vulnerability(request): # does this user have permission to see this record? - userAccess = Access() - if not userAccess.is_admin(): + userAccess = Access(int(request.session.get('srt_user_id', '0'))) + if not userAccess.is_creator(): return redirect(landing) - context = {'lvs_nos' : 0} + context = {} return toaster_render(request, 'create_vulnerability.html', context) def publish(request): # does this user have permission to see this record? - userAccess = Access() - if not userAccess.is_admin(): + userAccess = Access(int(request.session.get('srt_user_id', '0'))) + if not userAccess.is_creator(): return redirect(landing) - context = {'lvs_nos' : 0} + context = {} return toaster_render(request, 'publish.html', context) def manage_report(request): # does this user have permission to see this record? - userAccess = Access() - if not userAccess.is_admin(): + userAccess = Access(int(request.session.get('srt_user_id', '0'))) + if not userAccess.is_creator(): return redirect(landing) - return redirect(export,'management') + return redirect(report,'management') def guided_tour(request): - context = {'lvs_nos' : 0} + context = {} return toaster_render(request, 'guided_tour.html', context) - + def quicklink(request): current_user = SrtSetting.objects.get_or_create(name='current_user')[0] current_user.value = 16 @@ -741,7 +881,7 @@ def quicklink(request): current_user_access = SrtSetting.objects.get_or_create(name='current_user_access')[0] current_user_access.value = User.ADMIN current_user_access.save() - return redirect("/srtgui/start-select-cves") + return redirect("/srtgui/select-publish") def xhr_triage_commit(request): _log("xhr_triage_commit(%s)" % request.POST) @@ -752,7 +892,7 @@ def xhr_triage_commit(request): author_name = User.objects.get(pk=author_id).name action = request.POST['action'] - today = datetime.datetime.today().strftime("%Y-%m-%d") + today = datetime.today().strftime("%Y-%m-%d") if 'submit-notvulnerable' == action: reason = request.POST['reason'] cves = request.POST['cves'] @@ -786,10 +926,11 @@ def xhr_triage_commit(request): first_vulnerability = True investigation_names = {} for cve_name in cves[:-1].split(','): - + # update CVE cve = Cve.objects.get(name=cve_name) cve.status = Cve.VULNERABLE + cve.priority = severity if cve.comments: cve.comments += ', ' + reason else: @@ -864,7 +1005,322 @@ def xhr_triage_commit(request): _log("xhr_triage_commit:no(%s)" % e) return HttpResponse(json.dumps({"error":str(e) + "\n" + traceback.format_exc()}), content_type = "application/json") +def xhr_cve_commit(request): + _log("xhr_cve_commit(%s)" % request.POST) + if not 'action' in request.POST: + return HttpResponse(json.dumps({"error":"missing action\n"}), content_type = "application/json") + try: + cve = Cve.objects.get(id=request.POST['cve_id']) + action = request.POST['action'] + history_comment = '' + if 'submit-quickedit' == action: + note = request.POST['note'] + priority = int(request.POST['priority']) + private_note = request.POST['private_note'] + publish_state = request.POST['publish_state'] + publish_date = request.POST['publish_date'] + if (priority != cve.priority): + cve.priority = priority + history_comment += "Priority, " + if (note != cve.comments): + cve.comments = note + history_comment += "Note, " + if (private_note != cve.comments_private): + cve.comments_private = private_note + history_comment += "Private Note, " + if (publish_state != cve.publish_state): + cve.publish_state = publish_state + history_comment += "Publish State, " + if (publish_date != cve.publish_date): + cve.publish_date = publish_date + history_comment += "Publish Date, " + cve.save() + return_data = { + "error": "ok", + } + + access = Access(int(request.session.get('srt_user_id', '0'))) + if (history_comment != ''): + history_comment = history_comment[:-2] + history_comment += " edited" + CveHistory.objects.create(cve_id=cve.id, comment=history_comment, date=datetime.now().strftime('%Y-%m-%d'), author=access.current_user_name) + _log("xhr_cve_commit:SUCCESS") + return HttpResponse(json.dumps( return_data ), content_type = "application/json") + + except Exception as e: + _log("xhr_cve_commit:no(%s)" % e) + return HttpResponse(json.dumps({"error":str(e) + "\n" + traceback.format_exc()}), content_type = "application/json") + +def xhr_cve_publish_commit(request): + _log("xhr_cve_publish_commit(%s)" % request.POST) + cve_list = request.POST['cve_list'] + publish_state = int(request.POST['publish_state']) + _log("xhr_cve_publish_commit2") + try: + for name in cve_list.split(','): + _log("xhr_cve_publish_commit3:%s" % name) + if name: + _log("xhr_cve_publish_commit4") + cve = Cve.objects.get(name=name) + cve.publish_state = publish_state + cve.save() + _log("xhr_cve_publish_commit5") + # Add to publish pending queue? + if Cve.PUBLISH_SUBMITTED == publish_state: + _log("xhr_cve_publish_commit5a") + pub_req,created = PublishPending.objects.get_or_create(cve=cve) + pub_req.date=datetime.today().strftime('%Y-%m-%d') + pub_req.save() + _log("xhr_cve_publish_commit5b") + # Remove from publish pending queue? + if Cve.PUBLISH_PUBLISHED == publish_state: + _log("xhr_cve_publish_commit5c") + try: + pub_req = PublishPending.objects.get(cve=cve) + pub_req.delete() + except: + pass + _log("xhr_cve_publish_commit5d") + _log("xhr_cve_publish_commit6") + return_data = { + "error": "ok", + } + return HttpResponse(json.dumps( return_data ), content_type = "application/json") + except Exception as e: + _log("xhr_cve_publish_commit:no(%s)" % e) + return HttpResponse(json.dumps({"error":str(e) + "\n" + traceback.format_exc()}), content_type = "application/json") + +def xhr_vulnerability_commit(request): + _log("xhr_vulnerability_commit(%s)" % request.POST) + if not 'action' in request.POST: + return HttpResponse(json.dumps({"error":"missing action\n"}), content_type = "application/json") + + action = request.POST['action'] + v_id = request.POST['vulnerability_id'] + access = Access(int(request.session.get('srt_user_id', '0'))) + history_comment = '' + try: + if 'submit-quickedit' == action: + note = request.POST['note'] + private_note = request.POST['private_note'] + v = Vulnerability.objects.get(id=v_id) + if (v.comments != note): + v.comments = note + history_comment += "Note, " + if (v.comments_private != private_note): + v.comments_private = private_note + history_comment += "Private Note, " + if (v.status != request.POST['status']): + v.status = request.POST['status'] + history_comment += "Status, " + if (v.outcome != request.POST['outcome']): + v.outcome = request.POST['outcome'] + history_comment += "Outcome, " + if (v.severity != request.POST['severity']): + v.severity = request.POST['severity'] + history_comment += "Severity, " + if (history_comment != ''): + history_comment = history_comment[:-2] + history_comment += " edited" + v.save() + + if 'submit-addrelatedproduct' == action: + products = request.POST['products'] + product_names = '' + for product_id in products[:-1].split(','): + tmp = Product.objects.get(pk=product_id) + product_names += tmp.name + ' ' + tmp.version + ', ' + VulnerabilityProduct.objects.get_or_create(vulnerability_id=v_id, product_id=product_id, relation=1) + product_names = product_names[:-2] + history_comment = product_names + " added to related products" + if 'submit-trashrelated' == action: + record_id = request.POST['record_id'] + record = VulnerabilityProduct.objects.get(id=record_id) + product = Product.objects.get(id=record.product_id) + history_comment = product.name + ' ' + product.version + " removed from related products" + record.delete() + if 'submit-addaffectedproduct' == action: + products = request.POST['products'] + product_names = '' + for product_id in products[:-1].split(','): + tmp = Product.objects.get(pk=product_id) + product_names += tmp.name + ' ' + tmp.version + ', ' + VulnerabilityProduct.objects.get_or_create(vulnerability_id=v_id, product_id=product_id, relation=0) + product_names = product_names[:-2] + history_comment = product_names + " added to affected products" + if 'submit-trashaffected' == action: + record_id = request.POST['record_id'] + record = VulnerabilityProduct.objects.get(id=record_id) + product = Product.objects.get(id=record.product_id) + history_comment = product.name + ' ' + product.version + " removed from afftected products" + record.delete() + if 'submit-newcomment' == action: + comment = request.POST['comment'] + VulnerabilityComments.objects.create(vulnerability_id=v_id, comment=comment, date=datetime.today().strftime('%Y-%m-%d'), author=access.current_user_name) + history_comment = "New comment submitted" + if 'submit-trashcomment' == action: + record_id = request.POST['record_id'] + comment = VulnerabilityComments.objects.get(id=record_id) + history_comment = "Comment from " + comment.author + " deleted" + comment.delete() + if 'submit-trashattachment' == action: + record_id = request.POST['record_id'] + upload = VulnerabilityUploads.objects.get(id=record_id) + history_comment = "Upload '" + upload.description + "' from " + upload.author + " deleted" + try: + os.remove(upload.path) + except OSError: + pass + upload.delete() + if 'submit-addusernotify' == action: + users = request.POST['users'] + usernames = '' + for user_id in users[:-1].split(','): + usernames += User.objects.get(pk=user_id).name + ', ' + VulnerabilityNotification.objects.get_or_create(vulnerability_id=v_id, user_id=user_id) + usernames = usernames[:-2] + history_comment = usernames + " added to notifications" + if 'submit-trashusernotification' == action: + record_id = request.POST['record_id'] + notification_record = VulnerabilityNotification.objects.get(id=record_id) + removed_user = User.objects.get(pk=notification_record.user_id).name + history_comment = removed_user + " removed from notifications" + notification_record.delete() + if 'submit-adduseraccess' == action: + users = request.POST['users'] + usernames = '' + for user_id in users[:-1].split(','): + usernames += User.objects.get(pk=user_id).name + ', ' + VulnerabilityAccess.objects.get_or_create(vulnerability_id=v_id, user_id=user_id) + usernames = usernames[:-2] + history_comment = usernames + " granted access" + if 'submit-trashuseraccess' == action: + record_id = request.POST['record_id'] + access_record = VulnerabilityAccess.objects.get(id=record_id) + removed_user = User.objects.get(pk=access_record.user_id).name + history_comment = removed_user + "'s access removed" + access_record.delete() + if (history_comment != ''): + VulnerabilityHistory.objects.create(vulnerability_id=v_id, comment=history_comment, date=datetime.now().strftime('%Y-%m-%d'), author=access.current_user_name) + return_data = { + "error": "ok", + } + return HttpResponse(json.dumps( return_data ), content_type = "application/json") + except Exception as e: + _log("xhr_vulnerability_commit:no(%s)" % e) + return HttpResponse(json.dumps({"error":str(e) + "\n" + traceback.format_exc()}), content_type = "application/json") + +def xhr_investigation_commit(request): + _log("xhr_investigation_commit(%s)" % request.POST) + if not 'action' in request.POST: + return HttpResponse(json.dumps({"error":"missing action\n"}), content_type = "application/json") + + action = request.POST['action'] + invst_id = request.POST['investigation_id'] + access = Access(int(request.session.get('srt_user_id', '0'))) + history_comment = "Nothing happened." + try: + if 'submit-attachdefectlist' == action: + defects = request.POST['defects'] + product_id = Investigation.objects.get(id=invst_id).product_id + defect_names = "" + for defect_id in defects[:-1].split(','): + defect_names += Defect.objects.get(pk=defect_id).name + ", " + InvestigationToDefect.objects.get_or_create(investigation_id=invst_id, defect_id=defect_id) + defect_names = defect_names[:-2] + history_comment = defect_names + " added to defects" + if 'submit-attachdefect' == action: + query = request.POST['query'].upper() + product_id = Investigation.objects.get(id=invst_id).product_id + #check if defect already in SRTool data + try: + defect = Defect.objects.get(name=query) + except Defect.DoesNotExist: + defect = None + + #If defect not in SRTool, import data from Defect database and create record + if defect is None: + #try connecting to defect management tool + try: + cmd_list = SrtSetting.objects.get(name='SRTOOL_DEFECT_ADD').value.split(' ') + cmd_list.append(query) + result = subprocess.run(cmd_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if 0 == result.returncode: + defect = Defect.objects.get(name=query) + else: + defect = None + return HttpResponse(json.dumps({"error":str(e) + "\n"}), content_type = "application/json") + except Exception as e: + _log("xhr_investigation_commit:Error in defect attach query:(%s)\n" % e) + return HttpResponse(json.dumps({"error":str(e) + "\n"}), content_type = "application/json") + if defect: + InvestigationToDefect.objects.get_or_create(investigation_id=invst_id, defect_id=defect.id, product_id=product_id) + history_comment = "Attached " + defect.name + if 'submit-trashdefect' == action: + defects = request.POST['defects'] + product_id = Investigation.objects.get(id=invst_id).product_id + defect_names = "" + for defect_id in defects[:-1].split(','): + defect_names += Defect.objects.get(pk=defect_id).name + ", " + InvestigationToDefect.objects.get(investigation_id=invst_id, defect_id=defect_id).delete() + defect_names = defect_names[:-2] + history_comment = defect_names + " deleted from defects" + if 'submit-newcomment' == action: + comment = request.POST['comment'] + InvestigationComments.objects.create(investigation_id=invst_id, comment=comment, date=datetime.today().strftime('%Y-%m-%d'), author=access.current_user_name) + history_comment = "New comment submitted" + if 'submit-trashcomment' == action: + record_id = request.POST['record_id'] + comment = InvestigationComments.objects.get(id=record_id) + history_comment = "Comment from " + comment.author + " deleted" + comment.delete() + if 'submit-trashattachment' == action: + record_id = request.POST['record_id'] + upload = InvestigationUploads.objects.get(id=record_id) + history_comment = "Upload '" + upload.description + "' from " + upload.author + " deleted" + try: + os.remove(upload.path) + except OSError: + pass + upload.delete() + if 'submit-addusernotify' == action: + users = request.POST['users'] + usernames = "" + for user_id in users[:-1].split(','): + usernames += User.objects.get(pk=user_id).name + ", " + InvestigationNotification.objects.get_or_create(investigation_id=invst_id, user_id=user_id) + usernames = usernames[:-2] + history_comment = usernames + " added to notifications" + if 'submit-trashusernotification' == action: + record_id = request.POST['record_id'] + notification_record = InvestigationNotification.objects.get(id=record_id) + removed_user = User.objects.get(pk=notification_record.user_id).name + history_comment = removed_user + " removed from notifications" + notification_record.delete() + if 'submit-adduseraccess' == action: + users = request.POST['users'] + usernames = "" + for user_id in users[:-1].split(','): + usernames += User.objects.get(pk=user_id).name + ", " + InvestigationAccess.objects.get_or_create(investigation_id=invst_id, user_id=user_id) + history_comment = usernames + " granted access" + if 'submit-trashuseraccess' == action: + record_id = request.POST['record_id'] + access_record = InvestigationAccess.objects.get(id=record_id) + removed_user = User.objects.get(pk=access_record.user_id).name + history_comment = removed_user + "'s access removed" + access_record.delete() + InvestigationHistory.objects.create(investigation_id=invst_id, comment=history_comment, date=datetime.now().strftime('%Y-%m-%d'), author=access.current_user_name) + return_data = { + "error": "ok", + } + + return HttpResponse(json.dumps( return_data ), content_type = "application/json") + except Exception as e: + _log("xhr_investigation_commit:no(%s)" % e) + return HttpResponse(json.dumps({"error":str(e) + "\n"}), content_type = "application/json") + + def tbd(request): - context = {'lvs_nos' : 0} + context = {} return toaster_render(request, 'tbd.html', context) - diff --git a/lib/srtgui/widgets.py b/lib/srtgui/widgets.py index b43b7619..0c2b56bf 100644 --- a/lib/srtgui/widgets.py +++ b/lib/srtgui/widgets.py @@ -34,7 +34,7 @@ from django.utils import timezone from django.http import JsonResponse from django.core.urlresolvers import reverse -from orm.models import Vulnerability, SrtSetting, Access, Keywords +from orm.models import SrtSetting, Access, Keywords from srtgui.templatetags.projecttags import sectohms, get_tasks from srtgui.templatetags.projecttags import json as template_json @@ -89,8 +89,12 @@ class ToasterTable(TemplateView): context['empty_state'] = self.empty_state # global variables - context['project_enable'] = ('1' == os.environ.get('TOASTER_BUILDSERVER')) - context['access'] = Access() + context['report_id_list'] = '' + + # Number of visits to this view, as counted in the session variable. + srt_user_id=int(self.request.session.get('srt_user_id', '0')) + context['srt_user_id'] = srt_user_id ### REMOVE + context['access'] = Access(srt_user_id) return context @@ -113,6 +117,12 @@ class ToasterTable(TemplateView): return super(ToasterTable, self).get(request, *args, **kwargs) + def put(self, *args, **kwargs): + """ function to implement in the subclass which sets up + the put method """ + _log("TABLE_PUT_SUPER") + pass + def get_filter_info(self, request, **kwargs): self.setup_filters(**kwargs) diff --git a/lib/srtmain/settings.py b/lib/srtmain/settings.py index c69bedfb..0fbc73ba 100644 --- a/lib/srtmain/settings.py +++ b/lib/srtmain/settings.py @@ -19,7 +19,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# Django settings for SRT +# Django settings for SRT import os @@ -49,6 +49,10 @@ DATABASES = { 'PASSWORD': '', #'HOST': '127.0.0.1', # e.g. mysql server #'PORT': '3306', # e.g. mysql port + }, + # Sqlite database lock problem + 'OPTIONS': { + 'timeout': 20, } } |