aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/orm/management/commands/lsupdates.py3
-rw-r--r--lib/orm/migrations/0001_initial.py92
-rwxr-xr-xlib/orm/migrations/0003_modified.py61
-rwxr-xr-xlib/orm/migrations/0004_defect_status.py35
-rwxr-xr-xlib/orm/migrations/0005_publish_report.py34
-rwxr-xr-xlib/orm/migrations/0006_reconcile.py410
-rw-r--r--lib/orm/models.py677
-rw-r--r--lib/srtgui/api.py379
-rw-r--r--lib/srtgui/reports.py492
-rw-r--r--lib/srtgui/tables.py809
-rw-r--r--lib/srtgui/templates/base.html4
-rw-r--r--lib/srtgui/templates/basetable_top.html5
-rw-r--r--lib/srtgui/templates/cve.html25
-rw-r--r--lib/srtgui/templates/cves-select-toastertable.html120
-rw-r--r--lib/srtgui/templates/defect.html54
-rw-r--r--lib/srtgui/templates/detail_search_header.html3
-rwxr-xr-xlib/srtgui/templates/history-cve-toastertable.html73
-rwxr-xr-xlib/srtgui/templates/history-defect-toastertable.html73
-rwxr-xr-xlib/srtgui/templates/history-investigation-toastertable.html73
-rwxr-xr-xlib/srtgui/templates/history-vulnerability-toastertable.html73
-rw-r--r--lib/srtgui/templates/investigation.html45
-rwxr-xr-xlib/srtgui/templates/maintenance.html78
-rw-r--r--lib/srtgui/templates/management.html47
-rwxr-xr-xlib/srtgui/templates/notifications-toastertable.html2
-rwxr-xr-xlib/srtgui/templates/publish-cve-toastertable.html162
-rwxr-xr-xlib/srtgui/templates/publish-defect-toastertable.html168
-rwxr-xr-xlib/srtgui/templates/publish-list-toastertable.html162
-rw-r--r--lib/srtgui/templates/publish.html319
-rw-r--r--lib/srtgui/templates/report.html41
-rwxr-xr-xlib/srtgui/templates/srtool_metadata_include.html36
-rw-r--r--lib/srtgui/templates/toastertable.html1
-rw-r--r--lib/srtgui/templates/vulnerability.html44
-rw-r--r--lib/srtgui/templatetags/projecttags.py18
-rw-r--r--lib/srtgui/urls.py26
-rw-r--r--lib/srtgui/views.py973
-rwxr-xr-xlib/srtmain/management/commands/update.py41
-rwxr-xr-xlib/users/models.py19
37 files changed, 4976 insertions, 701 deletions
diff --git a/lib/orm/management/commands/lsupdates.py b/lib/orm/management/commands/lsupdates.py
index 2a89a811..ca67713a 100644
--- a/lib/orm/management/commands/lsupdates.py
+++ b/lib/orm/management/commands/lsupdates.py
@@ -327,9 +327,7 @@ class Command(BaseCommand):
for source in data_sources:
if source.loaded and not (source.update_frequency == DataSource.ONSTARTUP):
- logger.info("Skipping source data from %s",source.description)
print("Skipping datasource %s (already loaded)" % (source.description))
- _log("Skipping datasource %s (already loaded)" % (source.description))
continue
elif not source.init:
# No Init action?
@@ -338,7 +336,6 @@ class Command(BaseCommand):
else:
logger.info("Fetching datasource %s:%s" % (source.source,source.description))
print("Fetching datasource '%s:%s'" % (source.source,source.description))
- _log("Fetching datasource '%s:%s'" % (source.source,source.description))
# Development/testing shortcut
if ('cve' == source.data) and ('yes' == SrtSetting.objects.get(name='SRTDBG_SKIP_CVE_IMPORT').value):
diff --git a/lib/orm/migrations/0001_initial.py b/lib/orm/migrations/0001_initial.py
index 0914d2bc..69ff00a3 100644
--- a/lib/orm/migrations/0001_initial.py
+++ b/lib/orm/migrations/0001_initial.py
@@ -99,10 +99,11 @@ class Migration(migrations.Migration):
('cvssV2_baseScore',models.CharField(max_length=50, blank=True)),
('cvssV2_severity', models.CharField(max_length=50, blank=True)),
- ('packages', models.TextField(blank=True, null=True)),
+ ('packages', models.TextField(blank=True)),
('score_date', models.DateTimeField(null=True, blank=True)),
('srt_updated', models.DateTimeField(auto_now=True)),
+## ('srt_created', models.DateTimeField(auto_now_add=True)),
],
),
@@ -158,18 +159,18 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='CveSource',
fields=[
- ('cve', models.ForeignKey(related_name='source2cve', default=None, to='orm.cve', null=True,on_delete=models.CASCADE,)),
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('cve', models.ForeignKey(default=None, related_name='source2cve', to='orm.cve', null=True,on_delete=models.CASCADE,)),
('datasource', models.ForeignKey(default=None, to='orm.datasource',null=True,on_delete=models.CASCADE,)),
],
),
-
migrations.CreateModel(
name='CveToCwe',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
- ('cve', models.ForeignKey(related_name='cve2cwe', default=None, to='orm.cve', null=True,on_delete=models.CASCADE,)),
- ('cwe', models.ForeignKey(default=None, to='orm.cwetable', null=True,on_delete=models.CASCADE,)),
+ ('cve', models.ForeignKey(related_name='cve2cwe', to='orm.cve', on_delete=models.CASCADE,)),
+ ('cwe', models.ForeignKey(to='orm.cwetable', on_delete=models.CASCADE,)),
],
),
@@ -177,10 +178,11 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='Package',
fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('mode', models.IntegerField(default=0)),
('name', models.CharField(max_length=50, blank=True)),
('realname', models.CharField(max_length=50, blank=True)),
- ('invalidname', models.TextField(blank=True, null=True)),
+ ('invalidname', models.TextField(blank=True)),
('weight', models.IntegerField(default=0)),
('cve_count', models.IntegerField(default=0)),
('vulnerability_count', models.IntegerField(default=0)),
@@ -192,8 +194,9 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='PackageToCve',
fields=[
- ('package', models.ForeignKey(related_name='package2cve', default=None, to='orm.package', null=True,on_delete=models.CASCADE,)),
- ('cve', models.ForeignKey(related_name='cve2package', default=None, to='orm.cve', null=True,on_delete=models.CASCADE,)),
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('package', models.ForeignKey(related_name='package2cve', to='orm.package', on_delete=models.CASCADE,)),
+ ('cve', models.ForeignKey(related_name='cve2package', to='orm.cve', on_delete=models.CASCADE,)),
('applicable', models.NullBooleanField(default=True, null=True)),
],
),
@@ -202,13 +205,13 @@ class Migration(migrations.Migration):
name='CveReference',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
- ('cve', models.ForeignKey(related_name='references', default=None, to='orm.cve', null=True,on_delete=models.CASCADE,)),
+ ('cve', models.ForeignKey(related_name='references', to='orm.cve', on_delete=models.CASCADE,)),
('hyperlink', models.CharField(max_length=100, null=True)),
('resource', models.CharField(max_length=100, null=True)),
('type', models.CharField(max_length=100, null=True)),
('source', models.CharField(max_length=100, null=True)),
('name', models.CharField(max_length=100, null=True)),
- ('datasource', models.ForeignKey(related_name='source_references', default=None, to='orm.datasource', null=True,on_delete=models.CASCADE,)),
+ ('datasource', models.ForeignKey(related_name='source_references', to='orm.datasource', default=None, null=True,on_delete=models.CASCADE,)),
],
),
@@ -216,7 +219,7 @@ class Migration(migrations.Migration):
name='CveHistory',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
- ('cve', models.ForeignKey(default=None, to='orm.cve', null=True,on_delete=models.CASCADE,)),
+ ('cve', models.ForeignKey(default=None, null=True, to='orm.cve', on_delete=models.CASCADE,)),
('comment', models.TextField(blank=True)),
('date', models.DateField(null=True, blank=True)),
('author', models.TextField(blank=True)),
@@ -258,8 +261,8 @@ class Migration(migrations.Migration):
name='CveToVulnerablility',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
- ('vulnerability', models.ForeignKey(default=None, to='orm.vulnerability', null=True,on_delete=models.CASCADE,)),
- ('cve', models.ForeignKey(default=None, to='orm.cve', null=True,on_delete=models.CASCADE,)),
+ ('vulnerability', models.ForeignKey(to='orm.vulnerability', on_delete=models.CASCADE,)),
+ ('cve', models.ForeignKey(to='orm.cve', on_delete=models.CASCADE,)),
],
),
@@ -267,7 +270,7 @@ class Migration(migrations.Migration):
name='VulnerabilityComments',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
- ('vulnerability', models.ForeignKey(default=None, to='orm.vulnerability', null=True,on_delete=models.CASCADE,)),
+ ('vulnerability', models.ForeignKey(to='orm.vulnerability', on_delete=models.CASCADE,)),
('comment', models.TextField(blank=True)),
('date', models.DateField(null=True, blank=True)),
('author', models.TextField(blank=True)),
@@ -278,7 +281,7 @@ class Migration(migrations.Migration):
name='VulnerabilityHistory',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
- ('vulnerability', models.ForeignKey(default=None, to='orm.vulnerability', null=True,on_delete=models.CASCADE,)),
+ ('vulnerability', models.ForeignKey(to='orm.vulnerability', on_delete=models.CASCADE,)),
('comment', models.TextField(blank=True)),
('date', models.DateField(null=True, blank=True)),
('author', models.TextField(blank=True)),
@@ -289,7 +292,7 @@ class Migration(migrations.Migration):
name='VulnerabilityUploads',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
- ('vulnerability', models.ForeignKey(default=None, to='orm.vulnerability', null=True,on_delete=models.CASCADE,)),
+ ('vulnerability', models.ForeignKey(to='orm.vulnerability',on_delete=models.CASCADE,)),
('description', models.TextField(blank=True)),
('path', models.TextField(blank=True)),
('size', models.IntegerField(default=0)),
@@ -310,7 +313,7 @@ class Migration(migrations.Migration):
('resolution', models.IntegerField(default=0)),
('publish', models.TextField(blank=True)),
('release_version', models.CharField(max_length=50)),
- ('product', models.ForeignKey(default=None, to='orm.product', null=True,on_delete=models.CASCADE,)),
+ ('product', models.ForeignKey(to='orm.product', on_delete=models.CASCADE,)),
('date_created', models.CharField(max_length=50)),
('date_updated', models.CharField(max_length=50)),
('srt_updated', models.DateTimeField(auto_now=True)),
@@ -323,8 +326,8 @@ 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)),
- ('vulnerability', models.ForeignKey(related_name='vulnerability_investigation',default=None, to='orm.vulnerability', null=True,on_delete=models.CASCADE,)),
- ('product', models.ForeignKey(related_name='references', default=None, to='orm.product', null=True,on_delete=models.CASCADE,)),
+ ('vulnerability', models.ForeignKey(related_name='vulnerability_investigation',to='orm.vulnerability', on_delete=models.CASCADE,)),
+ ('product', models.ForeignKey(related_name='references', to='orm.product',on_delete=models.CASCADE,)),
('public', models.BooleanField(default=True)),
('comments', models.TextField(blank=True)),
@@ -340,9 +343,9 @@ class Migration(migrations.Migration):
name='InvestigationToDefect',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
- ('investigation', models.ForeignKey(default=None, to='orm.investigation', null=True,on_delete=models.CASCADE,)),
- ('defect', models.ForeignKey(default=None, to='orm.defect', null=True,on_delete=models.CASCADE,)),
- ('product', models.ForeignKey(default=None, to='orm.product', null=True,on_delete=models.CASCADE,)),
+ ('investigation', models.ForeignKey(to='orm.investigation', on_delete=models.CASCADE,)),
+ ('defect', models.ForeignKey(to='orm.defect', on_delete=models.CASCADE,)),
+ ('product', models.ForeignKey(to='orm.product', on_delete=models.CASCADE,)),
],
),
@@ -350,7 +353,7 @@ class Migration(migrations.Migration):
name='InvestigationComments',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
- ('investigation', models.ForeignKey(default=None, to='orm.investigation', null=True,on_delete=models.CASCADE,)),
+ ('investigation', models.ForeignKey(to='orm.investigation', on_delete=models.CASCADE,)),
('comment', models.TextField(blank=True)),
('date', models.DateField(null=True, blank=True)),
('author', models.TextField(blank=True)),
@@ -361,7 +364,7 @@ class Migration(migrations.Migration):
name='InvestigationHistory',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
- ('investigation', models.ForeignKey(default=None, to='orm.investigation', null=True,on_delete=models.CASCADE,)),
+ ('investigation', models.ForeignKey(to='orm.investigation', on_delete=models.CASCADE,)),
('comment', models.TextField(blank=True)),
('date', models.DateField(null=True, blank=True)),
('author', models.TextField(blank=True)),
@@ -372,7 +375,7 @@ class Migration(migrations.Migration):
name='InvestigationUploads',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
- ('investigation', models.ForeignKey(default=None, to='orm.investigation', null=True,on_delete=models.CASCADE,)),
+ ('investigation', models.ForeignKey(to='orm.investigation', on_delete=models.CASCADE,)),
('description', models.TextField(blank=True)),
('path', models.TextField(blank=True)),
('size', models.IntegerField(default=0)),
@@ -394,38 +397,43 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='VulnerabilityAccess',
fields=[
- ('vulnerability', models.ForeignKey(default=None, to='orm.vulnerability', null=True,on_delete=models.CASCADE,)),
- ('user', models.ForeignKey(default=None, to='users.srtuser', null=True,on_delete=models.CASCADE,)),
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('vulnerability', models.ForeignKey(to='orm.vulnerability', on_delete=models.CASCADE,)),
+ ('user', models.ForeignKey(to='users.srtuser', on_delete=models.CASCADE,)),
],
),
migrations.CreateModel(
name='InvestigationAccess',
fields=[
- ('investigation', models.ForeignKey(default=None, to='orm.investigation', null=True,on_delete=models.CASCADE,)),
- ('user', models.ForeignKey(default=None, to='users.srtuser', null=True,on_delete=models.CASCADE,)),
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('investigation', models.ForeignKey(to='orm.investigation', on_delete=models.CASCADE,)),
+ ('user', models.ForeignKey(to='users.srtuser', on_delete=models.CASCADE,)),
],
),
migrations.CreateModel(
name='VulnerabilityNotification',
fields=[
- ('vulnerability', models.ForeignKey(default=None, to='orm.vulnerability', null=True,on_delete=models.CASCADE,)),
- ('user', models.ForeignKey(default=None, to='users.srtuser', null=True,on_delete=models.CASCADE,)),
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('vulnerability', models.ForeignKey(to='orm.vulnerability', on_delete=models.CASCADE,)),
+ ('user', models.ForeignKey(to='users.srtuser', on_delete=models.CASCADE,)),
],
),
migrations.CreateModel(
name='InvestigationNotification',
fields=[
- ('investigation', models.ForeignKey(default=None, to='orm.investigation', null=True,on_delete=models.CASCADE,)),
- ('user', models.ForeignKey(default=None, to='users.srtuser', null=True,on_delete=models.CASCADE,)),
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('investigation', models.ForeignKey(to='orm.investigation', on_delete=models.CASCADE,)),
+ ('user', models.ForeignKey(to='users.srtuser', on_delete=models.CASCADE,)),
],
),
migrations.CreateModel(
name='CpeTable',
fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('vulnerable', models.BooleanField(default='False')),
('cpeMatchString', models.TextField(blank=True)),
('cpe23Uri', models.TextField(blank=True)),
@@ -436,14 +444,16 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='CpeToCve',
fields=[
- ('cpe', models.ForeignKey(default=None, to='orm.cpetable',on_delete=models.CASCADE,)),
- ('cve', models.ForeignKey(default=None, to='orm.cve',on_delete=models.CASCADE,)),
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('cpe', models.ForeignKey(to='orm.cpetable',on_delete=models.CASCADE,)),
+ ('cve', models.ForeignKey(to='orm.cve',on_delete=models.CASCADE,)),
],
),
migrations.CreateModel(
name='CpeFilter',
fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('key_prime', models.CharField(max_length=50)),
('key_sub', models.CharField(max_length=50)),
('status', models.IntegerField(default=CpeFilter.UNDECIDED)),
@@ -456,6 +466,7 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='PublishPending',
fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('cve', models.ForeignKey(default=None, to='orm.cve',blank=True,null=True,on_delete=models.CASCADE,)),
('vulnerability', models.ForeignKey(default=None, to='orm.vulnerability',blank=True,null=True,on_delete=models.CASCADE,)),
('investigation', models.ForeignKey(default=None, to='orm.investigation',blank=True,null=True,on_delete=models.CASCADE,)),
@@ -468,19 +479,21 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='Notify',
fields=[
- ('category', models.CharField(max_length=50, null=True)),
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('category', models.CharField(max_length=50)),
('description', models.TextField()),
('url', models.TextField()),
('priority', models.IntegerField(default=0)),
('author', models.TextField()),
- ('srt_updated', models.DateTimeField(auto_now_add=True)),
- ('srt_created', models.DateTimeField(auto_now=True)),
+## ('srt_updated', models.DateTimeField(auto_now=True)),
+## ('srt_created', models.DateTimeField(auto_now_add=True)),
],
),
migrations.CreateModel(
name='NotifyAccess',
fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('notify', models.ForeignKey(default=None, to='orm.notify',blank=True,null=True,on_delete=models.CASCADE,)),
('user', models.ForeignKey(default=None, to='users.srtuser',blank=True,null=True,on_delete=models.CASCADE,)),
],
@@ -489,7 +502,8 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='NotifyCategories',
fields=[
- ('category', models.CharField(max_length=50, null=True)),
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('category', models.CharField(max_length=50)),
],
),
diff --git a/lib/orm/migrations/0003_modified.py b/lib/orm/migrations/0003_modified.py
new file mode 100755
index 00000000..e8752007
--- /dev/null
+++ b/lib/orm/migrations/0003_modified.py
@@ -0,0 +1,61 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('orm', '0002_updates'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='cve',
+ name='srt_created',
+ field=models.DateTimeField(auto_now_add=True),
+ ),
+ migrations.AddField(
+ model_name='cve',
+ name='tags',
+ field=models.TextField(blank=True, default='', null=True),
+ ),
+ migrations.AddField(
+ model_name='cve',
+ name='acknowledge_date',
+ field=models.DateTimeField(null=True),
+ ),
+
+ migrations.AddField(
+ model_name='investigation',
+ name='tags',
+ field=models.TextField(blank=True, default='', null=True),
+ ),
+ migrations.AddField(
+ model_name='investigation',
+ name='srt_created',
+ field=models.DateTimeField(auto_now_add=True),
+ ),
+ migrations.AddField(
+ model_name='investigation',
+ name='srt_updated',
+ field=models.DateTimeField(auto_now=True),
+ ),
+
+ migrations.AddField(
+ model_name='vulnerability',
+ name='tags',
+ field=models.TextField(blank=True, default='', null=True),
+ ),
+ migrations.AddField(
+ model_name='vulnerability',
+ name='srt_created',
+ field=models.DateTimeField(auto_now_add=True),
+ ),
+ migrations.AddField(
+ model_name='vulnerability',
+ name='srt_updated',
+ field=models.DateTimeField(auto_now=True),
+ ),
+ ]
diff --git a/lib/orm/migrations/0004_defect_status.py b/lib/orm/migrations/0004_defect_status.py
new file mode 100755
index 00000000..4e5b2f8d
--- /dev/null
+++ b/lib/orm/migrations/0004_defect_status.py
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('orm', '0003_modified'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='defect',
+ name='srt_status',
+ field=models.IntegerField(default=0),
+ ),
+ migrations.AddField(
+ model_name='defect',
+ name='srt_outcome',
+ field=models.IntegerField(default=0),
+ ),
+ migrations.AddField(
+ model_name='defect',
+ name='srt_priority',
+ field=models.IntegerField(default=0),
+ ),
+ migrations.AddField(
+ model_name='defect',
+ name='duplicate_of',
+ field=models.CharField(max_length=50, blank=True, default=''),
+ ),
+
+ ]
diff --git a/lib/orm/migrations/0005_publish_report.py b/lib/orm/migrations/0005_publish_report.py
new file mode 100755
index 00000000..6a0c34ee
--- /dev/null
+++ b/lib/orm/migrations/0005_publish_report.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('orm', '0004_defect_status'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='PublishSet',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('cve', models.ForeignKey(default=None, to='orm.cve', null=True, on_delete=models.CASCADE,)),
+ ('state', models.IntegerField(default=0)),
+ ('reason', models.TextField(blank=True)),
+ ],
+ ),
+
+ migrations.CreateModel(
+ name='DefectHistory',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('defect', models.ForeignKey(to='orm.defect', on_delete=models.CASCADE,)),
+ ('comment', models.TextField(blank=True)),
+ ('date', models.DateField(null=True, blank=True)),
+ ('author', models.TextField(blank=True)),
+ ],
+ ),
+
+ ]
diff --git a/lib/orm/migrations/0006_reconcile.py b/lib/orm/migrations/0006_reconcile.py
new file mode 100755
index 00000000..e7ad54bf
--- /dev/null
+++ b/lib/orm/migrations/0006_reconcile.py
@@ -0,0 +1,410 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.5 on 2020-01-12 06:21
+from __future__ import unicode_literals
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('orm', '0005_publish_report'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='notify',
+ name='srt_created',
+ field=models.DateTimeField(auto_now_add=True, null=True),
+ ),
+ migrations.AddField(
+ model_name='notify',
+ name='srt_updated',
+ field=models.DateTimeField(auto_now=True, null=True),
+ ),
+ migrations.AlterField(
+ model_name='cpefilter',
+ name='automatic',
+ field=models.BooleanField(default='False'),
+ ),
+ migrations.AlterField(
+ model_name='cpefilter',
+ name='key_prime',
+ field=models.CharField(max_length=40),
+ ),
+ migrations.AlterField(
+ model_name='cpefilter',
+ name='key_sub',
+ field=models.CharField(max_length=40),
+ ),
+ migrations.AlterField(
+ model_name='cpefilter',
+ name='status',
+ field=models.IntegerField(choices=[(0, 'Undecided'), (1, 'Include'), (2, 'Exclude'), (3, 'Manual')], default=0),
+ ),
+ migrations.AlterField(
+ model_name='cpetocve',
+ name='cpe',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cpe2cve', to='orm.CpeTable'),
+ ),
+ migrations.AlterField(
+ model_name='cpetocve',
+ name='cve',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cve2cpe', to='orm.Cve'),
+ ),
+ migrations.AlterField(
+ model_name='cve',
+ name='publish_state',
+ field=models.IntegerField(choices=[(0, 'Unpublished'), (1, 'Not to be Published'), (2, 'Published'), (3, 'Publish Request (New)'), (4, 'Publish Request (Update)'), (5, 'Publish Submitted')], default=0),
+ ),
+ migrations.AlterField(
+ model_name='cve',
+ name='score_date',
+ field=models.DateField(blank=True, null=True),
+ ),
+ migrations.AlterField(
+ model_name='cve',
+ name='srt_created',
+ field=models.DateTimeField(auto_now_add=True, null=True),
+ ),
+ migrations.AlterField(
+ model_name='cve',
+ name='srt_updated',
+ field=models.DateTimeField(auto_now=True, null=True),
+ ),
+ migrations.AlterField(
+ model_name='cve',
+ name='status',
+ field=models.IntegerField(choices=[(0, 'Historical'), (1, 'New'), (2, 'New-Reserved'), (3, 'Investigate'), (4, 'Vulnerable'), (5, 'Not Vulnerable')], default=1),
+ ),
+ migrations.AlterField(
+ model_name='cvehistory',
+ name='cve',
+ field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='cve_history', to='orm.Cve'),
+ ),
+ migrations.AlterField(
+ model_name='cvelocal',
+ name='cvssV3_attackVector',
+ field=models.CharField(blank=True, max_length=50),
+ ),
+ migrations.AlterField(
+ model_name='cvereference',
+ name='datasource',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='source_references', to='orm.DataSource'),
+ ),
+ migrations.AlterField(
+ model_name='cvesource',
+ name='cve',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='cve_parent', to='orm.Cve'),
+ ),
+ migrations.AlterField(
+ model_name='cvesource',
+ name='datasource',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='cve_datasource', to='orm.DataSource'),
+ ),
+ migrations.AlterField(
+ model_name='cvetocwe',
+ name='cwe',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cwe2cve', to='orm.CweTable'),
+ ),
+ migrations.AlterField(
+ model_name='cvetovulnerablility',
+ name='cve',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cve_to_vulnerability', to='orm.Cve'),
+ ),
+ migrations.AlterField(
+ model_name='cvetovulnerablility',
+ name='vulnerability',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vulnerability_to_cve', to='orm.Vulnerability'),
+ ),
+ migrations.AlterField(
+ model_name='datasource',
+ name='update_frequency',
+ field=models.IntegerField(choices=[(0, 'Minute'), (1, 'Hourly'), (2, 'Daily'), (3, 'Weekly'), (4, 'Monthly'), (5, 'OnDemand'), (6, 'OnStartup')], default=2),
+ ),
+ migrations.AlterField(
+ model_name='defect',
+ name='priority',
+ field=models.IntegerField(choices=[(0, 'Undefined'), (1, 'Low'), (2, 'Medium'), (3, 'High'), (4, 'Critical'), (5, 'PRIORITY_ERROR')], default=1),
+ ),
+ migrations.AlterField(
+ model_name='defect',
+ name='product',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='product_defect', to='orm.Product'),
+ ),
+ migrations.AlterField(
+ model_name='defect',
+ name='resolution',
+ field=models.IntegerField(choices=[(0, 'Unresolved'), (1, 'Resolved'), (2, 'Fixed'), (3, "Won't Fix"), (4, 'Withdrawn'), (5, 'Rejected'), (6, 'Duplicate'), (7, 'Not Applicable'), (8, 'Replaced By Requirement'), (9, 'Cannot Reproduce'), (10, 'Done')], default=0),
+ ),
+ migrations.AlterField(
+ model_name='defect',
+ name='srt_outcome',
+ field=models.IntegerField(choices=[(0, 'Open'), (1, 'Closed (Not Vulnerable)'), (2, 'Closed (Fixed)'), (3, "Closed (Won't Fix)")], default=0),
+ ),
+ migrations.AlterField(
+ model_name='defect',
+ name='srt_priority',
+ field=models.IntegerField(choices=[(0, 'Undefined'), (1, 'Low'), (2, 'Medium'), (3, 'High'), (4, 'Critical'), (5, 'PRIORITY_ERROR')], default=1),
+ ),
+ migrations.AlterField(
+ model_name='defect',
+ name='srt_status',
+ field=models.IntegerField(choices=[(0, 'Historical'), (1, 'New'), (2, 'New-Reserved'), (3, 'Investigate'), (4, 'Vulnerable'), (5, 'Not Vulnerable')], default=3),
+ ),
+ migrations.AlterField(
+ model_name='defect',
+ name='status',
+ field=models.IntegerField(choices=[(0, 'Open'), (1, 'In progress'), (2, 'On Hold'), (3, 'Checked In'), (4, 'Resolved'), (5, 'Closed')], default=0),
+ ),
+ migrations.AlterField(
+ model_name='defecthistory',
+ name='defect',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='defect_history', to='orm.Defect'),
+ ),
+ migrations.AlterField(
+ model_name='helptext',
+ name='area',
+ field=models.IntegerField(choices=[(0, 'variable')]),
+ ),
+ migrations.AlterField(
+ model_name='helptext',
+ name='text',
+ field=models.TextField(),
+ ),
+ migrations.AlterField(
+ model_name='investigation',
+ name='outcome',
+ field=models.IntegerField(choices=[(0, 'Open'), (1, 'Closed (Not Vulnerable)'), (2, 'Closed (Fixed)'), (3, "Closed (Won't Fix)")], default=3),
+ ),
+ migrations.AlterField(
+ model_name='investigation',
+ name='priority',
+ field=models.IntegerField(choices=[(0, 'Undefined'), (1, 'Low'), (2, 'Medium'), (3, 'High'), (4, 'Critical'), (5, 'PRIORITY_ERROR')], default=1),
+ ),
+ migrations.AlterField(
+ model_name='investigation',
+ name='product',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='product_investigation', to='orm.Product'),
+ ),
+ migrations.AlterField(
+ model_name='investigation',
+ name='srt_created',
+ field=models.DateTimeField(auto_now_add=True, null=True),
+ ),
+ migrations.AlterField(
+ model_name='investigation',
+ name='srt_updated',
+ field=models.DateTimeField(auto_now=True, null=True),
+ ),
+ migrations.AlterField(
+ model_name='investigation',
+ name='status',
+ field=models.IntegerField(choices=[(0, 'Historical'), (1, 'New'), (2, 'New-Reserved'), (3, 'Investigate'), (4, 'Vulnerable'), (5, 'Not Vulnerable')], default=0),
+ ),
+ migrations.AlterField(
+ model_name='investigation',
+ name='tags',
+ field=models.TextField(blank=True, default=''),
+ ),
+ migrations.AlterField(
+ model_name='investigationaccess',
+ name='investigation',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='investigation_users', to='orm.Investigation'),
+ ),
+ migrations.AlterField(
+ model_name='investigationaccess',
+ name='user',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='investigation_user', to=settings.AUTH_USER_MODEL),
+ ),
+ migrations.AlterField(
+ model_name='investigationcomments',
+ name='investigation',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='investigation_comments', to='orm.Investigation'),
+ ),
+ migrations.AlterField(
+ model_name='investigationhistory',
+ name='investigation',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='investigation_history', to='orm.Investigation'),
+ ),
+ migrations.AlterField(
+ model_name='investigationnotification',
+ name='investigation',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='investigation_notification', to='orm.Investigation'),
+ ),
+ migrations.AlterField(
+ model_name='investigationnotification',
+ name='user',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='investigation_notify', to=settings.AUTH_USER_MODEL),
+ ),
+ migrations.AlterField(
+ model_name='investigationtodefect',
+ name='defect',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='defect_to_investigation', to='orm.Defect'),
+ ),
+ migrations.AlterField(
+ model_name='investigationtodefect',
+ name='investigation',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='investigation_to_defect', to='orm.Investigation'),
+ ),
+ migrations.AlterField(
+ model_name='investigationtodefect',
+ name='product',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='defect_to_product', to='orm.Product'),
+ ),
+ migrations.AlterField(
+ model_name='investigationuploads',
+ name='investigation',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='investigation_uploads', to='orm.Investigation'),
+ ),
+ migrations.AlterField(
+ model_name='notify',
+ name='author',
+ field=models.TextField(blank=True),
+ ),
+ migrations.AlterField(
+ model_name='notify',
+ name='description',
+ field=models.TextField(blank=True),
+ ),
+ migrations.AlterField(
+ model_name='notify',
+ name='url',
+ field=models.TextField(blank=True),
+ ),
+ migrations.AlterField(
+ model_name='notifyaccess',
+ name='notify',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='todo2user', to='orm.Notify'),
+ ),
+ migrations.AlterField(
+ model_name='notifyaccess',
+ name='user',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='user2todo', to=settings.AUTH_USER_MODEL),
+ ),
+ migrations.AlterField(
+ model_name='package',
+ name='mode',
+ field=models.IntegerField(choices=[(0, 'For'), (1, 'Against')], default=0),
+ ),
+ migrations.AlterField(
+ model_name='publishpending',
+ name='cve',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='publish_pending_cves', to='orm.Cve'),
+ ),
+ migrations.AlterField(
+ model_name='publishpending',
+ name='investigation',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='publish_pending_investigations', to='orm.Investigation'),
+ ),
+ migrations.AlterField(
+ model_name='publishpending',
+ name='note',
+ field=models.TextField(blank=True),
+ ),
+ migrations.AlterField(
+ model_name='publishpending',
+ name='vulnerability',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='publish_pending_vulnerabilities', to='orm.Vulnerability'),
+ ),
+ migrations.AlterField(
+ model_name='publishset',
+ name='state',
+ field=models.IntegerField(choices=[(0, 'Skip'), (1, 'New'), (2, 'Modified'), (3, 'New_User'), (4, 'Modified_User'), (5, 'PUBLISH_SET_ERROR')], default=0),
+ ),
+ migrations.AlterField(
+ model_name='srtsetting',
+ name='helptext',
+ field=models.TextField(),
+ ),
+ migrations.AlterField(
+ model_name='vulnerability',
+ name='outcome',
+ field=models.IntegerField(choices=[(0, 'Open'), (1, 'Closed (Not Vulnerable)'), (2, 'Closed (Fixed)'), (3, "Closed (Won't Fix)")], default=0),
+ ),
+ migrations.AlterField(
+ model_name='vulnerability',
+ name='priority',
+ field=models.IntegerField(choices=[(0, 'Undefined'), (1, 'Low'), (2, 'Medium'), (3, 'High'), (4, 'Critical'), (5, 'PRIORITY_ERROR')], default=1),
+ ),
+ migrations.AlterField(
+ model_name='vulnerability',
+ name='public',
+ field=models.BooleanField(default=True),
+ ),
+ migrations.AlterField(
+ model_name='vulnerability',
+ name='srt_created',
+ field=models.DateTimeField(auto_now_add=True, null=True),
+ ),
+ migrations.AlterField(
+ model_name='vulnerability',
+ name='srt_updated',
+ field=models.DateTimeField(auto_now=True, null=True),
+ ),
+ migrations.AlterField(
+ model_name='vulnerability',
+ name='status',
+ field=models.IntegerField(choices=[(0, 'Historical'), (1, 'New'), (2, 'New-Reserved'), (3, 'Investigate'), (4, 'Vulnerable'), (5, 'Not Vulnerable')], default=3),
+ ),
+ migrations.AlterField(
+ model_name='vulnerability',
+ name='tags',
+ field=models.TextField(blank=True, default=''),
+ ),
+ migrations.AlterField(
+ model_name='vulnerabilityaccess',
+ name='user',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vulnerability_user', to=settings.AUTH_USER_MODEL),
+ ),
+ migrations.AlterField(
+ model_name='vulnerabilityaccess',
+ name='vulnerability',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vulnerability_users', to='orm.Vulnerability'),
+ ),
+ migrations.AlterField(
+ model_name='vulnerabilitycomments',
+ name='vulnerability',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vulnerability_comments', to='orm.Vulnerability'),
+ ),
+ migrations.AlterField(
+ model_name='vulnerabilityhistory',
+ name='vulnerability',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vulnerability_history', to='orm.Vulnerability'),
+ ),
+ migrations.AlterField(
+ model_name='vulnerabilitynotification',
+ name='user',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vulnerability_notify', to=settings.AUTH_USER_MODEL),
+ ),
+ migrations.AlterField(
+ model_name='vulnerabilitynotification',
+ name='vulnerability',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vulnerability_notification', to='orm.Vulnerability'),
+ ),
+ migrations.AlterField(
+ model_name='vulnerabilitytoinvestigation',
+ name='investigation',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='investigation2vulnerability', to='orm.Investigation'),
+ ),
+ migrations.AlterField(
+ model_name='vulnerabilitytoinvestigation',
+ name='vulnerability',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vulnerability2investigation', to='orm.Vulnerability'),
+ ),
+ migrations.AlterField(
+ model_name='vulnerabilityuploads',
+ name='vulnerability',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vulnerability_uploads', to='orm.Vulnerability'),
+ ),
+ migrations.AlterUniqueTogether(
+ name='cpefilter',
+ unique_together=set([('key_prime', 'key_sub')]),
+ ),
+ migrations.AlterUniqueTogether(
+ name='product',
+ unique_together=set([('name', 'version', 'profile')]),
+ ),
+ ]
diff --git a/lib/orm/models.py b/lib/orm/models.py
index 0f6cfb17..0dd73ba4 100644
--- a/lib/orm/models.py
+++ b/lib/orm/models.py
@@ -114,6 +114,266 @@ def GitURLField(**kwargs):
# Core Classes
+# Helper class to common mappings
+class SRTool():
+
+ # Global date format
+ DATE_FORMAT = '%Y-%m-%d'
+ DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S'
+
+ # SRTool Priority
+ UNDEFINED = 0
+ LOW = 1
+ MEDIUM = 2
+ HIGH = 3
+ CRITICAL = 4
+ PRIORITY_ERROR = 99
+ SRT_PRIORITY = (
+ (UNDEFINED, 'Undefined'),
+ (LOW, 'Low'),
+ (MEDIUM, 'Medium'),
+ (HIGH, 'High'),
+ (CRITICAL, 'Critical'),
+ )
+ @staticmethod
+ def priority_text(index):
+ if (0 > index) or (index >= len(SRTool.SRT_PRIORITY)):
+ return 'PRIORITY_ERROR'
+ return SRTool.SRT_PRIORITY[index][1]
+ @staticmethod
+ def priority_index(value):
+ for item in SRTool.SRT_PRIORITY:
+ if value == item[1]:
+ return item[0]
+ return SRTool.PRIORITY_ERROR
+
+ # SRTool Severity (same integer values as prority)
+ SRT_SEVERITY = (
+ (UNDEFINED, 'UNDEFINED'),
+ (LOW, 'LOW'),
+ (MEDIUM, 'MEDIUM'),
+ (HIGH, 'HIGH'),
+ (CRITICAL, 'CRITICAL'),
+ )
+ @staticmethod
+ def severity_text(index):
+ if (0 > index) or (index >= len(SRTool.SRT_SEVERITY)):
+ return 'SEVERITY_ERROR'
+ return SRTool.SRT_SEVERITY[index][1]
+ @staticmethod
+ def severity_index(value):
+ for item in SRTool.SRT_SEVERITY:
+ if value == item[1]:
+ return item[0]
+ return SRTool.PRIORITY_ERROR
+
+ # SRTool Status
+ HISTORICAL = 0
+ NEW = 1
+ NEW_RESERVED = 2
+ INVESTIGATE = 3
+ VULNERABLE = 4
+ NOT_VULNERABLE = 5
+ NEW_INACTIVE = 6
+ INVESTIGATE_INACTIVE = 7
+ VULNERABLE_INACTIVE = 8
+ NOT_VULNERABLE_INACTIVE = 9
+ STATUS_ERROR = 99
+ SRT_STATUS = (
+ (HISTORICAL, 'Historical'),
+ (NEW, 'New'),
+ (NEW_RESERVED, 'New-Reserved'),
+ (INVESTIGATE, 'Investigate'),
+ (VULNERABLE, 'Vulnerable'),
+ (NOT_VULNERABLE, 'Not Vulnerable'),
+ (NEW_INACTIVE, '(New)'),
+ (INVESTIGATE_INACTIVE, '(Investigate)'),
+ (VULNERABLE_INACTIVE, '(Vulnerable)'),
+ (NOT_VULNERABLE_INACTIVE, '(Not Vulnerable)'),
+ )
+ @staticmethod
+ def status_text(index):
+ if (0 > index) or (index >= len(SRTool.SRT_STATUS)):
+ return 'STATUS_ERROR'
+ return SRTool.SRT_STATUS[index][1]
+ @staticmethod
+ def status_index(value):
+ for item in SRTool.SRT_STATUS:
+ if value == item[1]:
+ return item[0]
+ return SRTool.STATUS_ERROR
+ @staticmethod
+ def status_to_inactive(value):
+ if SRTool.NEW == value:
+ return SRTool.NEW_INACTIVE
+ elif SRTool.INVESTIGATE == value:
+ return SRTool.INVESTIGATE_INACTIVE
+ elif SRTool.VULNERABLE == value:
+ return SRTool.VULNERABLE_INACTIVE
+ elif SRTool.NOT_VULNERABLE == value:
+ return SRTool.NOT_VULNERABLE_INACTIVE
+ else:
+ return value
+ @staticmethod
+ def status_to_active(value):
+ if SRTool.NEW_INACTIVE == value:
+ return SRTool.NEW
+ elif SRTool.INVESTIGATE_INACTIVE == value:
+ return SRTool.INVESTIGATE
+ elif SRTool.VULNERABLE_INACTIVE == value:
+ return SRTool.VULNERABLE
+ elif SRTool.NOT_VULNERABLE_INACTIVE == value:
+ return SRTool.NOT_VULNERABLE
+ else:
+ return value
+
+ OPEN = 0
+ CLOSED = 1
+ FIXED = 2
+ NOT_FIX = 3
+ OUTCOME_ERROR = 4
+ SRT_OUTCOME = (
+ (OPEN, 'Open'),
+ (CLOSED, 'Closed (Not Vulnerable)'),
+ (FIXED, 'Closed (Fixed)'),
+ (NOT_FIX, "Closed (Won't Fix)"),
+ )
+ @staticmethod
+ def outcome_text(index):
+ if (0 > index) or (index >= len(SRTool.SRT_OUTCOME)):
+ return "OUTCOME_ERROR"
+ return SRTool.SRT_OUTCOME[index][1]
+ @staticmethod
+ def outcome_index(value):
+ for item in SRTool.SRT_OUTCOME:
+ if value == item[1]:
+ return item[0]
+ return SRTool.OUTCOME_ERROR
+
+ # Publish state
+ PUBLISH_UNPUBLISHED = 0
+ PUBLISH_NOPUBLISH = 1
+ PUBLISH_PUBLISHED = 2
+ PUBLISH_REQUEST = 3
+ PUBLISH_UPDATE = 4
+ PUBLISH_SUBMITTED = 5
+ PUBLISH_ERROR = 99
+ SRT_PUBLISH_STATE = (
+ (PUBLISH_UNPUBLISHED, 'Unpublished'),
+ (PUBLISH_NOPUBLISH, 'Not to be Published'),
+ (PUBLISH_PUBLISHED, 'Published'),
+ (PUBLISH_REQUEST, 'Publish Request (New)'),
+ (PUBLISH_UPDATE, 'Publish Request (Update)'),
+ (PUBLISH_SUBMITTED, 'Publish Submitted'),
+ )
+ @staticmethod
+ def publish_text(index):
+ if (0 > index) or (index >= len(SRTool.SRT_PUBLISH_STATE)):
+ return SRTool.SRT_PUBLISH_STATE[SRTool.PUBLISH_ERROR][1]
+ return 'PUBLISH_ERROR'
+ @staticmethod
+ def publish_index(value):
+ for item in SRTool.SRT_PUBLISH_STATE:
+ if value == item[1]:
+ return item[0]
+ return SRTool.PUBLISH_ERROR
+
+ # Normalize displayed dates
+ @staticmethod
+ def date_ymd_text(value):
+ if isinstance(value,datetime):
+ return(value.strftime("%Y-%m-%d"))
+ return(value)
+
+ # Extract dictionary tag values
+ @staticmethod
+ def get_dict_tag(tag,dict_str,default=None):
+ dict = json.loads(dict_str)
+ if tag in dict:
+ return dict[tag]
+ return default
+
+
+# Helper class to format and track updates
+# Enforce strict formatting and content to enable reporting, change filtering, pretty printing
+class Update():
+ # General history prefix format (type,source,semicolon-joined changes):
+ # UPDATE(User):Priority(%s,%s);Tag();Status(%s,%s) {helpful text}
+ # CREATE(Defect): {Created from defect ABCD-1234}
+ # Update report check strings: 'UPDATE(','Priority(','Status('
+
+ # General update label
+ UPDATE_STR = "UPDATE(%s):"
+ CREATE_STR = "CREATE(%s):"
+ UPDATE_PREFIX_STR = "UPDATE("
+ CREATE_PREFIX_STR = "CREATE("
+
+ # Update sources
+ SOURCE_USER = "User"
+ SOURCE_TRIAGE = "Triage"
+ SOURCE_CVE = "CVE"
+ SOURCE_DEFECT = "Defect"
+
+ # Update labels (no string overlaps allowed)
+ NEW_NAME = "New_Name(%s,%s)"
+ PRIORITY = "Priority(%s,%s)"
+ STATUS = "Status(%s,%s)"
+ SEVERITY_V3 = "Severity_V3(%s,%s)"
+ SEVERITY_V2 = "Severity_V2(%s,%s)"
+ OUTCOME = "Outcome(%s,%s)"
+ RELEASE = "Release(%s,%s)"
+ DESCRIPTION = "Description()"
+ LASTMODIFIEDDATE = "LastModifiedDate(%s,%s)"
+ NOTE = "User_Note()"
+ PRIVATE_NOTE = "Private_Note()"
+ TAG = "Tag()"
+ PUBLISH_STATE = "Publish_State(%s,%s)"
+ PUBLISH_DATE = "Publish_Date(%s,%s)"
+ AFFECTED_COMPONENT = "Affected_Component(%s,%s)"
+ ACKNOWLEDGE_DATE = "AcknowledgeDate(%s,%s)"
+ ATTACH_CVE = "Attach_CVE(%s)"
+ DETACH_CVE = "Detach_CVE(%s)"
+ ATTACH_VUL = "Attach_Vulnerability(%s)"
+ DETACH_VUL = "Detach_Vulnerability(%s)"
+ ATTACH_INV = "Attach_Investigration(%s)"
+ DETACH_INV = "Detach_Investigration(%s)"
+ ATTACH_DEV = "Attach_Defect(%s)"
+ DETACH_DEV = "Detach_Defect(%s)"
+ ATTACH_DOC = "Attach_Document(%s)"
+ DETACH_DOC = "Detach_Document(%s)"
+ ATTACH_USER_NOTIFY = "Attach_User_Notify(%s)"
+ DETACH_USER_NOTIFY = "Detach_User_Notify(%s)"
+ ATTACH_ACCESS = "Attach_Access(%s)"
+ DETACH_ACCESS = "Detach_Access(%s)"
+ ATTACH_PRODUCT = "Attach_Product(%s)"
+ DETACH_PRODUCT = "Detach_Product(%s)"
+ MARK_NEW = "Mark_New(%s)"
+ MARK_UPDATED = "Mark_Updated(%s)"
+ MARK_PREFIX = "Mark_"
+ MARK_NEW_PREFIX = "Mark_New"
+ MARK_UPDATED_PREFIX = "Mark_Updated"
+ MARK_UNMARK = "Mark_Unmark()"
+
+ # Update Report list
+ UPDATE_CHECK_LIST = (
+ PRIORITY,
+ STATUS,
+ SEVERITY_V3,
+ SEVERITY_V2,
+ RELEASE,
+ MARK_NEW,
+ MARK_UPDATED,
+ )
+
+ #Any matching string for the period indicates reportable change
+ @staticmethod
+ def get_check_list():
+ check_list = []
+ for check in UPDATE_CHECK_LIST:
+ simple_check = re.sub(r'(.*', '(', check)
+ check_list.append(simple_check)
+ return(check_list)
+
class SrtSetting(models.Model):
name = models.CharField(max_length=63)
helptext = models.TextField()
@@ -128,6 +388,11 @@ class SrtSetting(models.Model):
return(SrtSetting.objects.get(name=key).value)
except:
return(default)
+ @staticmethod
+ def set_setting(key,value):
+ obj,created = SrtSetting.objects.get_or_create(name=key)
+ obj.value = value
+ obj.save()
class HelpText(models.Model):
@@ -163,6 +428,10 @@ class DataSource(models.Model):
DATE_FORMAT = '%Y-%m-%d'
DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S'
+ # Metadata
+ LOOKUP_MISSING = 'LOOKUP-MISSING'
+ PREVIEW_SOURCE = 'PREVIEW-SOURCE'
+
key = models.CharField(max_length=20)
data = models.CharField(max_length=20)
source = models.CharField(max_length=20)
@@ -194,20 +463,22 @@ class CweTable(models.Model):
class Cve(models.Model):
search_allowed_fields = ['name', 'description', 'publishedDate',
- 'lastModifiedDate', 'comments', 'comments_private']
+ 'lastModifiedDate', 'comments', 'comments_private', 'tags', 'packages']
# SRTool Priority
UNDEFINED = 0
- MINOR = 1
- LOW = 2
- MEDIUM = 3
- HIGH = 4
+ LOW = 1
+ MEDIUM = 2
+ HIGH = 3
+ CRITICAL = 4
+ PRIORITY_ERROR = 5
PRIORITY = (
(UNDEFINED, 'Undefined'),
- (MINOR, 'Minor'),
(LOW, 'Low'),
(MEDIUM, 'Medium'),
(HIGH, 'High'),
+ (CRITICAL, 'Critical'),
+ (PRIORITY_ERROR, 'PRIORITY_ERROR'),
)
# WR Status
@@ -256,6 +527,7 @@ class Cve(models.Model):
status = models.IntegerField(choices=STATUS, default=NEW)
comments = models.TextField(blank=True)
comments_private = models.TextField(blank=True)
+ tags = models.TextField(blank=True, default='', null=True)
cve_data_type = models.CharField(max_length=100, blank=True)
cve_data_format = models.CharField(max_length=50, blank=True)
@@ -264,6 +536,7 @@ class Cve(models.Model):
public = models.BooleanField(default=True)
publish_state = models.IntegerField(choices=PUBLISH_STATE, default=PUBLISH_UNPUBLISHED)
publish_date = models.CharField(max_length=50, blank=True)
+ acknowledge_date = models.DateTimeField(null=True)
description = models.TextField(blank=True)
publishedDate = models.CharField(max_length=50, blank=True)
@@ -281,25 +554,41 @@ class Cve(models.Model):
packages = models.TextField(blank=True)
score_date = models.DateField(null=True, blank=True)
- srt_updated = models.DateTimeField(auto_now=True)
+ srt_updated = models.DateTimeField(auto_now=True, null=True)
+ srt_created = models.DateTimeField(auto_now_add=True, null=True)
@property
def get_priority_text(self):
- return Cve.PRIORITY[int(self.priority)][1]
+ return SRTool.priority_text(self.priority)
+ @property
+ def get_status_text(self):
+ return SRTool.status_text(self.status)
@property
def get_publish_text(self):
return Cve.PUBLISH_STATE[int(self.publish_state)][1]
@property
- def get_status_text(self):
- return Cve.STATUS[int(self.status)][1]
- @property
def is_local(self):
try:
CveLocal.objects.get(name=self.name)
return True
except:
return False
-
+ @property
+ def get_publishset_state(self):
+ try:
+ obj = PublishSet.objects.get(cve=self)
+ return obj.state_text
+ except:
+ return PublishSet.PUBLISH_SET_STATE[PublishSet.PUBLISH_SET_NONE][1]
+ @property
+ def get_public_comments(self):
+ the_comments = self.comments.strip()
+ the_packages = self.packages.strip()
+ if not the_comments or not the_packages:
+ return '%s%s' % (the_comments,the_packages)
+ if the_comments == the_packages:
+ return the_comments
+ return '%s' % (the_comments)
class CveDetail():
# CPE item list
@@ -317,6 +606,7 @@ class CveDetail():
description = ''
publishedDate = ''
+ acknowledge_date = ''
lastModifiedDate = ''
url_title = ''
url = ''
@@ -431,11 +721,12 @@ class CveLocal(models.Model):
# Map of all sources for the given CVE
class CveSource(models.Model):
- cve = models.ForeignKey(Cve,related_name="cve_parent",on_delete=models.CASCADE,)
+ cve = models.ForeignKey(Cve,related_name="cve_parent",blank=True, null=True,on_delete=models.CASCADE,)
datasource = models.ForeignKey(DataSource,related_name="cve_datasource", blank=True, null=True,on_delete=models.CASCADE,)
class CveHistory(models.Model):
- cve = models.ForeignKey(Cve,related_name="cve_history",on_delete=models.CASCADE,)
+ search_allowed_fields = ['cve__name', 'comment', 'date', 'author']
+ cve = models.ForeignKey(Cve,related_name="cve_history",default=None, null=True, on_delete=models.CASCADE,)
comment = models.TextField(blank=True)
date = models.DateField(null=True, blank=True)
author = models.TextField(blank=True)
@@ -485,12 +776,12 @@ class Package(models.Model):
@staticmethod
def update_computed_counts(package_name=None):
# A 'None' indicates all packages
- _log("update_computed_counts0:%s" % package_name)
+# _log("update_computed_counts0:%s" % package_name)
if package_name:
package_list = Package.objects.filter(name=package_name)
else:
package_list = Package.objects.all()
- _log("update_computed_counts:p:%s" % len(package_list))
+# _log("update_computed_counts:p:%s" % len(package_list))
for package in package_list:
try:
state = "p"
@@ -498,7 +789,7 @@ class Package(models.Model):
package.vulnerability_count = 0
package.investigation_count = 0
package.defect_count = 0
- _log("update_computed_counts2:c:%s" % len(package.package2cve.all()))
+# _log("update_computed_counts2:c:%s" % len(package.package2cve.all()))
for pc in package.package2cve.all():
cve = pc.cve
package.cve_count += 1
@@ -559,7 +850,7 @@ class CveToCwe(models.Model):
class CveReference(models.Model):
cve = models.ForeignKey(Cve,related_name="references",on_delete=models.CASCADE,)
- hyperlink = models.CharField(max_length=100)
+ hyperlink = models.CharField(max_length=100, null=True)
resource = models.CharField(max_length=100, null=True)
type = models.CharField(max_length=100, null=True)
source = models.CharField(max_length=100, null=True)
@@ -586,26 +877,20 @@ class Product(models.Model):
def long_name(self):
long_name = '%s %s %s' % (self.name,self.version,self.profile)
return long_name.strip()
- def get_defect_tag(self,tag):
- dict = json.loads(self.defect_tags)
- try:
- return dict[tag]
- except:
- _log("ERROR:get_defect_tag:%s[%s]" % (dict,tag))
- return ''
- def get_product_tag(self,tag):
- dict = json.loads(self.product_tags)
- try:
- return dict[tag]
- except:
- _log("ERROR:get_product_tags:%s[%s]" % (dict,tag))
- return ''
+ def get_defect_tag(self,tag,default=None):
+ return SRTool.get_dict_tag(tag,self.defect_tags,default)
+ def get_product_tag(self,tag,default=None):
+ return SRTool.get_dict_tag(tag,self.product_tags,default)
+ def get_defect_str(self):
+ return self.defect_tags.replace('"','')
+ def get_product_str(self):
+ return self.product_tags.replace('"','')
# VULNERABILITY
# Company-level Vulnerablility Record
class Vulnerability(models.Model):
- search_allowed_fields = ['name', 'comments', 'comments_private']
+ search_allowed_fields = ['name', 'comments', 'comments_private', 'tags']
HISTORICAL = 0
NEW = 1
@@ -632,18 +917,21 @@ class Vulnerability(models.Model):
(FIXED, 'Closed (Fixed)'),
(NOT_FIX, "Closed (Won't Fix)"),
)
- # SRTool Severity, matched with Cve/Defect Priority with placeholder for 'minor'
+
+ # SRTool Priority
UNDEFINED = 0
- MINOR = 1
- LOW = 2
- MEDIUM = 3
- HIGH = 4
+ LOW = 1
+ MEDIUM = 2
+ HIGH = 3
+ CRITICAL = 4
+ PRIORITY_ERROR = 5
PRIORITY = (
(UNDEFINED, 'Undefined'),
- (MINOR, 'Minor'),
(LOW, 'Low'),
(MEDIUM, 'Medium'),
(HIGH, 'High'),
+ (CRITICAL, 'Critical'),
+ (PRIORITY_ERROR, 'PRIORITY_ERROR'),
)
name = models.CharField(max_length=50)
@@ -653,21 +941,26 @@ class Vulnerability(models.Model):
public = models.BooleanField(default=True)
comments = models.TextField(blank=True, default='')
comments_private = models.TextField(blank=True, default='')
+ tags = models.TextField(blank=True, default='')
status = models.IntegerField(choices=STATUS, default=INVESTIGATE)
outcome = models.IntegerField(choices=OUTCOME, default=OPEN)
priority = models.IntegerField(choices=PRIORITY, default=LOW)
+ srt_updated = models.DateTimeField(auto_now=True, null=True)
+ srt_created = models.DateTimeField(auto_now_add=True, null=True)
+
+ @property
+ def get_priority_text(self):
+ return SRTool.priority_text(self.priority)
@property
def get_status_text(self):
- return Vulnerability.STATUS[int(self.status)][1]
+ return SRTool.status_text(self.status)
@property
def get_outcome_text(self):
+ return SRTool.outcome_text(self.outcome)
return Vulnerability.OUTCOME[int(self.outcome)][1]
@property
- def get_priority_text(self):
- return Vulnerability.PRIORITY[int(self.priority)][1]
- @property
def get_long_name(self):
if self.cve_primary_name:
return "%s (%s)" % (self.name,self.cve_primary_name)
@@ -698,6 +991,9 @@ class Vulnerability(models.Model):
print("Error in new_vulnerability_name")
raise
return "VUL-%05d" % index
+ @property
+ def investigation_list(self):
+ return VulnerabilityToInvestigation.objects.filter(vulnerability_id=self.id).order_by('investigation__product__order')
class VulnerabilityComments(models.Model):
vulnerability = models.ForeignKey(Vulnerability,related_name="vulnerability_comments",on_delete=models.CASCADE,)
@@ -706,6 +1002,7 @@ class VulnerabilityComments(models.Model):
author = models.TextField(blank=True)
class VulnerabilityHistory(models.Model):
+ search_allowed_fields = ['vulnerability__name', 'comment', 'date', 'author']
vulnerability = models.ForeignKey(Vulnerability,related_name="vulnerability_history",on_delete=models.CASCADE,)
comment = models.TextField(blank=True)
date = models.DateField(null=True, blank=True)
@@ -733,58 +1030,62 @@ 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
- NONE = 0
- MINOR = 1
- LOW = 2
- MEDIUM = 3
- HIGH = 4
- Priority = (
- (NONE, 'None'),
- (MINOR, 'P4'),
- (LOW, 'P3'),
- (MEDIUM, 'P2'),
- (HIGH, 'P1'),
+ # Defect/SRTool Priority
+ DEFECT_UNDEFINED = 0
+ DEFECT_LOW = 1
+ DEFECT_MEDIUM = 2
+ DEFECT_HIGH = 3
+ DEFECT_CRITICAL = 4
+ DEFECT_PRIORITY_ERROR = 5
+ DEFECT_PRIORITY = (
+ (DEFECT_UNDEFINED, 'Undefined'),
+ (DEFECT_LOW, 'Low'),
+ (DEFECT_MEDIUM, 'Medium'),
+ (DEFECT_HIGH, 'High'),
+ (DEFECT_CRITICAL, 'Critical'),
+ (DEFECT_PRIORITY_ERROR, 'PRIORITY_ERROR'),
)
- OPEN = 0
- IN_PROGRESS = 1
- ON_HOLD = 2
- CHECKED_IN = 3
- RESOLVED = 4
- CLOSED = 5
- Status = (
- (OPEN, 'Open'),
- (IN_PROGRESS, 'In progress'),
- (ON_HOLD, 'On Hold'),
- (CHECKED_IN, 'Checked In'),
- (RESOLVED, 'Resolved'),
- (CLOSED, 'Closed'),
+ DEFECT_STATUS_OPEN = 0
+ DEFECT_STATUS_IN_PROGRESS = 1
+ DEFECT_STATUS_ON_HOLD = 2
+ DEFECT_STATUS_CHECKED_IN = 3
+ DEFECT_STATUS_RESOLVED = 4
+ DEFECT_STATUS_CLOSED = 5
+ DEFECT_STATUS = (
+ (DEFECT_STATUS_OPEN, 'Open'),
+ (DEFECT_STATUS_IN_PROGRESS, 'In progress'),
+ (DEFECT_STATUS_ON_HOLD, 'On Hold'),
+ (DEFECT_STATUS_CHECKED_IN, 'Checked In'),
+ (DEFECT_STATUS_RESOLVED, 'Resolved'),
+ (DEFECT_STATUS_CLOSED, 'Closed'),
)
- UNRESOLVED = 0
- 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'),
+ DEFECT_UNRESOLVED = 0
+ DEFECT_RESOLVED = 1
+ DEFECT_FIXED = 2
+ DEFECT_WILL_NOT_FIX = 3
+ DEFECT_WITHDRAWN = 4
+ DEFECT_REJECTED = 5
+ DEFECT_DUPLICATE = 6
+ DEFECT_NOT_APPLICABLE = 7
+ DEFECT_REPLACED_BY_REQUIREMENT = 8
+ DEFECT_CANNOT_REPRODUCE = 9
+ DEFECT_DONE = 10
+ DEFECT_RESOLUTION = (
+ (DEFECT_UNRESOLVED, 'Unresolved'),
+ (DEFECT_RESOLVED, 'Resolved'),
+ (DEFECT_FIXED, 'Fixed'),
+ (DEFECT_WILL_NOT_FIX, 'Won\'t Fix'),
+ (DEFECT_WITHDRAWN, 'Withdrawn'),
+ (DEFECT_REJECTED, 'Rejected'),
+ (DEFECT_DUPLICATE, 'Duplicate'),
+ (DEFECT_NOT_APPLICABLE, 'Not Applicable'),
+ (DEFECT_REPLACED_BY_REQUIREMENT, 'Replaced By Requirement'),
+ (DEFECT_CANNOT_REPRODUCE, 'Cannot Reproduce'),
+ (DEFECT_DONE, 'Done'),
)
+
Components = (
'BSP',
'Kernel',
@@ -796,12 +1097,62 @@ class Defect(models.Model):
'Test',
)
+ HISTORICAL = 0
+ NEW = 1
+ NEW_RESERVED = 2
+ INVESTIGATE = 3
+ VULNERABLE = 4
+ NOT_VULNERABLE = 5
+ SRT_STATUS = (
+ (HISTORICAL, 'Historical'),
+ (NEW, 'New'),
+ (NEW_RESERVED, 'New-Reserved'),
+ (INVESTIGATE, 'Investigate'),
+ (VULNERABLE, 'Vulnerable'),
+ (NOT_VULNERABLE, 'Not Vulnerable'),
+ )
+
+ OPEN = 0
+ CLOSED = 1
+ FIXED = 2
+ NOT_FIX = 3
+ SRT_OUTCOME = (
+ (OPEN, 'Open'),
+ (CLOSED, 'Closed (Not Vulnerable)'),
+ (FIXED, 'Closed (Fixed)'),
+ (NOT_FIX, "Closed (Won't Fix)"),
+ )
+
+ # SRTool Priority
+ UNDEFINED = 0
+ LOW = 1
+ MEDIUM = 2
+ HIGH = 3
+ CRITICAL = 4
+ PRIORITY_ERROR = 5
+ SRT_PRIORITY = (
+ (UNDEFINED, 'Undefined'),
+ (LOW, 'Low'),
+ (MEDIUM, 'Medium'),
+ (HIGH, 'High'),
+ (CRITICAL, 'Critical'),
+ (PRIORITY_ERROR, 'PRIORITY_ERROR'),
+ )
+
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)
+ duplicate_of = models.CharField(max_length=50, blank=True, default='')
+
+ # External defect specific values
+ priority = models.IntegerField(choices=DEFECT_PRIORITY, default=DEFECT_LOW)
+ status = models.IntegerField(choices=DEFECT_STATUS, default=DEFECT_STATUS_OPEN)
+ resolution = models.IntegerField(choices=DEFECT_RESOLUTION, default=DEFECT_UNRESOLVED)
+ # SRTool compatible values
+ srt_priority = models.IntegerField(choices=SRT_PRIORITY, default=LOW)
+ srt_status = models.IntegerField(choices=SRT_STATUS, default=INVESTIGATE)
+ srt_outcome = models.IntegerField(choices=SRT_OUTCOME, default=OPEN)
+
publish = models.TextField(blank=True)
release_version = models.CharField(max_length=50)
product = models.ForeignKey(Product,related_name="product_defect",on_delete=models.CASCADE,)
@@ -812,25 +1163,77 @@ class Defect(models.Model):
# Methods
@property
+ def get_defect_priority_text(self):
+ return Defect.DEFECT_PRIORITY[int(self.priority)][1]
+ @property
+ def get_defect_status_text(self):
+ return Defect.DEFECT_STATUS[int(self.status)][1]
+ @property
+ def get_defect_resolution_text(self):
+ return Defect.DEFECT_RESOLUTION[int(self.resolution)][1]
+ @property
def get_priority_text(self):
- return Defect.Priority[int(self.priority)][1]
+ return SRTool.priority_text(self.srt_priority)
@property
def get_status_text(self):
- return Defect.Status[int(self.status)][1]
+ return SRTool.status_text(self.srt_status)
+ @property
+ def get_outcome_text(self):
+ return SRTool.outcome_text(self.srt_outcome)
+ @property
+ def get_date_created_text(self):
+ return re.sub(r"T.*", "", self.date_created)
+ @property
+ def get_date_updated_text(self):
+ return re.sub(r"T.*", "", self.date_updated)
@property
- def get_resolution_text(self):
- return Defect.Resolution[int(self.resolution)][1]
def get_long_name(self):
if self.release_version:
return "%s (%s)" % (self.name,self.release_version)
return "%s" % (self.name)
+ @property
+ def get_cve_names(self):
+ cve_list = []
+ for di in InvestigationToDefect.objects.filter(defect = self):
+ for i2v in VulnerabilityToInvestigation.objects.filter(investigation = di.investigation):
+ for v2c in CveToVulnerablility.objects.filter(vulnerability = i2v.vulnerability):
+ cve_list.append(v2c.cve.name)
+ return ','.join(cve_list)
+ @property
+ def get_cve_ids(self):
+ cve_list = []
+ for di in InvestigationToDefect.objects.filter(defect = self):
+ for i2v in VulnerabilityToInvestigation.objects.filter(investigation = di.investigation):
+ for v2c in CveToVulnerablility.objects.filter(vulnerability = i2v.vulnerability):
+ cve_list.append(str(v2c.cve.id))
+ return ','.join(cve_list)
+ @property
+ def get_publishset_state(self):
+ pub_list = []
+ cve_list = self.get_cve_names
+ if not cve_list:
+ return PublishSet.PUBLISH_SET_STATE[PublishSet.PUBLISH_SET_NONE][1]
+ for cve_name in cve_list.split(','):
+ try:
+ cve = Cve.objects.get(name = cve_name)
+ pub_list.append(cve.get_publishset_state)
+ except Exception as e:
+ pass
+ return ','.join(pub_list)
+
+class DefectHistory(models.Model):
+ search_allowed_fields = ['defect__name', 'comment', 'date', 'author']
+ defect = models.ForeignKey(Defect,related_name="defect_history",on_delete=models.CASCADE,)
+ comment = models.TextField(blank=True)
+ date = models.DateField(null=True, blank=True)
+ author = models.TextField(blank=True)
# INVESTIGATION
# Product-level Vulnerablility Investigation Record
class Investigation(models.Model):
- search_allowed_fields = ['name', 'comments', 'comments_private']
+ search_allowed_fields = ['name', 'comments', 'comments_private', 'tags']
HISTORICAL = 0
NEW = 1
@@ -858,18 +1261,22 @@ class Investigation(models.Model):
(NOT_FIX, "Closed (Won't Fix)"),
)
+ # SRTool Priority
UNDEFINED = 0
- MINOR = 1
- LOW = 2
- MEDIUM = 3
- HIGH = 4
+ LOW = 1
+ MEDIUM = 2
+ HIGH = 3
+ CRITICAL = 4
+ PRIORITY_ERROR = 5
PRIORITY = (
(UNDEFINED, 'Undefined'),
- (MINOR, 'Minor'),
(LOW, 'Low'),
(MEDIUM, 'Medium'),
(HIGH, 'High'),
+ (CRITICAL, 'Critical'),
+ (PRIORITY_ERROR, 'PRIORITY_ERROR'),
)
+
name = models.CharField(max_length=50)
vulnerability = models.ForeignKey(Vulnerability,related_name="vulnerability_investigation",on_delete=models.CASCADE,)
product = models.ForeignKey(Product,related_name="product_investigation",on_delete=models.CASCADE,)
@@ -877,21 +1284,25 @@ class Investigation(models.Model):
public = models.BooleanField(default=True)
comments = models.TextField(blank=True)
comments_private = models.TextField(blank=True)
+ tags = models.TextField(blank=True, default='')
status = models.IntegerField(choices=STATUS, default=OPEN)
outcome = models.IntegerField(choices=OUTCOME, default=INVESTIGATE)
priority = models.IntegerField(choices=PRIORITY, default=LOW)
+ srt_updated = models.DateTimeField(auto_now=True, null=True)
+ srt_created = models.DateTimeField(auto_now_add=True, null=True)
+
# Methods
@property
+ def get_priority_text(self):
+ return SRTool.priority_text(self.priority)
+ @property
def get_status_text(self):
- return Investigation.STATUS[int(self.status)][1]
+ return SRTool.status_text(self.status)
@property
def get_outcome_text(self):
- return Investigation.OUTCOME[int(self.outcome)][1]
- @property
- def get_priority_text(self):
- return Investigation.PRIORITY[int(self.priority)][1]
+ return SRTool.outcome_text(self.outcome)
@property
def get_long_name(self):
if self.vulnerability and self.vulnerability.cve_primary_name:
@@ -920,6 +1331,7 @@ class InvestigationComments(models.Model):
author = models.TextField(blank=True)
class InvestigationHistory(models.Model):
+ search_allowed_fields = ['investigation__name', 'comment', 'date', 'author']
investigation = models.ForeignKey(Investigation,related_name="investigation_history",on_delete=models.CASCADE,)
comment = models.TextField(blank=True)
date = models.DateField(null=True, blank=True)
@@ -979,17 +1391,21 @@ def _log_args(msg, *args, **kwargs):
# Action items waiting
class Notify(models.Model):
search_allowed_fields = ['category','description','url']
+
+ # SRTool Priority
UNDEFINED = 0
- MINOR = 1
- LOW = 2
- MEDIUM = 3
- HIGH = 4
+ LOW = 1
+ MEDIUM = 2
+ HIGH = 3
+ CRITICAL = 4
+ PRIORITY_ERROR = 5
PRIORITY = (
(UNDEFINED, 'Undefined'),
- (MINOR, 'Minor'),
(LOW, 'Low'),
(MEDIUM, 'Medium'),
(HIGH, 'High'),
+ (CRITICAL, 'Critical'),
+ (PRIORITY_ERROR, 'PRIORITY_ERROR'),
)
category = models.CharField(max_length=50)
@@ -997,8 +1413,10 @@ class Notify(models.Model):
priority = models.IntegerField(default=0)
url = models.TextField(blank=True)
author = models.TextField(blank=True)
- srt_updated = models.DateTimeField(auto_now_add=True)
- srt_created = models.DateTimeField(auto_now=True)
+## srt_updated = models.DateTimeField(auto_now_add=True)
+## srt_created = models.DateTimeField(auto_now=True)
+ srt_updated = models.DateTimeField(auto_now=True, null=True)
+ srt_created = models.DateTimeField(auto_now_add=True, null=True)
@property
def get_priority_text(self):
@@ -1013,6 +1431,35 @@ class NotifyAccess(models.Model):
class NotifyCategories(models.Model):
category = models.CharField(max_length=50)
+class PublishSet(models.Model):
+ search_allowed_fields = ['cve__name','cve__description','cve__status','cve__publishedDate','cve__lastModifiedDate']
+
+ # Publish state
+ PUBLISH_SET_NONE = 0
+ PUBLISH_SET_NEW = 1
+ PUBLISH_SET_MODIFIED = 2
+ PUBLISH_SET_NEW_USER = 3
+ PUBLISH_SET_MODIFIED_USER = 4
+ PUBLISH_SET_ERROR = 5
+ PUBLISH_SET_STATE = (
+ (PUBLISH_SET_NONE, 'Skip'),
+ (PUBLISH_SET_NEW, 'New'),
+ (PUBLISH_SET_MODIFIED, 'Modified'),
+ (PUBLISH_SET_NEW_USER, 'New_User'),
+ (PUBLISH_SET_MODIFIED_USER, 'Modified_User'),
+ (PUBLISH_SET_ERROR, 'PUBLISH_SET_ERROR'),
+ )
+
+ cve = models.ForeignKey(default=None, to='orm.cve', null=True, on_delete=models.CASCADE,)
+ state = models.IntegerField(choices=PUBLISH_SET_STATE, default=PUBLISH_SET_NONE)
+ reason = models.TextField(blank=True)
+
+ @property
+ def state_text(self):
+ if (0 > self.state) or (self.state >= len(self.PUBLISH_SET_STATE)):
+ return self.PUBLISH_SET_STATE[self.PUBLISH_SET_ERROR][1]
+ return self.PUBLISH_SET_STATE[self.state][1]
+
#
# Database Cache Support
#
diff --git a/lib/srtgui/api.py b/lib/srtgui/api.py
index 16ff88b0..e84113a0 100644
--- a/lib/srtgui/api.py
+++ b/lib/srtgui/api.py
@@ -22,6 +22,10 @@ import os
import sys
import logging
import subprocess
+from datetime import datetime, date
+import traceback
+import re
+import json
from django.http import JsonResponse
@@ -70,6 +74,31 @@ def execute_process(*args):
return result.returncode,result.stdout,result.stderr
#
+# Update CVE datasource list: (a) fetch alt sources, (b) refresh preview sources
+#
+
+# #### TODO
+def update_cve_datasources(source_filter=''):
+ # Attach all matching CVE sources
+ _log("Alternate1:%s" % (cve_object.name))
+ query_set = DataSource.objects.filter(data="cve")
+ if source_filter:
+ query_set =query_set.filter(source=source_filter)
+ for ds in query_set:
+ _log("Alternate2:%s" % (ds.key))
+ if ds.cve_filter and cve_object.name.startswith(ds.cve_filter):
+ cve_source_object,created = CveSource.objects.get_or_create(cve=cve_object,datasource=ds)
+ _log("Alternate CVE source %s for %s (created=%s)" % (ds.key,cve_object.name,created))
+
+ # Force update the CVE summary data from sources
+ result_returncode,result_stdout,result_stderr = execute_process(
+ './bin/nist/srtool_nist.py',
+ '--update-cve-list',
+ cve_object.name,
+ '--force'
+ )
+
+#
# Extract Upstream CVE record details
#
@@ -82,6 +111,7 @@ def readCveDetails_Upstream(cve, cve_datasource):
# Get the object
lookup_command = cve_datasource.lookup
+ lookup_attributes = ''
if not lookup_command:
v.description = "ERROR(%s):missing lookup command" % (cve_datasource.description)
return v
@@ -135,6 +165,16 @@ def readCveDetails_Upstream(cve, cve_datasource):
#_log("cpe_list:%s:%s:" % (cve.name,value))
elif name == 'ref_list':
v.ref_list = value
+ elif name == 'ATTRIBUTES':
+ # Returned metadata
+ lookup_attributes = value
+ _log("NOTE:readCveDetails_Upstream:%s:%s" % (v.name,v.cvssV2_severity))
+
+ # Check for metadata special cases
+ if cve_datasource.LOOKUP_MISSING in lookup_attributes:
+ pass
+
+
return v
#
@@ -338,3 +378,342 @@ def summaryCveDetails(cve,cve_sources):
cve_detail.ref_list = cve_main.ref_list
return cve_detail,cve_html
+
+#
+# Publish Support
+#
+
+
+# Accumulate the history status changes over the date range
+# CVE rec
+# cve[name][key][first,last]
+ # Severity_V3(8.0 HIGH,5.4 MEDIUM)
+ # Severity_V2(8.5 HIGH,4.3 MEDIUM)
+ # Priority(UNDEFINED,Medium)
+ # Status(Historical,Vulnerable)
+# CVE product/defect
+# cve[name][product][defect][key][first,last]
+ # Release(,8.0.0.30)
+ # Status(Historical,Vulnerable)
+
+
+# Calculate the publishable CVEs for a given period
+# Accumulate the CVE history status changes over the date range
+def publishCalculate(date_start,date_stop):
+ from orm.models import SrtSetting, PublishSet, Cve, CveHistory, DefectHistory, Update, SRTool, InvestigationToDefect, Product
+
+ # Precompile the filter for efficiency
+ update_regex = re.compile(r"([^\(]*)\(([^,]*),([^\)]*)\)")
+
+ # Accumulate the CVE history status changes
+ # Severity_V3(8.0 HIGH,5.4 MEDIUM)
+ # Severity_V2(8.5 HIGH,4.3 MEDIUM)
+ # Priority(UNDEFINED,Medium)
+ # Status(Historical,Vulnerable)
+ cve_updates = {}
+ # cve_updates[cve_id_str][key][first,last]
+ def cve_update(cve_id_str,change):
+ m = update_regex.search(change)
+ if m:
+ field = m.group(1)
+ value_old = m.group(2)
+ value_new = m.group(3)
+ else:
+ field = re.sub(r"\(.*", "", change)
+ value_old = ''
+ value_new = ''
+
+ if not field in ('Severity_V3','Severity_V2'):
+ return
+
+ # Fix-up
+ if ('Severity_V3' == field) or ('Severity_V2' == field):
+ score_old,severity_old = value_old.split(' ')
+ score_new,severity_new = value_new.split(' ')
+ if score_old.replace('0','') == score_new.replace('0',''):
+ return
+ if severity_old == severity_new:
+ return
+ value_old = severity_old
+ value_new = severity_new
+
+ if not cve_id_str in cve_updates:
+ cve_updates[cve_id_str] = {}
+ if not field in cve_updates[cve_id_str]:
+ # Preset the old value and accumulate the new value
+ cve_updates[cve_id_str][field] = [value_old,value_new]
+ else:
+ # Only accumulate the new value
+ cve_updates[cve_id_str][field] = [cve_updates[cve_id_str][field][0],value_new]
+
+ # Accumulate the CVE Defect history status changes
+ # Status(Historical,Vulnerable)
+ # Priority(UNDEFINED,Medium)
+ # Release(,8.0.0.30)
+ defect_updates = {}
+ # defect_updates[cve_id_str][product][defect][key][first,last]
+ def defect_update(cve_id_str,product_key,defect_name,change):
+ m = update_regex.search(change)
+ if m:
+ field = m.group(1)
+ value_old = m.group(2)
+ value_new = m.group(3)
+ else:
+ field = re.sub(r"\(.*", "", change)
+ value_old = ''
+ value_new = ''
+
+ if not cve_id_str in defect_updates:
+ defect_updates[cve_id_str] = {}
+ if not product_key in defect_updates[cve_id_str]:
+ defect_updates[cve_id_str][product_key] = {}
+ if not defect_name in defect_updates[cve_id_str][product_key]:
+ defect_updates[cve_id_str][product_key][defect_name] = {}
+ if not field in defect_updates[cve_id_str][product_key][defect_name]:
+ # Preset the old value and accumulate the new value
+ defect_updates[cve_id_str][product_key][defect_name][field] = [value_old,value_new]
+ else:
+ # Only accumulate the new value
+ defect_updates[cve_id_str][product_key][defect_name][field] = [defect_updates[cve_id_str][product_key][defect_name][field][0],value_new]
+
+ try:
+ PublishSet.objects.all().delete()
+
+ # Convert dates to CVE-type dates
+ date_start_text = date_start.strftime('%Y-%m-%d')
+ date_stop_text = date_stop.strftime('%Y-%m-%d')
+
+ # Find all candidate new CVEs
+ queryset = \
+ Cve.objects.filter(acknowledge_date__gte=date_start_text,acknowledge_date__lte=date_stop_text) | \
+ Cve.objects.filter(srt_created__gte=date_start,srt_created__lte=date_stop)
+ exclude_list = [Cve.NEW, Cve.HISTORICAL, Cve.NEW_RESERVED]
+ queryset = queryset.exclude(status__in=exclude_list)
+
+ # Gather only CVE histories from currently supported products
+ # This assumes that the defect names have the format "<DEFECT_KEY>-*"
+ # Example entry: "CREATE(Defect): {Created from defect <DEFECT_KEY>-7058}"
+ # Gather the supported product keys
+ product_filter = []
+ product_query = Product.objects.filter()
+ for product in product_query:
+ if "support" == product.get_product_tag('mode').order_by('-order'):
+ product_filter.append(product.get_defect_tag('key'))
+ # Scan the CVE histories
+ new_cves = {}
+ create_filter = Update.CREATE_STR % Update.SOURCE_DEFECT
+ for cve in queryset:
+ try:
+ history_query = CveHistory.objects.filter(cve=cve,comment__startswith=create_filter)
+ if history_query:
+ supported = False
+ _keys = []
+ for history in history_query:
+ _keys.append(history.comment)
+ for key in product_filter:
+ # CREATE(Defect): {Created from defect <DEFECT_KEY>}
+ if 0 < history.comment.find(' %s-' % key):
+ supported = True
+ break
+ if not supported:
+ continue
+ except:
+ # No matches to test
+ pass
+
+ p = PublishSet(cve=cve, state=PublishSet.PUBLISH_SET_NEW, reason='LastModifiedDate(,%s)' % cve.lastModifiedDate)
+ p.save()
+ new_cves[str(cve.id)] = True
+
+ # Fixup
+ bootstrap_date = datetime.strptime('2019-03-10',"%Y-%m-%d")
+ if date_start < bootstrap_date:
+ date_start = bootstrap_date
+
+ # Find all candidate updated CVEs, made by user or imported from CVE integration tools
+ # UPDATE(CVE):Severity_V3(8.0 HIGH,5.4 MEDIUM);Severity_V2(8.5 HIGH,4.3 MEDIUM);LastModifiedDate(2017-08-12,2019-03-19)
+ for ch in CveHistory.objects.filter(date__gte=date_start_text,date__lte=date_stop_text,comment__startswith=Update.UPDATE_PREFIX_STR).order_by('date'):
+ # Already new
+ if ch.cve.id in new_cves:
+ continue
+ # Ignore CVEs with non-applicable
+ if ch.cve.status in [Cve.NEW, Cve.HISTORICAL, Cve.NEW_RESERVED]:
+ continue
+ change_str = re.sub(r"^.*:", "", ch.comment)
+ change_str = re.sub(r"{.*", "", change_str)
+ for change in change_str.split(';'):
+ cve_update(str(ch.cve.id),change)
+
+ # Find all candidate updated Defects, made by user or imported from defect integration tools
+ # UPDATE(Defect):Priority(UNDEFINED,Medium);Status(Historical,Investigate);Release(,8.0.0.30) {Update from defect LIN8-8669}
+ for dh in DefectHistory.objects.filter(date__gte=date_start_text,date__lte=date_stop_text,comment__startswith=Update.UPDATE_PREFIX_STR).order_by('date'):
+ # Get the product key
+ for i2d in InvestigationToDefect.objects.filter(defect_id=dh.defect.id):
+ # get first product key
+ product_key = i2d.product.key
+ break
+ else:
+ # no investigation for this orphaned defect
+ continue
+ change_str = re.sub(r"^.*:", "", dh.comment)
+ change_str = re.sub(r"{.*", "", change_str)
+ for change in change_str.split(';'):
+ cve_id_strs = dh.defect.get_cve_ids
+ for cve_id_str in cve_id_strs.split(','):
+ # Already new
+ if cve_id_str in new_cves:
+ continue
+ defect_update(cve_id_str,product_key,dh.defect.name,change)
+
+
+ # Merge manual Marks to table
+ queryset = CveHistory.objects.filter(
+ date__gte=date_start,
+ date__lte=date_stop)
+ for cvehistory in queryset:
+ if cvehistory.comment.startswith(Update.MARK_NEW_PREFIX):
+ publish_object,created = PublishSet.objects.get_or_create(cve=cvehistory.cve)
+ publish_object.state = PublishSet.PUBLISH_SET_NEW_USER
+ publish_object.reason= "CC " + cvehistory.comment
+ publish_object.save()
+ elif cvehistory.comment.startswith(Update.MARK_UPDATED_PREFIX):
+ publish_object,created = PublishSet.objects.get_or_create(cve=cvehistory.cve)
+ publish_object.state = PublishSet.PUBLISH_SET_MODIFIED_USER
+ publish_object.reason= "DD " + cvehistory.comment
+ publish_object.save()
+ elif cvehistory.comment.startswith(Update.MARK_UNMARK):
+ publish_object,created = PublishSet.objects.get_or_create(cve=cvehistory.cve)
+ publish_object.state = PublishSet.PUBLISH_SET_NONE
+ publish_object.reason= "EE " + cvehistory.comment
+ _log("PUBLISH_SET_NONE(%d):%s:%s" % (cvehistory.id,cvehistory.cve.name,cvehistory.comment))
+ publish_object.save()
+
+ #
+ # for all cves, merge data, create publish records
+ # cve_change_tree[cve_id_str][dict]
+ #
+
+ cve_change_tree = {}
+ # cve_updates[cve_id_str][key][first,last]
+ for cve_id_str in cve_updates:
+ if not cve_id_str in cve_change_tree:
+ cve_change_tree[cve_id_str] = {}
+ for key in cve_updates[cve_id_str]:
+ cve_change_tree[cve_id_str][key] = cve_updates[cve_id_str][key]
+
+ # defect_updates[cve_id_str][product][defect][key][first,last]
+ for cve_id_str in defect_updates:
+ if not cve_id_str in cve_change_tree:
+ cve_change_tree[cve_id_str] = {}
+ for product in defect_updates[cve_id_str]:
+ product_updates = []
+ for defect in defect_updates[cve_id_str][product]:
+ defect_changes = []
+ for key in defect_updates[cve_id_str][product][defect].keys():
+ defect_changes.append('%s(%s,%s)' % (key,defect_updates[cve_id_str][product][defect][key][0],defect_updates[cve_id_str][product][defect][key][1]))
+ product_updates.append('%s[%s]' % (defect,','.join(defect_changes)))
+ cve_change_tree[cve_id_str][product] = '|'.join(product_updates)
+
+ # Create publish records
+ for cve_id_str in cve_change_tree:
+ publish_object,created = PublishSet.objects.get_or_create(cve_id=int(cve_id_str))
+ publish_object.state = PublishSet.PUBLISH_SET_MODIFIED
+ publish_object.reason = json.dumps(cve_change_tree[cve_id_str])
+ publish_object.save()
+
+ # Update last calculation date
+ SrtSetting.set_setting('publish_last_calc',datetime.today().strftime('%m/%d/%Y %H:%M'))
+ except Exception as e:
+ _log("ERROR:publishCalculate:%s,%s." % (e,traceback.print_stack()))
+
+
+# Reset: for each CVE History:
+# (a) Remove any MARK_NEW or MARK_UPDATED in the period
+#
+def publishReset(date_start,date_stop):
+ from orm.models import Cve, CveHistory, Update
+ # Fixup
+ #bootstrap_date = datetime.strptime('2019-03-10',"%Y-%m-%d")
+
+ # Deleted manual Marks from table
+ queryset = CveHistory.objects.filter(
+ date__gte=date_start,
+ date__lte=date_stop)
+ for cvehistory in queryset:
+ if cvehistory.comment.startswith(Update.MARK_PREFIX):
+ cvehistory.delete()
+
+# MarkNew: for each CVE:
+# (a) Remove any previous MARK_UPDATED in the period (there can be many periods)
+# (a) Remove any previous MARK_NEW (there can only be one)
+# (b) Insert MARK_NEW at period's middle date
+#
+def publishMarkNew(cve_list,reason_map,date_start,date_stop):
+ from orm.models import Cve, CveHistory, Update
+ # Fixup
+ bootstrap_date = datetime.strptime('2019-03-10',"%Y-%m-%d")
+ if date_start < bootstrap_date:
+ date_start = bootstrap_date
+ mid_date = date_start + (date_stop - date_start)/2
+ for cve_name in cve_list.split(','):
+ cve = Cve.objects.get(name = cve_name)
+ # Remove marks in period
+ queryset = CveHistory.objects.filter(
+ cve = cve,
+ comment__startswith = Update.MARK_PREFIX,
+ date__gte=date_start,
+ date__lte=date_stop)
+ for cvehistory in queryset:
+ cvehistory.delete()
+ # Remove all mark news
+ queryset = CveHistory.objects.filter(cve = cve,comment__startswith = Update.MARK_NEW_PREFIX)
+ for cvehistory in queryset:
+ cvehistory.delete()
+ cvehistory = CveHistory(cve=cve, comment=Update.MARK_NEW % reason_map[cve_name], date=mid_date, author='SRTool')
+ cvehistory.save()
+
+# MarkModified: for each CVE:
+# (a) Remove any previous MARK_UPDATED in the period (there can be many periods)
+# (b) Insert MARK_UPDATED at period's middle date
+#
+def publishMarkModified(cve_list,reason_map,date_start,date_stop):
+ from orm.models import Cve, CveHistory, Update
+ # Fixup
+ bootstrap_date = datetime.strptime('2019-03-10',"%Y-%m-%d")
+ if date_start < bootstrap_date:
+ date_start = bootstrap_date
+ mid_date = date_start + (date_stop - date_start)/2
+ for cve_name in cve_list.split(','):
+ cve = Cve.objects.get(name = cve_name)
+ # Remove mark in period
+ queryset = CveHistory.objects.filter(
+ cve = cve,
+ comment__startswith = Update.MARK_PREFIX,
+ date__gte=date_start,
+ date__lte=date_stop)
+ for cvehistory in queryset:
+ cvehistory.delete()
+ cvehistory = CveHistory(cve=cve, comment=Update.MARK_UPDATED % reason_map[cve_name], date=mid_date, author='SRTool')
+ cvehistory.save()
+
+# MarkNone: for each CVE:
+# (a) Remove any MARK_NEW or MARK_UPDATED in the period
+#
+def publishMarkNone(cve_list,date_start,date_stop):
+ from orm.models import Cve, CveHistory, Update
+ # Fixup
+ bootstrap_date = datetime.strptime('2019-03-10',"%Y-%m-%d")
+ date_start_max = max(date_start,bootstrap_date)
+ mid_date = date_start_max + (date_stop - date_start_max)/2
+ for cve_name in cve_list.split(','):
+ cve = Cve.objects.get(name = cve_name)
+ queryset = CveHistory.objects.filter(
+ cve = cve,
+ comment__startswith = Update.MARK_PREFIX,
+ date__gte=date_start,
+ date__lte=date_stop)
+ for cvehistory in queryset:
+ cvehistory.delete()
+ cvehistory = CveHistory(cve=cve, comment=Update.MARK_UNMARK, date=mid_date, author='SRTool')
+ cvehistory.save()
+
diff --git a/lib/srtgui/reports.py b/lib/srtgui/reports.py
index 572e45bf..e282a8d0 100644
--- a/lib/srtgui/reports.py
+++ b/lib/srtgui/reports.py
@@ -25,6 +25,8 @@ import csv
from orm.models import Cve, CveSource, Vulnerability, Investigation, Defect, Product
from orm.models import Package
+from orm.models import SRTool, SrtSetting
+from orm.models import PublishSet, DefectHistory
from srtgui.api import readCveDetails, summaryCveDetails
from django.db.models import Q
@@ -93,7 +95,14 @@ class ManagementReport(Report):
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> \
+ <input type="radio" name="format" value="csv"> CSV \
+ (Separator: \
+ <select name="csv_separator"> \
+ <option value="semi">Semi-colon</option> \
+ <option value="comma">Comma</option> \
+ <option value="tab">Tab</option> \
+ </select>) \
+ <br> \
'
return context
@@ -107,12 +116,15 @@ class ManagementReport(Report):
format = request_POST.get('format', '')
title = request_POST.get('title', '')
report_type = request_POST.get('report_type', '')
+ csv_separator = request_POST.get('csv_separator', 'semi')
- report_name = '%s/management_%s_%s.%s' % (SRT_REPORT_DIR,report_type,datetime.today().strftime('%Y%m%d%H%M'),format)
+ 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"
+ tab = ';'
+ if csv_separator == 'comma': tab = ','
+ if csv_separator == 'tab': tab = '\t'
else:
tab = " = "
@@ -125,11 +137,13 @@ class ManagementReport(Report):
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_critical',tab,Vulnerability.objects.filter(priority=Vulnerability.CRITICAL).count()))
file.write("%s%s%s\n" % ('vulnerability_high',tab,Vulnerability.objects.filter(priority=Vulnerability.HIGH).count()))
file.write("%s%s%s\n" % ('vulnerability_medium',tab,Vulnerability.objects.filter(priority=Vulnerability.MEDIUM).count()))
- file.write("%s%s%s\n" % ('vulnerability_low',tab,Vulnerability.objects.filter(priority=Vulnerability.HIGH).count()))
+ file.write("%s%s%s\n" % ('vulnerability_low',tab,Vulnerability.objects.filter(priority=Vulnerability.LOW).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_critical',tab,Investigation.objects.filter(priority=Investigation.CRITICAL).count()))
file.write("%s%s%s\n" % ('investigation_high',tab,Investigation.objects.filter(priority=Investigation.HIGH).count()))
file.write("%s%s%s\n" % ('investigation_medium',tab,Investigation.objects.filter(priority=Investigation.MEDIUM).count()))
file.write("%s%s%s\n" % ('investigation_low',tab,Investigation.objects.filter(priority=Investigation.LOW).count()))
@@ -254,7 +268,7 @@ class CveReport(Report):
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)
+ 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:
@@ -417,9 +431,9 @@ class VulnerabilityReport(Report):
report_type = request_POST.get('report_type', '')
record_list = request_POST.get('record_list', '')
- v = Vulnerability.objects.get(id=record_list)
+ vulnerability = 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)
+ 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:
@@ -429,24 +443,26 @@ class VulnerabilityReport(Report):
if ('summary' == report_type) or ('audit' == report_type):
if 'txt' == format:
- file.write("Report : Vulnerability %s - Summary\n" % v.name)
+ file.write("Report : Vulnerability %s - Summary\n" % vulnerability.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(" Priority: %s\n" % v.get_priority_text)
- file.write(" Comments: %s\n" % v.comments)
+ file.write("Vulnerability: %s\n" % vulnerability.name)
+ file.write(" Status: %s\n" % vulnerability.get_status_text)
+ file.write(" Outcome: %s\n" % vulnerability.get_outcome_text)
+ file.write(" Priority: %s\n" % vulnerability.get_priority_text)
+ file.write(" Comments: %s\n" % vulnerability.comments)
file.write("\n")
- file.write("Affected Products:\n")
+ file.write("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):
+ for i,product in enumerate(Product.objects.all().order_by('order')):
+ product_header = False
+ for investigation in Investigation.objects.filter(vulnerability=vulnerability,product=product):
+ found_p = True
found_i = True
+ if not product_header:
+ file.write("%2d) Product: %s\n" % (i+1,investigation.product.long_name))
+ product_header = 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)
@@ -457,24 +473,13 @@ class VulnerabilityReport(Report):
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(" No products found\n")
file.write("\n")
file.write("Comments:\n")
found_c = False
- for i,vc in enumerate(v.vulnerability_comments.all()):
+ for i,vc in enumerate(vulnerability.vulnerability_comments.all()):
found_c = True
file.write(" %2d) %s (%s): %s\n" % (i,vc.date,vc.author,vc.comment))
if not found_c:
@@ -483,7 +488,7 @@ class VulnerabilityReport(Report):
if 'audit' == report_type:
file.write("\n")
file.write("Audit Trail:\n")
- for i,vh in enumerate(v.vulnerability_history.all()):
+ for i,vh in enumerate(vulnerability.vulnerability_history.all()):
file.write(" %2d) %s (%s): %s\n" % (i,vh.date,vh.author,vh.comment))
file.write("\n")
@@ -529,7 +534,7 @@ class InvestigationReport(Report):
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)
+ 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:
@@ -593,7 +598,14 @@ class DefectReport(Report):
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> \
+ <input type="radio" name="format" value="csv"> CSV \
+ (Separator: \
+ <select name="csv_separator"> \
+ <option value="semi">Semi-colon</option> \
+ <option value="comma">Comma</option> \
+ <option value="tab">Tab</option> \
+ </select>) \
+ <br> \
'
return context
@@ -608,29 +620,36 @@ class DefectReport(Report):
title = request_POST.get('title', '')
report_type = request_POST.get('report_type', '')
record_list = request_POST.get('record_list', '')
+ csv_separator = request_POST.get('csv_separator', 'semi')
- report_name = '%s/defect_%s_%s.%s' % (SRT_REPORT_DIR,report_type,datetime.today().strftime('%Y%m%d%H%M'),format)
+ 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"
+ tab = ';'
+ if csv_separator == 'comma': tab = ','
+ if csv_separator == 'tab': tab = '\t'
else:
tab = ","
if ('summary' == report_type):
if 'csv' == format:
- file.write("Name\tSummary\tPriority\tStatus\tResolution\tReleased Version\tURL\tInvestigations\tProduct\n")
+ file.write("Name\tSummary\tPriority\tStatus\tResolution\tSRT Priority\tSRT Status\tSRT Outcome\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")
+ file.write("Name,Summary,Priority,Status,Resolution,SRT Priority,SRT Status,SRT Outcome,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_defect_priority_text,tab))
+ file.write("%s%s" % (defect.get_defect_status_text,tab))
+ file.write("%s%s" % (defect.get_defect_resolution_text,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.get_outcome_text,tab))
file.write("%s%s" % (defect.release_version,tab))
file.write("%s%s" % (defect.publish,tab))
file.write("%s%s" % (defect.url,tab))
@@ -832,7 +851,7 @@ class CvesReport(Report):
defect.name if defect else '<no_defect>',
defect.get_priority_text if defect else '',
defect.get_status_text if defect else '',
- defect.get_resolution_text if defect else '',
+ defect.get_defect_resolution_text if defect else '',
])
else:
writer.writerow([
@@ -848,7 +867,7 @@ class CvesReport(Report):
defect.name if defect else '<no_defect>',
defect.get_priority_text if defect else '',
defect.get_status_text if defect else '',
- defect.get_resolution_text if defect else '',
+ defect.get_defect_resolution_text if defect else '',
])
def exec_report(self, *args, **kwargs):
@@ -865,7 +884,7 @@ class CvesReport(Report):
record_list = request_POST.get('record_list', '')
name_filter = request_POST.get('name_filter', '').upper()
- report_name = '%s/cves_%s_%s.%s' % (SRT_REPORT_DIR,report_type,datetime.today().strftime('%Y%m%d%H%M'),format)
+ report_name = '%s/cves_%s_%s.%s' % (SRT_REPORT_DIR,report_type,datetime.today().strftime('%Y%m%d_%H%M'),format)
if 'csv' == format:
delimiter = '\t'
else:
@@ -947,7 +966,7 @@ class SelectCvesReport(Report):
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)
+ 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:
@@ -1105,7 +1124,7 @@ class VulnerabilitiesReport(Report):
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)
+ report_name = '%s/vulnerabilities_%s_%s.%s' % (SRT_REPORT_DIR,report_type,datetime.today().strftime('%Y%m%d_%H%M'),format)
if 'csv' == format:
delimiter = '\t'
else:
@@ -1153,11 +1172,18 @@ class InvestigationsReport(Report):
<input type="radio" name="columns" value="all" > All<br> \
'
context['report_format_list'] = '\
- <input type="radio" name="format" value="txt" checked> Text (comma delimited)<br> \
- <input type="radio" name="format" value="csv"> CSV (tab delimited)<br> \
+ <input type="radio" name="format" value="txt" checked> Text<br> \
+ <input type="radio" name="format" value="csv"> CSV \
+ (Separator: \
+ <select name="csv_separator"> \
+ <option value="semi">Semi-colon</option> \
+ <option value="comma">Comma</option> \
+ <option value="tab">Tab</option> \
+ </select>) \
+ <br> \
'
context['report_custom_list'] = '\
- Product defect prefix filter = <input type="text" placeholder="e.g. LIN9" name="name_filter" size="40"> (method to filter by product)<br>\
+ Product defect prefix filter = <input type="text" placeholder="" name="name_filter" size="40"> (method to filter by product)<br>\
'
return context
@@ -1175,6 +1201,7 @@ class InvestigationsReport(Report):
'Comments Private',
'Vulnerability',
'Product',
+ 'Updated',
])
else:
writer.writerow([
@@ -1188,6 +1215,7 @@ class InvestigationsReport(Report):
'Comments Private',
'Vulnerability',
'Product',
+ 'Updated',
])
else:
investigation_defects = ''
@@ -1212,6 +1240,7 @@ class InvestigationsReport(Report):
investigation.comments_private,
investigation.vulnerability.get_long_name,
investigation.product.long_name,
+ investigation.srt_updated.strftime('%m-%d-%Y'),
])
else:
writer.writerow([
@@ -1225,6 +1254,7 @@ class InvestigationsReport(Report):
investigation.comments_private,
investigation.vulnerability.get_long_name,
investigation.product.long_name,
+ investigation.srt_updated.strftime('%m-%d-%Y'),
])
def exec_report(self, *args, **kwargs):
@@ -1240,12 +1270,16 @@ class InvestigationsReport(Report):
report_type = request_POST.get('report_type', '')
record_list = request_POST.get('record_list', '')
name_filter = request_POST.get('name_filter', '').upper()
+ csv_separator = request_POST.get('csv_separator', 'semi')
+
+ report_name = '%s/investigations_%s_%s.%s' % (SRT_REPORT_DIR,report_type,datetime.today().strftime('%Y%m%d_%H%M'),format)
- report_name = '%s/investigations_%s_%s.%s' % (SRT_REPORT_DIR,report_type,datetime.today().strftime('%Y%m%d%H%M'),format)
if 'csv' == format:
- delimiter = '\t'
+ delimiter = ';'
+ if csv_separator == 'comma': delimiter = ','
+ if csv_separator == 'tab': delimiter = '\t'
else:
- delimiter = ','
+ delimiter = ","
with open(report_name, 'w', newline='') as csvfile:
writer = csv.writer(csvfile, delimiter=delimiter,
@@ -1293,11 +1327,19 @@ class DefectsReport(Report):
'
context['report_format_list'] = '\
<input type="radio" name="format" value="txt" checked> Text (comma delimited)<br> \
- <input type="radio" name="format" value="csv"> CSV (tab delimited)<br> \
+ <input type="radio" name="format" value="csv"> CSV \
+ (Separator: \
+ <select name="csv_separator"> \
+ <option value="semi">Semi-colon</option> \
+ <option value="comma">Comma</option> \
+ <option value="tab">Tab</option> \
+ </select>) \
+ <br> \
'
+
context['report_custom_list'] = '\
- Defect name filter = <input type="text" placeholder="e.g. LIN9" name="name_filter" size="40"> <br>\
- '
+ Defect name filter = <input type="text" placeholder="e.g. %s" name="name_filter" size="40"> <br>\
+ ' % SrtSetting.get_setting('SRTOOL_DEFECT_SAMPLENAME',"DEFECT-XYZ")
return context
def print_row(self,writer,is_header,is_full,defect):
@@ -1309,6 +1351,9 @@ class DefectsReport(Report):
'Priority',
'Status',
'Resolution',
+ 'SRT Priority',
+ 'SRT Status',
+ 'SRT Outcome',
'Release Version',
'Publish',
'Investigations',
@@ -1321,6 +1366,9 @@ class DefectsReport(Report):
'Priority',
'Status',
'Resolution',
+ 'SRT Priority',
+ 'SRT Status',
+ 'SRT Outcome',
'Release Version',
'Publish',
'URL',
@@ -1337,9 +1385,12 @@ class DefectsReport(Report):
writer.writerow([
defect.name,
defect.summary,
+ defect.get_defect_priority_text,
+ defect.get_defect_status_text,
+ defect.get_defect_resolution_text,
defect.get_priority_text,
defect.get_status_text,
- defect.get_resolution_text,
+ defect.get_outcome_text,
defect.release_version,
defect.publish,
defect_investigations,
@@ -1349,9 +1400,12 @@ class DefectsReport(Report):
writer.writerow([
defect.name,
defect.summary,
+ defect.get_defect_priority_text,
+ defect.get_defect_status_text,
+ defect.get_defect_resolution_text,
defect.get_priority_text,
defect.get_status_text,
- defect.get_resolution_text,
+ defect.get_outcome_text,
defect.release_version,
defect.publish,
defect.url,
@@ -1372,10 +1426,13 @@ class DefectsReport(Report):
report_type = request_POST.get('report_type', '')
record_list = request_POST.get('record_list', '')
name_filter = request_POST.get('name_filter', '').upper()
+ csv_separator = request_POST.get('csv_separator', 'semi')
- report_name = '%s/defects_%s_%s.%s' % (SRT_REPORT_DIR,report_type,datetime.today().strftime('%Y%m%d%H%M'),format)
+ report_name = '%s/defects_%s_%s.%s' % (SRT_REPORT_DIR,report_type,datetime.today().strftime('%Y%m%d_%H%M'),format)
if 'csv' == format:
- delimiter = '\t'
+ delimiter = ';'
+ if csv_separator == 'comma': delimiter = ','
+ if csv_separator == 'tab': delimiter = '\t'
else:
delimiter = ','
with open(report_name, 'w', newline='') as csvfile:
@@ -1437,7 +1494,7 @@ class ProductsReport(Report):
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)
+ 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:
@@ -1504,7 +1561,6 @@ class PublishCveReport(Report):
_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', '')
@@ -1513,11 +1569,9 @@ class PublishCveReport(Report):
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)
+ 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:
@@ -1531,9 +1585,7 @@ class PublishCveReport(Report):
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:
@@ -1553,9 +1605,8 @@ class PublishCveReport(Report):
file.write("%s" % (cve.description))
file.write("\n")
except Exception as e:
- _log("FOOX:%s" % e)
+ _log("EXCEPTION:%s" % e)
- _log("FOO9:%s" % (report_name))
return report_name,os.path.basename(report_name)
class PublishPendingCveReport(Report):
@@ -1586,7 +1637,6 @@ class PublishPendingCveReport(Report):
_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', '')
@@ -1595,11 +1645,9 @@ class PublishPendingCveReport(Report):
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)
+ 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:
@@ -1613,11 +1661,9 @@ class PublishPendingCveReport(Report):
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))
@@ -1635,9 +1681,188 @@ class PublishPendingCveReport(Report):
file.write("%s" % (cve.description))
file.write("\n")
except Exception as e:
- _log("FOOX:%s" % e)
+ _log("EXCEPTION:%s" % e)
+
+ return report_name,os.path.basename(report_name)
+
+class PublishListReport(Report):
+ """Report for the Publish Cve Page"""
+
+ def __init__(self, parent_page, *args, **kwargs):
+ _log_args("REPORT_PUBLISHLIST_INIT(%s)" % parent_page, *args, **kwargs)
+ super(PublishListReport, self).__init__(parent_page, *args, **kwargs)
+
+ def get_context_data(self, *args, **kwargs):
+ _log_args("REPORT_PUBLISHLIST_CONTEXT", *args, **kwargs)
+ context = super(PublishListReport, self).get_context_data(*args, **kwargs)
+ context['report_type_list'] = '\
+ <option value="preview">Preview CVE Publish List</option> \
+ <option value="report">Publish Report </option> \
+ '
+ 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 \
+ (Separator: \
+ <select name="csv_separator"> \
+ <option value="semi">Semi-colon</option> \
+ <option value="comma">Comma</option> \
+ <option value="tab">Tab</option> \
+ </select>) \
+ <br> \
+ '
+ return context
+
+ def exec_report(self, *args, **kwargs):
+ _log_args("REPORT_PUBLISHLIST_EXEC", *args, **kwargs)
+ super(PublishListReport, self).exec_report(*args, **kwargs)
+
+ request_POST = self.request.POST
+ format = request_POST.get('format', '')
+ report_type = request_POST.get('report_type', '')
+ csv_separator = request_POST.get('csv_separator', 'semi')
+
+ report_name = '%s/publish_list_%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 = ';'
+ if csv_separator == 'comma': tab = ','
+ if csv_separator == 'tab': tab = '\t'
+ else:
+ tab = ","
+
+ if ('preview' == report_type):
+ if 'csv' == format:
+ file.write("State\tCve_Name\tCve_Published\tCve_Modified\tCve_Status\tCve_Acknowledge\tReason\tCVE_Description\n".replace('\t',tab))
+ if 'txt' == format:
+ file.write("Report : CVEs Table\n")
+ file.write("\n")
+ file.write('%-7s %-18s %11s %11s %16s %11s %-35s %s\n' % ('State','Cve_Name','Published','Modified','Cve_Status','Acknowledge','CVE_Description','Reason'))
+
+ for publishset in PublishSet.objects.all():
+ if 'csv' == format:
+ file.write("%s%s" % (publishset.state_text,tab))
+ file.write("%s%s" % (publishset.cve.name,tab))
+ file.write("%s%s" % (publishset.cve.publishedDate,tab))
+ file.write("%s%s" % (publishset.cve.lastModifiedDate,tab))
+ file.write("%s%s" % (publishset.cve.get_status_text,tab))
+ file.write("%s%s" % (publishset.cve.acknowledge_date,tab))
+ file.write("%s%s" % (publishset.reason,tab))
+ file.write("%s%s" % (publishset.cve.description,tab))
+ file.write("\n")
+ if 'txt' == format:
+ try:
+ acknowledge_date = publishset.cve.acknowledge_date.strftime('%m/%d/%Y')
+ except:
+ acknowledge_date = ''
+ if publishset.cve.description:
+ description = publishset.cve.description[:30] + '...'
+ else:
+ description = ''
+ file.write("%-7s," % publishset.state_text)
+ file.write("%-18s," % publishset.cve.name)
+ file.write("%11s," % publishset.cve.publishedDate)
+ file.write("%11s," % publishset.cve.lastModifiedDate)
+ file.write("%16s," % publishset.cve.get_status_text)
+ file.write("%11s," % acknowledge_date)
+ file.write("%-35s," % description)
+ file.write("%s," % publishset.reason)
+ file.write("\n")
+
+ if ('report' == report_type):
+ product_list = Product.objects.all()
+
+ def get_product_status_matrix(product_list,cve):
+ # Preset the default product status labels
+ status_table = {}
+ product_top_order = 99
+ product_top_defect = []
+ for product in product_list:
+ status_table[product.key] = publishset.cve.get_status_text
+ # Set the specific status for the child investigations
+ for cv in cve.cve_to_vulnerability.all():
+ #status_text = cv.vulnerability.get_status_text
+ for investigation in cv.vulnerability.vulnerability_investigation.all():
+ product_key = investigation.product.key
+ release_version_list = []
+ for id in investigation.investigation_to_defect.all():
+ # Find defect(s) for higest ordered product
+ if product_top_order > investigation.product.order:
+ product_top_order = investigation.product.order
+ product_top_defect = []
+ if product_top_order == investigation.product.order:
+ product_top_defect.append(id.defect.name)
+ # Gather the status or release version
+ if id.defect.release_version:
+ release_version_list.append(id.defect.release_version)
+ release_version = '/'.join(release_version_list)
+ if release_version:
+ status_table[product_key] = release_version
+ elif investigation.status in (SRTool.NOT_VULNERABLE,SRTool.VULNERABLE):
+ status_table[product_key] = investigation.get_status_text
+ else:
+ status_table[product_key] = ''
+ return status_table
+
+ if 'csv' == format:
+ file.write("State\tCve_Name\tCve_Published\tCve_Modified\tCve_Status\tCve_Acknowledge\tCVE_Description")
+ for product in product_list:
+ file.write("\t%s" % product.long_name)
+ file.write("\n")
+
+ if 'txt' == format:
+ file.write("Report : CVEs Table\n")
+ file.write("\n")
+ file.write('%-7s,%-18s,%11s,%11s,%16s,%11s,%-35s,' % ('State','Cve_Name','Published','Modified','Cve_Status','Acknowledge','CVE_Description'))
+ for product in product_list:
+ min_len = max(16,len(product.long_name)+1)
+ str_format = "%s%ds," % ('%',min_len)
+ file.write(str_format % product.long_name)
+ file.write("\n")
+ for publishset in PublishSet.objects.all():
+ if 'csv' == format:
+ # Print common status
+ file.write("%s%s" % (publishset.state_text,tab))
+ file.write("%s%s" % (publishset.cve.name,tab))
+ file.write("%s%s" % (publishset.cve.publishedDate,tab))
+ file.write("%s%s" % (publishset.cve.lastModifiedDate,tab))
+ file.write("%s%s" % (publishset.cve.get_status_text,tab))
+ file.write("%s%s" % (publishset.cve.acknowledge_date,tab))
+ file.write("%s%s" % (publishset.reason,tab))
+ file.write("%s%s" % (publishset.cve.description,tab))
+ # Compute the product columns
+ status_table = get_product_status_matrix(product_list,publishset.cve)
+ # Print the product columns
+ for product in Product.objects.all():
+ file.write("%s%s" % (status_table[product.key],tab))
+ file.write("\n")
+ if 'txt' == format:
+ try:
+ acknowledge_date = publishset.cve.acknowledge_date.strftime('%m/%d/%Y')
+ except:
+ acknowledge_date = ''
+ if publishset.cve.description:
+ description = publishset.cve.description[:30] + '...'
+ else:
+ description = ''
+ # Print common status
+ file.write("%-7s," % publishset.state_text)
+ file.write("%-18s," % publishset.cve.name)
+ file.write("%11s," % publishset.cve.publishedDate)
+ file.write("%11s," % publishset.cve.lastModifiedDate)
+ file.write("%16s," % publishset.cve.get_status_text)
+ file.write("%11s," % acknowledge_date)
+ file.write("%-35s," % description)
+ # Compute the product columns
+ status_table = get_product_status_matrix(product_list,publishset.cve)
+ # Print the product columns
+ for product in Product.objects.all():
+ min_len = max(16,len(product.long_name)+1)
+ str_format = "%s%ds," % ('%',min_len)
+ file.write(str_format % status_table[product.key])
+ file.write("\n")
- _log("FOO9:%s" % (report_name))
return report_name,os.path.basename(report_name)
class PackageFiltersReport(Report):
@@ -1659,7 +1884,7 @@ class PackageFiltersReport(Report):
'
context['report_columnrange_list'] = ''
context['report_format_list'] = '\
- <input type="radio" name="format" value="csv" checked> CSV<br> \
+ <input type="radio" name="format" value="csv" checked> r<br> \
'
return context
@@ -1731,7 +1956,7 @@ class CpesSrtoolReport(Report):
report_type = request_POST.get('report_type', '')
record_list = request_POST.get('record_list', '')
- report_name = '%s/cpes_srtool_%s_%s.%s' % (SRT_REPORT_DIR,report_type,datetime.today().strftime('%Y%m%d%H%M'),format)
+ report_name = '%s/cpes_srtool_%s_%s.%s' % (SRT_REPORT_DIR,report_type,datetime.today().strftime('%Y%m%d_%H%M'),format)
reportfile = open(report_name, 'w', newline='')
if 'csv' == format:
@@ -1776,6 +2001,111 @@ class CpesSrtoolReport(Report):
return report_name,os.path.basename(report_name)
+###############################################################################
+#
+# History reports
+#
+
+class HistoryDefectReport(Report):
+ """Report for the History Defect Page"""
+
+ def __init__(self, parent_page, *args, **kwargs):
+ _log_args("WR_HISTORY_DEFECT_INIT(%s)" % parent_page, *args, **kwargs)
+ super(HistoryDefectReport, self).__init__(parent_page, *args, **kwargs)
+
+ def get_context_data(self, *args, **kwargs):
+ _log_args("WR_HISTORY_DEFECT_CONTEXT", *args, **kwargs)
+ context = super(HistoryDefectReport, self).get_context_data(*args, **kwargs)
+
+ context['report_type_list'] = '\
+ <option value="history">Defect History</option> \
+ '
+
+ 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 \
+ (Separator: \
+ <select name="csv_separator"> \
+ <option value="semi">Semi-colon</option> \
+ <option value="comma">Comma</option> \
+ <option value="tab">Tab</option> \
+ </select>) \
+ <br> \
+ '
+
+ context['report_recordrange_list'] = '\
+ <input type="radio" name="records" value="selected" checked> Selected<br> \
+ <input type="radio" name="records" value="all"> All<br> \
+ '
+
+ # Add a date range
+ date_start = datetime.strptime('2019-2-15', '%Y-%m-%d')
+ date_stop = datetime.strptime('2019-3-15', '%Y-%m-%d')
+ context['report_date_list'] = '\
+ Start: <input type="text" name="date_start" value="%s"><br> \
+ Stop: <input type="text" name="date_stop" value="%s"> \
+ ' % (date_start.strftime('%m/%d/%Y'),date_stop.strftime('%m/%d/%Y'))
+
+ # Done!
+ return context
+
+ def exec_report(self, *args, **kwargs):
+ _log_args("WR_HISTORY_DEFECT_EXEC", *args, **kwargs)
+
+ request_POST = self.request.POST
+
+ records = request_POST.get('records', '')
+ format = request_POST.get('format', '')
+# title = request_POST.get('title', '')
+ report_type = request_POST.get('report_type', '')
+ record_list = request_POST.get('record_list', '')
+ csv_separator = request_POST.get('csv_separator', 'semi')
+
+ # Dates (make as no timezone)
+ msg = ''
+ try:
+ msg = 'Start:%s' % request_POST.get('date_start', '')
+ date_start = datetime.strptime(request_POST.get('date_start', ''), '%m/%d/%Y')
+ msg = 'Stop:%s' % request_POST.get('date_stop', '')
+ date_stop = datetime.strptime(request_POST.get('date_stop', ''), '%m/%d/%Y')
+ if date_stop < date_start:
+ return 'Error:stop date is before start date',''
+ except Exception as e:
+ return 'Error:bad format for dates (must be mm/dd/yyyy) (%s)(%s)' % (msg,e),''
+
+ report_name = '%s/defect_history_%s_%s.%s' % (SRT_REPORT_DIR,report_type,datetime.today().strftime('%Y%m%d_%H%M'),format)
+ with open(report_name, 'w') as file:
+
+ if 'csv' == format:
+ separator = ";"
+ if csv_separator == 'comma': separator = ","
+ if csv_separator == 'tab': separator = "\t"
+ writer = csv.writer(csvfile, delimiter=separator,
+ quotechar='"', quoting=csv.QUOTE_MINIMAL)
+ else:
+ separator = ","
+
+ if ('history' == report_type):
+ if 'csv' == format:
+ writer.writerow(['Index','Defect','Date','Author','Comment'])
+ if 'txt' == format:
+ file.write("Report : Defect History\n")
+ file.write("\n")
+ text_format='%02d) %-14s %-10s %-10s %s\n'
+ file.write(text_format % (0,'Defect','Date','Author','Comment'))
+
+ for i,dh in enumerate(DefectHistory.objects.filter(date__gte=date_start,date__lte=date_stop).order_by('defect__name')):
+ if 'csv' == format:
+ writer.writerow([i+1,dh.defect.name,dh.date.strftime('%Y-%m-%d'),dh.author,dh.comment])
+ if 'txt' == format:
+ file.write(text_format % (i+1,dh.defect.name,dh.date.strftime('%Y-%m-%d'),dh.author,dh.comment))
+
+ return report_name,os.path.basename(report_name)
+
+###############################################################################
+#
+
class DefaultReport(Report):
"""Report for the Default Page"""
@@ -1843,11 +2173,19 @@ class ReportManager():
elif 'update-published' == parent_page:
return PublishPendingCveReport(parent_page, *args, **kwargs)
+ elif 'publish' == parent_page:
+ return PublishListReport(parent_page, *args, **kwargs)
+ elif 'publish-list' == parent_page:
+ return PublishListReport(parent_page, *args, **kwargs)
+
elif 'package-filters' == parent_page:
return PackageFiltersReport(parent_page, *args, **kwargs)
elif 'cpes_srtool' == parent_page:
return CpesSrtoolReport(parent_page, *args, **kwargs)
+ elif 'history_defect' == parent_page:
+ return HistoryDefectReport(parent_page, *args, **kwargs)
+
else:
return DefaultReport(parent_page, *args, **kwargs)
diff --git a/lib/srtgui/tables.py b/lib/srtgui/tables.py
index 44d02a11..e80ee71e 100644
--- a/lib/srtgui/tables.py
+++ b/lib/srtgui/tables.py
@@ -19,12 +19,17 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+import re
+import json
+
from srtgui.widgets import ToasterTable
from orm.models import Cve, Vulnerability, Investigation, CweTable, Product
from orm.models import Package
-from orm.models import CpeTable, CpeFilter, Defect, DataSource
+from orm.models import CpeTable, CpeFilter, Defect, DataSource, SrtSetting
from orm.models import PublishPending
from orm.models import Notify, NotifyCategories
+from orm.models import CveHistory, VulnerabilityHistory, InvestigationHistory, DefectHistory
+from orm.models import PublishSet
from users.models import UserSafe
from django.db.models import Q
@@ -32,8 +37,6 @@ from django.db.models import Q
from srtgui.tablefilter import TableFilter
from srtgui.tablefilter import TableFilterActionToggle
-import re
-
# quick development/debugging support
from srtgui.api import _log
@@ -159,24 +162,6 @@ class CvesTable(ToasterTable):
hidden=True,
)
- self.add_column(title="Data Type",
- field_name="cve_data_type",
- hideable=True,
- hidden=True,
- )
-
- self.add_column(title="Data Format",
- field_name="cve_data_format",
- hideable=True,
- hidden=True,
- )
-
- self.add_column(title="Data Version",
- field_name="cve_data_version",
- hideable=True,
- hidden=True,
- )
-
self.add_column(title="Description",
field_name="description",
hideable=False,
@@ -205,23 +190,23 @@ class CvesTable(ToasterTable):
static_data_template=priority_v2_template,
)
- self.add_column(title="Packages",
+ self.add_column(title="Affected Components",
field_name="packages",
- hideable=True,
- hidden=True,
+ hideable=False,
+ hidden=False,
)
self.add_column(title="Published",
help_text="Initial publish date of the CVE",
hideable=False,
- #orderable=True,
+ orderable=True,
field_name="publishedDate",
)
self.add_column(title="Modified",
help_text="Last modification date of the CVE",
hideable=True,
- #orderable=True,
+ orderable=True,
field_name="lastModifiedDate",
)
@@ -237,6 +222,12 @@ class CvesTable(ToasterTable):
hideable=True,
)
+ self.add_column(title="Tags",
+ field_name="tags",
+ hideable=True,
+ hidden=True,
+ )
+
self.add_column(title="Publish Request",
help_text="SRT Publish Request State",
hideable=True,
@@ -289,7 +280,7 @@ class CvesTable(ToasterTable):
orderable=True,
field_name="srt_updated",
static_data_name="srt_updated",
- static_data_template='{{data.srt_updated | date:"m/d/y H:i"}}'
+ static_data_template='{{data.srt_updated | date:"Y/m/d"}}'
)
source_count_template = '''
@@ -315,6 +306,9 @@ class SelectCveTable(ToasterTable):
context = super(SelectCveTable, self).get_context_data(**kwargs)
context['products'] = Product.objects.all()
context['components'] = Defect.Components
+ context['doesnotimpact_text'] = SrtSetting.get_setting('SRTOOL_DEFECT_DOESNOTIMPACT',"It doesn't impact the product")
+ context['components'] = Defect.Components
+
return context
def apply_row_customization(self, row):
@@ -562,39 +556,76 @@ class DefectsTable(ToasterTable):
def setup_filters(self, *args, **kwargs):
- # Priority filter
- is_priority = TableFilter(name="is_priority",
+ # Defect Priority filter
+ is_defect_priority = TableFilter(name="is_defect_priority",
title="Filter defects by 'Priority'")
- for priority in range(len(Defect.Priority)):
- is_priority.add_action(TableFilterActionToggle(
- Defect.Priority[priority][1].lower().replace(' ','_'),
- Defect.Priority[priority][1],
- Q(priority=Defect.Priority[priority][0]))
+ for priority in range(len(Defect.DEFECT_PRIORITY)):
+ if Defect.DEFECT_PRIORITY_ERROR == Defect.DEFECT_PRIORITY[priority][0]:
+ continue
+ is_defect_priority.add_action(TableFilterActionToggle(
+ Defect.DEFECT_PRIORITY[priority][1].lower().replace(' ','_'),
+ Defect.DEFECT_PRIORITY[priority][1],
+ Q(priority=Defect.DEFECT_PRIORITY[priority][0]))
)
- self.add_filter(is_priority)
-
- # Status filter
- is_status = TableFilter(name="is_status",
- title="Filter defects by 'Status'")
- for status in range(len(Defect.Status)):
- is_status.add_action(TableFilterActionToggle(
- Defect.Status[status][1].lower().replace(' ','_'),
- Defect.Status[status][1],
- Q(status=Defect.Status[status][0]))
+ self.add_filter(is_defect_priority)
+
+ # Defect Status filter
+ is_defect_status = TableFilter(name="is_defect_status",
+ title="Filter defects by defect 'Status'")
+ for status in range(len(Defect.DEFECT_STATUS)):
+ is_defect_status.add_action(TableFilterActionToggle(
+ Defect.DEFECT_STATUS[status][1].lower().replace(' ','_'),
+ Defect.DEFECT_STATUS[status][1],
+ Q(status=Defect.DEFECT_STATUS[status][0]))
)
- self.add_filter(is_status)
+ self.add_filter(is_defect_status)
# Resolution filter
is_resolution = TableFilter(name="is_resolution",
title="Filter defects by 'Resolution'")
- for resolution in range(len(Defect.Resolution)):
+ for resolution in range(len(Defect.DEFECT_RESOLUTION)):
is_resolution.add_action(TableFilterActionToggle(
- Defect.Resolution[resolution][1].lower().replace(' ','_'),
- Defect.Resolution[resolution][1],
- Q(resolution=Defect.Resolution[resolution][0]))
+ Defect.DEFECT_RESOLUTION[resolution][1].lower().replace(' ','_'),
+ Defect.DEFECT_RESOLUTION[resolution][1],
+ Q(resolution=Defect.DEFECT_RESOLUTION[resolution][0]))
)
self.add_filter(is_resolution)
+ # SRT Priority filter
+ is_srt_priority = TableFilter(name="is_srt_priority",
+ title="Filter defects by 'Priority'")
+ for priority in range(len(Defect.SRT_PRIORITY)):
+ if Defect.PRIORITY_ERROR == Defect.SRT_PRIORITY[priority][0]:
+ continue
+ is_srt_priority.add_action(TableFilterActionToggle(
+ Defect.SRT_PRIORITY[priority][1].lower().replace(' ','_'),
+ Defect.SRT_PRIORITY[priority][1],
+ Q(priority=Defect.SRT_PRIORITY[priority][0]))
+ )
+ self.add_filter(is_srt_priority)
+
+ # SRTool Status filter
+ is_srt_status = TableFilter(name="is_srt_status",
+ title="Filter defects by 'Status'")
+ for status in range(len(Defect.SRT_STATUS)):
+ is_srt_status.add_action(TableFilterActionToggle(
+ Defect.SRT_STATUS[status][1].lower().replace(' ','_'),
+ Defect.SRT_STATUS[status][1],
+ Q(status=Defect.SRT_STATUS[status][0]))
+ )
+ self.add_filter(is_srt_status)
+
+ # SRTool Outcome filter
+ is_srt_outcome = TableFilter(name="is_srt_outcome",
+ title="Filter defects by 'Outcome'")
+ for status in range(len(Defect.SRT_OUTCOME)):
+ is_srt_outcome.add_action(TableFilterActionToggle(
+ Defect.SRT_OUTCOME[status][1].lower().replace(' ','_'),
+ Defect.SRT_OUTCOME[status][1],
+ Q(status=Defect.SRT_OUTCOME[status][0]))
+ )
+ self.add_filter(is_srt_outcome)
+
# Product filter
#(name="Wind River Linux",version="LTS-17")
is_product = TableFilter(name="is_product",
@@ -631,31 +662,58 @@ class DefectsTable(ToasterTable):
field_name="summary",
)
- self.add_column(title="Priority",
+ self.add_column(title="Defect Priority",
hideable=False,
- field_name="priority",
orderable=True,
- filter_name="is_priority",
- static_data_name="priority",
- static_data_template='{{data.get_priority_text}}',
+ filter_name="is_defect_priority",
+ static_data_name="defect_priority",
+ static_data_template='{{data.get_defect_priority_text}}',
)
- self.add_column(title="Status",
+ self.add_column(title="Defect Status",
hideable=False,
- field_name="status",
orderable=True,
- filter_name="is_status",
- static_data_name="status",
- static_data_template='{{data.get_status_text}}',
+ filter_name="is_defect_status",
+ static_data_name="defect_status",
+ static_data_template='{{data.get_defect_status_text}}',
)
- self.add_column(title="Resolution",
+ self.add_column(title="Defect 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_name="defect_resolution",
+ static_data_template='{{data.get_defect_resolution_text}}',
+ )
+
+ self.add_column(title="SRT Priority",
+ hideable=False,
+ orderable=True,
+ filter_name="is_srt_priority",
+ static_data_name="srt_priority",
+ static_data_template='{{data.get_priority_text}}',
+ )
+
+ self.add_column(title="SRT Status",
+ hideable=False,
+ orderable=True,
+ filter_name="is_srt_status",
+ static_data_name="srt_status",
+ static_data_template='{{data.get_status_text}}',
+ )
+
+ self.add_column(title="Duplicate Of",
+ hideable=True,
+ hidden=True,
+ field_name="duplicate_of",
+ )
+
+ self.add_column(title="Outcome",
+ hideable=True,
+ hidden=True,
+ filter_name="is_srt_outcome",
+ static_data_name="srt_outcome",
+ static_data_template='{{data.get_outcome_text}}',
)
self.add_column(title="Release Version",
@@ -712,6 +770,23 @@ class DefectsTable(ToasterTable):
static_data_template=product_link_template,
)
+ self.add_column(title="Defect Created",
+ hideable=True,
+ hidden=True,
+ orderable=True,
+ field_name="date_created",
+ static_data_name="date_created",
+ static_data_template='{{date_created}}'
+ )
+ self.add_column(title="Defect Update",
+ hideable=True,
+ hidden=True,
+ orderable=True,
+ field_name="date_updated",
+ static_data_name="date_updated",
+ static_data_template='{{data.date_updated}}'
+ )
+
self.add_column(title="SRT Update",
hideable=True,
hidden=True,
@@ -726,7 +801,7 @@ class CwesTable(ToasterTable):
def __init__(self, *args, **kwargs):
super(CwesTable, self).__init__(*args, **kwargs)
- self.default_orderby = "name_sort"
+ self.default_orderby = "name"
def get_context_data(self, **kwargs):
context = super(CwesTable, self).get_context_data(**kwargs)
@@ -1162,13 +1237,17 @@ class ProductsTable(ToasterTable):
)
self.add_column(title="Defect Tags",
- field_name="defect_tags",
- hideable=False,
+ hideable=True,
+ hidden=True,
+ static_data_name="defect_tags",
+ static_data_template='{{data.get_defect_str}}',
)
self.add_column(title="Product Tags",
- field_name="product_tags",
- hideable=False,
+ hideable=True,
+ hidden=True,
+ static_data_name="product_tags",
+ static_data_template='{{data.get_product_str}}',
)
@@ -1262,6 +1341,10 @@ class VulnerabilitiesTable(ToasterTable):
# Priority filter
is_priority = TableFilter(name="is_priority",
title="Filter Vulnerabilities by 'Priority'")
+ exec_is_undefined = TableFilterActionToggle(
+ "undefined",
+ "Undefined",
+ Q(priority=Vulnerability.UNDEFINED))
exec_is_low = TableFilterActionToggle(
"low",
"Low",
@@ -1274,9 +1357,15 @@ class VulnerabilitiesTable(ToasterTable):
"high",
"High",
Q(priority=Vulnerability.HIGH))
+ exec_is_critical = TableFilterActionToggle(
+ "critical",
+ "Critical",
+ Q(priority=Vulnerability.CRITICAL))
+ is_priority.add_action(exec_is_undefined)
is_priority.add_action(exec_is_low)
is_priority.add_action(exec_is_medium)
is_priority.add_action(exec_is_high)
+ is_priority.add_action(exec_is_critical)
self.add_filter(is_priority)
def setup_queryset(self, *args, **kwargs):
@@ -1356,6 +1445,12 @@ class VulnerabilitiesTable(ToasterTable):
hideable=True,
)
+ self.add_column(title="Tags",
+ field_name="tags",
+ hideable=True,
+ hidden=True,
+ )
+
investigate_link_template = '''
{% for investigation in data.vulnerability_investigation.all %}
{% if not forloop.first %} {% endif %}<a href="{% url 'investigation' investigation.name %}" target="_blank">{{investigation.name}}</a>
@@ -1397,6 +1492,16 @@ class VulnerabilitiesTable(ToasterTable):
hidden=False,
)
+ self.add_column(title="SRT Update",
+ hideable=True,
+ hidden=True,
+ orderable=True,
+ field_name="srt_updated",
+ static_data_name="srt_updated",
+ static_data_template='{{data.srt_updated | date:"Y/m/d"}}'
+ )
+
+
class InvestigationsTable(ToasterTable):
"""Table of All Investigations in SRTool"""
@@ -1458,23 +1563,34 @@ class InvestigationsTable(ToasterTable):
# Priority filter
is_priority = TableFilter(name="is_priority",
title="Filter Investigations by 'Priority'")
+ exec_is_undefined = TableFilterActionToggle(
+ "undefined",
+ "Undefined",
+ Q(priority=Vulnerability.UNDEFINED))
exec_is_low = TableFilterActionToggle(
"low",
"Low",
- Q(priority=Investigation.LOW))
+ Q(priority=Vulnerability.LOW))
exec_is_medium = TableFilterActionToggle(
"medium",
"Medium",
- Q(priority=Investigation.MEDIUM))
+ Q(priority=Vulnerability.MEDIUM))
exec_is_high = TableFilterActionToggle(
"high",
"High",
- Q(priority=Investigation.HIGH))
+ Q(priority=Vulnerability.HIGH))
+ exec_is_critical = TableFilterActionToggle(
+ "critical",
+ "Critical",
+ Q(priority=Vulnerability.CRITICAL))
+ is_priority.add_action(exec_is_undefined)
is_priority.add_action(exec_is_low)
is_priority.add_action(exec_is_medium)
is_priority.add_action(exec_is_high)
+ is_priority.add_action(exec_is_critical)
self.add_filter(is_priority)
+
# Product filter
is_product = TableFilter(name="is_product",
title="Filter Investigations by 'Product'")
@@ -1572,6 +1688,12 @@ class InvestigationsTable(ToasterTable):
hideable=True,
)
+ self.add_column(title="Tags",
+ field_name="tags",
+ hideable=True,
+ hidden=True,
+ )
+
self.add_column(title="Vulnerability",
hidden=False,
orderable=False,
@@ -1587,6 +1709,16 @@ class InvestigationsTable(ToasterTable):
static_data_template="<a href=\"{% url 'product' data.product.id %}\">{{data.product.long_name}}</a>",
)
+ self.add_column(title="SRT Update",
+ hideable=True,
+ hidden=True,
+ orderable=True,
+ field_name="srt_updated",
+ static_data_name="srt_updated",
+ static_data_template='{{data.srt_updated | date:"Y/m/d"}}'
+ )
+
+
class SourcesTable(ToasterTable):
"""Table of All Data Sources in SRTool"""
@@ -2193,3 +2325,538 @@ class PackageFilterDetailTable(ToasterTable):
static_data_name="defects",
static_data_template=defect_link_template,
)
+
+class HistoryCveTable(ToasterTable):
+ """Table of History Cves Details in SRTool"""
+
+ def __init__(self, *args, **kwargs):
+ super(HistoryCveTable, self).__init__(*args, **kwargs)
+ self.default_orderby = "-date"
+
+ def get_context_data(self,**kwargs):
+ context = super(HistoryCveTable, self).get_context_data(**kwargs)
+ return context
+
+ def setup_queryset(self, *args, **kwargs):
+ self.queryset = CveHistory.objects.all()
+
+ def setup_columns(self, *args, **kwargs):
+ self.add_column(title="Cve",
+ hideable=False,
+ orderable=True,
+ static_data_name="name",
+ static_data_template='''<a href="{% url 'cve' data.cve.name %}">{{data.cve.name}}</a>''',
+ )
+ self.add_column(title="Comment",
+ field_name="comment",
+ hideable=False,
+ orderable=True,
+ )
+ self.add_column(title="Date",
+ field_name="date",
+ hideable=False,
+ orderable=True,
+ )
+ self.add_column(title="Author",
+ field_name="author",
+ hideable=False,
+ orderable=True,
+ )
+
+class HistoryVulnerabilityTable(ToasterTable):
+ """Table of History Vulnerability Details in SRTool"""
+
+ def __init__(self, *args, **kwargs):
+ super(HistoryVulnerabilityTable, self).__init__(*args, **kwargs)
+ self.default_orderby = "-date"
+
+ def get_context_data(self,**kwargs):
+ context = super(HistoryVulnerabilityTable, self).get_context_data(**kwargs)
+ return context
+
+ def setup_queryset(self, *args, **kwargs):
+ self.queryset = VulnerabilityHistory.objects.all()
+
+ def setup_columns(self, *args, **kwargs):
+ self.add_column(title="Vulnerability",
+ hideable=False,
+ orderable=True,
+ static_data_name="vulnerability",
+ static_data_template='''<a href="{% url 'vulnerability' data.vulnerability.name %}">{{data.vulnerability.name}}</a>''',
+ )
+ self.add_column(title="Comment",
+ field_name="comment",
+ hideable=False,
+ orderable=True,
+ )
+ self.add_column(title="Date",
+ field_name="date",
+ hideable=False,
+ orderable=True,
+ )
+ self.add_column(title="Author",
+ field_name="author",
+ hideable=False,
+ orderable=True,
+ )
+
+class HistoryInvestigationTable(ToasterTable):
+ """Table of History Investigation Details in SRTool"""
+
+ def __init__(self, *args, **kwargs):
+ super(HistoryInvestigationTable, self).__init__(*args, **kwargs)
+ self.default_orderby = "-date"
+
+ def get_context_data(self,**kwargs):
+ context = super(HistoryInvestigationTable, self).get_context_data(**kwargs)
+ return context
+
+ def setup_queryset(self, *args, **kwargs):
+ self.queryset = InvestigationHistory.objects.all()
+
+ def setup_columns(self, *args, **kwargs):
+ self.add_column(title="Investigation",
+ hideable=False,
+ orderable=True,
+ static_data_name="investigation",
+ static_data_template='''<a href="{% url 'investigation' data.investigation.name %}">{{data.investigation.name}}</a>''',
+ )
+ self.add_column(title="Comment",
+ field_name="comment",
+ hideable=False,
+ orderable=True,
+ )
+ self.add_column(title="Date",
+ field_name="date",
+ hideable=False,
+ orderable=True,
+ )
+ self.add_column(title="Author",
+ field_name="author",
+ hideable=False,
+ orderable=True,
+ )
+
+class HistoryDefectTable(ToasterTable):
+ """Table of History Defect Details in SRTool"""
+
+ def __init__(self, *args, **kwargs):
+ super(HistoryDefectTable, self).__init__(*args, **kwargs)
+ self.default_orderby = "-date"
+
+ def get_context_data(self,**kwargs):
+ context = super(HistoryDefectTable, self).get_context_data(**kwargs)
+ return context
+
+ def setup_queryset(self, *args, **kwargs):
+ self.queryset = DefectHistory.objects.all()
+
+ def setup_columns(self, *args, **kwargs):
+ self.add_column(title="Defect",
+ hideable=False,
+ orderable=True,
+ static_data_name="defect",
+ static_data_template='''<a href="{% url 'defect_name' data.defect.name %}">{{data.defect.name}}</a>''',
+ )
+ self.add_column(title="Comment",
+ field_name="comment",
+ hideable=False,
+ orderable=True,
+ )
+ self.add_column(title="Date",
+ field_name="date",
+ hideable=False,
+ orderable=True,
+ )
+ self.add_column(title="Author",
+ field_name="author",
+ hideable=False,
+ orderable=True,
+ )
+
+
+class PublishListTable(ToasterTable):
+ """Table of Publish View in SRTool"""
+
+ def __init__(self, *args, **kwargs):
+ super(PublishListTable, self).__init__(*args, **kwargs)
+ self.default_orderby = "cve"
+
+ def get_context_data(self,**kwargs):
+ context = super(PublishListTable, self).get_context_data(**kwargs)
+ return context
+
+ def setup_filters(self, *args, **kwargs):
+ # Is Status filter
+ is_status = TableFilter(name="is_status",
+ title="Filter CVE's by 'Status'")
+ for status in range(len(Cve.STATUS)):
+ is_status.add_action(TableFilterActionToggle(
+ Cve.STATUS[status][1].lower().replace(' ','_'),
+ Cve.STATUS[status][1],
+ Q(cve__status=Cve.STATUS[status][0]))
+ )
+ self.add_filter(is_status)
+
+ # Is State filter
+ is_state = TableFilter(name="is_state",
+ title="Filter items by 'State'")
+ for state in range(len(PublishSet.PUBLISH_SET_STATE)):
+ if PublishSet.PUBLISH_SET_ERROR == PublishSet.PUBLISH_SET_STATE[state][0]:
+ continue
+ is_state.add_action(TableFilterActionToggle(
+ PublishSet.PUBLISH_SET_STATE[state][1].lower().replace(' ','_'),
+ PublishSet.PUBLISH_SET_STATE[state][1],
+ Q(state=PublishSet.PUBLISH_SET_STATE[state][0]))
+ )
+ self.add_filter(is_state)
+
+#Record.objects.filter( Q(parameter__icontains="wd2") | ~Q(parameter__icontains="wd") )
+
+ # V3 filter
+ is_v3 = TableFilter(name="is_v3",title="Filter items by 'V3'")
+ exec_v3 = TableFilterActionToggle(
+ "v3",
+ "Severity_V3 change",
+ Q(reason__icontains="Severity_V3"))
+ is_v3.add_action(exec_v3)
+ self.add_filter(is_v3)
+
+ # V2 filter
+ is_v2 = TableFilter(name="is_v2",title="Filter items by 'V2'")
+ exec_v2 = TableFilterActionToggle(
+ "v2",
+ "Severity_V2 change",
+ Q(reason__icontains="Severity_V2"))
+ is_v2.add_action(exec_v2)
+ self.add_filter(is_v2)
+
+ # Product filters
+ # Gather the supported products
+ product_query = Product.objects.filter()
+ product_filter = []
+ for product in product_query:
+ if "support" == product.get_product_tag('mode').order_by('-order'):
+ product_filter.append(product.get_defect_tag('key'))
+ for product_key in product_filter:
+ is_filter = TableFilter(name="is_%s" % product_key,title="Filter CVE's by '%s'" % product_key)
+ for status in range(len(Cve.STATUS)):
+ is_filter.add_action(TableFilterActionToggle(
+ product_key.lower(),
+ product_key,
+ Q(reason__icontains==product_key))
+ )
+ self.add_filter(is_filter)
+
+
+ def setup_queryset(self, *args, **kwargs):
+ self.queryset = PublishSet.objects.all()
+
+ def apply_row_customization(self, row):
+ data = super(PublishListTable, self).apply_row_customization(row)
+ # data:dict_keys(['rows', 'total', 'default_orderby', 'error', 'columns'])
+ def get_key(key,dict):
+ if key in dict:
+ return(dict[key])
+ return ''
+ # {'Severity_V2': '["", "MEDIUM"]', 'Severity_V3': '["", "MEDIUM"]'}
+ for i in range(len(data['rows'])):
+ reason = data['rows'][i]['reason']
+ if not reason:
+ continue
+ try:
+ # CvssV3
+ reason_dict = json.loads(reason)
+ cvssV3 = get_key('Severity_V3',reason_dict)
+ if cvssV3:
+ data['rows'][i]['cvssV3'] = "%s,%s" % (cvssV3[0],cvssV3[1])
+ # CvssV2
+ cvssV2 = get_key('Severity_V2',reason_dict)
+ if cvssV2:
+ data['rows'][i]['cvssV2'] = "%s,%s" % (cvssV2[0],cvssV2[1])
+ # Products
+ for product_key in ('LIN5','CGP5','SCP5','OVP','LIN6','CGP6','SCP6','LIN7','CGP7','SCP7','LIN8','LIN9','LIN10','LIN1018'):
+ product_col = get_key(product_key,reason_dict)
+ if product_col:
+ data['rows'][i][product_key] = "%s" % (product_col)
+ except Exception as e:
+ continue
+ return data
+
+ 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" id="box_{{data.id}}" name="{{data.cve.name}}" />',
+ )
+
+ self.add_column(title="State",
+ hideable=False,
+ orderable=True,
+ filter_name="is_state",
+ static_data_name="state",
+ static_data_template='''{{data.state_text}}''',
+ )
+
+ self.add_column(title="CVE",
+ field_name="cve__name",
+ hideable=False,
+ orderable=True,
+ static_data_name="cve__name",
+ static_data_template='''<a href="{% url 'cve' data.cve.name %}">{{data.cve.name}}</a>''',
+ )
+
+ self.add_column(title="CVE Published",
+ field_name="cve__publishedDate",
+ hideable=False,
+ orderable=True,
+ )
+
+ self.add_column(title="CVE Modified",
+ field_name="cve__lastModifiedDate",
+ hideable=False,
+ orderable=True,
+ )
+
+ self.add_column(title="CVE Status",
+ field_name="cve_status",
+ hideable=False,
+ orderable=True,
+ filter_name="is_status",
+ static_data_name="cve_status",
+ static_data_template='''{{data.cve.get_status_text}}''',
+ )
+
+ self.add_column(title="cvssV3",
+ hideable=True,
+ hidden=False,
+ filter_name="is_v3",
+ static_data_name="cvssV3",
+ static_data_template='',
+ )
+
+ self.add_column(title="cvssV2",
+ hideable=True,
+ hidden=False,
+ filter_name="is_v2",
+ static_data_name="cvssV2",
+ static_data_template='',
+ )
+
+ self.add_column(title="CVE Description",
+ field_name="cve__description",
+ hideable=False,
+ orderable=False,
+ )
+
+ # Product columns
+ for product_key in ('LIN5','CGP5','SCP5','OVP','LIN6','CGP6','SCP6','LIN7','CGP7','SCP7','LIN8','LIN9','LIN10','LIN1018'):
+ self.add_column(title=product_key,
+# hideable=True,
+# hidden=True,
+ filter_name="is_%s" % product_key,
+ static_data_name=product_key,
+ static_data_template='',
+ )
+
+ self.add_column(title="CVE Acknowledge",
+ hideable=True,
+ hidden=True,
+ orderable=True,
+ static_data_name="cve_acknowledge",
+ static_data_template='''{{data.cve.acknowledge_date|date:'Y-m-d'}}''',
+ )
+
+ self.add_column(title="Public Comments",
+ hideable=False,
+ orderable=False,
+ static_data_name="public_comments",
+ static_data_template='''{{data.cve.get_public_comments}}''',
+ )
+
+ self.add_column(title="reason",
+ field_name="reason",
+ hideable=False,
+ orderable=False,
+ )
+
+
+class PublishCveTable(ToasterTable):
+ """Table of Publish View in SRTool"""
+
+ def __init__(self, *args, **kwargs):
+ super(PublishCveTable, self).__init__(*args, **kwargs)
+ self.default_orderby = "-status"
+
+ def get_context_data(self,**kwargs):
+ context = super(PublishCveTable, self).get_context_data(**kwargs)
+ return context
+
+ def setup_filters(self, *args, **kwargs):
+ # Is Status filter
+ is_status = TableFilter(name="is_status",
+ title="Filter CVE's by 'Status'")
+ for status in range(len(Cve.STATUS)):
+ is_status.add_action(TableFilterActionToggle(
+ Cve.STATUS[status][1].lower().replace(' ','_'),
+ Cve.STATUS[status][1],
+ Q(status=Cve.STATUS[status][0]))
+ )
+ self.add_filter(is_status)
+
+ def setup_queryset(self, *args, **kwargs):
+ self.queryset = Cve.objects.all()
+ exclude_list = [Cve.NEW, Cve.HISTORICAL, Cve.NEW_RESERVED] # CVE.NEW
+ self.queryset = self.queryset.exclude(status__in=exclude_list)
+
+ 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" id="box_{{data.id}}" name="{{data.name}}" />',
+ )
+
+ self.add_column(title="State",
+ hideable=False,
+ orderable=True,
+ static_data_name="state",
+ static_data_template='''{{data.get_publishset_state}}''',
+ )
+
+ self.add_column(title="Name",
+ field_name="name",
+ hideable=False,
+ orderable=True,
+ static_data_name="cve_name",
+ static_data_template='''<a href="{% url 'cve' data.name %}">{{data.name}}</a>''',
+ )
+
+ self.add_column(title="CVE Published",
+ field_name="publishedDate",
+ hideable=False,
+ orderable=True,
+ )
+
+ self.add_column(title="CVE Modified",
+ field_name="lastModifiedDate",
+ hideable=False,
+ orderable=True,
+ )
+
+ self.add_column(title="CVE Status",
+ field_name="status",
+ hideable=False,
+ orderable=True,
+ filter_name="is_status",
+ static_data_name="cve_status",
+ static_data_template='''{{data.get_status_text}}''',
+ )
+
+ self.add_column(title="CVE Description",
+ field_name="description",
+ hideable=False,
+ orderable=False,
+ )
+
+ self.add_column(title="CVE Acknowledge",
+ hideable=True,
+ hidden=True,
+ orderable=True,
+ static_data_name="cve_acknowledge",
+ static_data_template='''{{data.acknowledge_date|date:'Y-m-d'}}''',
+ )
+
+
+class PublishDefectTable(ToasterTable):
+ """Table of Publish View in SRTool"""
+
+ def __init__(self, *args, **kwargs):
+ super(PublishDefectTable, self).__init__(*args, **kwargs)
+ self.default_orderby = "-date_updated"
+
+ def get_context_data(self,**kwargs):
+ context = super(PublishDefectTable, self).get_context_data(**kwargs)
+ return context
+
+ def setup_filters(self, *args, **kwargs):
+ # Is Status filter
+ is_status = TableFilter(name="is_status",
+ title="Filter CVE's by 'Status'")
+ for status in range(len(Defect.DEFECT_STATUS)):
+ is_status.add_action(TableFilterActionToggle(
+ Defect.DEFECT_STATUS[status][1].lower().replace(' ','_'),
+ Defect.DEFECT_STATUS[status][1],
+ Q(status=Defect.DEFECT_STATUS[status][0]))
+ )
+ self.add_filter(is_status)
+
+ def setup_queryset(self, *args, **kwargs):
+ self.queryset = Defect.objects.all()
+
+ 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" id="box_{{data.id}}" name="{{data.name}}" />',
+ )
+
+ self.add_column(title="State",
+ hideable=False,
+ orderable=True,
+ static_data_name="state",
+ static_data_template='''{{data.get_publishset_state}}''',
+ )
+
+ self.add_column(title="Name",
+ hideable=False,
+ orderable=True,
+ static_data_name="name",
+ static_data_template='''<a href="{% url 'defect_name' data.name %}">{{data.name}}</a>''',
+ )
+
+ self.add_column(title="Created",
+ hideable=False,
+ orderable=True,
+ static_data_name="date_created",
+ static_data_template='''{{data.get_date_created_text}}''',
+ )
+
+ self.add_column(title="Modified",
+ hideable=False,
+ orderable=True,
+ static_data_name="date_updated",
+ static_data_template='''{{data.get_date_updated_text}}''',
+ )
+
+ 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_defect_status_text}}''',
+ )
+
+ self.add_column(title="Summary",
+ field_name="summary",
+ hideable=False,
+ orderable=False,
+ )
+
+ self.add_column(title="Release Version",
+ field_name="release_version",
+ orderable=True,
+ )
+
+ self.add_column(title="CVE List",
+ field_name="get_cve_names",
+ hideable=False,
+ orderable=False,
+ )
+
diff --git a/lib/srtgui/templates/base.html b/lib/srtgui/templates/base.html
index f8b43194..623dfdd8 100644
--- a/lib/srtgui/templates/base.html
+++ b/lib/srtgui/templates/base.html
@@ -226,9 +226,9 @@ window.onclick = function(event) {
<li id="navbar-export">
{% if request.resolver_match.url_name == 'landing' %}
- <a href="{% url 'report' request.resolver_match.url_name %}"><i class="glyphicon glyphicon-tasks"></i> Export</a>
+ <a href="{% url 'report' request.resolver_match.url_name %}" target="_blank"><i class="glyphicon glyphicon-tasks"></i> Export</a>
{% else %}
- <a id="report_link" href="{% url 'report' request.resolver_match.url_name %}"><i class="glyphicon glyphicon-tasks"></i> Export</a>
+ <a id="report_link" href="{% url 'report' request.resolver_match.url_name %}" target="_blank"><i class="glyphicon glyphicon-tasks"></i> Export</a>
{% endif %}
</li>
diff --git a/lib/srtgui/templates/basetable_top.html b/lib/srtgui/templates/basetable_top.html
index 5a9076d2..ce478c05 100644
--- a/lib/srtgui/templates/basetable_top.html
+++ b/lib/srtgui/templates/basetable_top.html
@@ -140,7 +140,7 @@
if ( !editColTimer ) {
//
- // we don't have a timer active so set one up
+ // we do not have a timer active so set one up
// and clear the action list
//
@@ -173,8 +173,9 @@
<form class="navbar-form navbar-left" id="searchform">
<div class="form-group">
<div class="btn-group">
- <input class="form-control" id="search" name="search" type="text" placeholder="Search {%if object_search_display %}{{object_search_display}}{%else%}{{objectname}}{%endif%}" value="{%if request.GET.search %}{{request.GET.search}}{% endif %}"/>
+ <input class="form-control" id="search" name="search" type="text" placeholder="Search! {%if object_search_display %}{{object_search_display}}{%else%}{{objectname}}{%endif%}" value="{%if request.GET.search %}{{request.GET.search}}{% endif %}"/>
{% if request.GET.search %}<a href="javascript:$('#search').val('');searchform.submit()" tabindex="-1"><span class="remove-search-btn-variables glyphicon glyphicon-remove-circle"></span></a>{%endif%}
+ <span class="glyphicon glyphicon-question-sign get-help" title="Default is an 'and' search; use 'OR' keyword to 'or' the terms"></span>
</div>
</div>
<input type="hidden" name="orderby" value="{{request.GET.orderby}}">
diff --git a/lib/srtgui/templates/cve.html b/lib/srtgui/templates/cve.html
index ecbcf39e..c3cfcac5 100644
--- a/lib/srtgui/templates/cve.html
+++ b/lib/srtgui/templates/cve.html
@@ -40,6 +40,7 @@
{% else %}
<span style="padding-left:30px;"><button id="select-cveedit" class="btn btn-default" type="button">Edit CVE Data ...</button></span>
{% endif %}
+ <span style="padding-left:30px;"><button id="submit-delete-cve" class="btn btn-default" type="button">Delete CVE</button></span>
{% endif %}
</span>
{% if not is_edit %}
@@ -106,8 +107,8 @@
</tr>
</thead>
- {% if cve_list_table.1.0.cve_history.all %}
- {% for c in cve_list_table.1.0.cve_history.all %}
+ {% if object.cve_history.all %}
+ {% for c in object.cve_history.all %}
<tr>
<td>{{ c.comment }}</td>
<td>{{ c.date }}</td>
@@ -123,6 +124,7 @@
</div>
<HR ALIGN="center" WIDTH="100%">
+Created={{object.srt_created}} Updated={{object.srt_updated}}
<script>
var selected_quickedit=false;
@@ -149,7 +151,9 @@
}
// reload the page with the updated tables
- if (('new_name' in data) && ("" != data.new_name)) {
+ if (('new_name' in data) && (0 == data.new_name.indexOf("url:"))) {
+ window.location.replace(data.new_name.replace("url:",""));
+ } else if (('new_name' in data) && ("" != data.new_name)) {
var new_url = "{% url 'cve' object.name %}".replace("{{object.name}}",data.new_name);
window.location.replace(new_url);
} else {
@@ -199,18 +203,24 @@
$('#submit-quickedit').click(function(){
var note=$('#text-note').val().trim();
var private_note=$('#text-private-note').val().trim();
+ var tags=$('#text-tags').val().trim();
var priority=$('#select-priority-state').val();
var status=$('#select-status-state').val();
var publish_state=$('#select-publish-state').val();
var publish_date=$('#select-publish-date').val();
+ var acknowledge_date=$('#text-acknowledge-date').val();
+ var affected_components=$('#text-affected-components').val();
postCommitAjaxRequest({
"action" : 'submit-quickedit',
"priority" : priority,
"status" : status,
"note" : note,
"private_note" : private_note,
+ "tags" : tags,
"publish_state" : publish_state,
"publish_date" : publish_date,
+ "acknowledge_date" : acknowledge_date,
+ "affected_components" : affected_components
});
});
@@ -292,6 +302,15 @@
});
});
+ $("#submit-delete-cve").click(function(){
+ var result = confirm("Are you sure you want to permamently delete '{{object.name}}' and all its related records?");
+ if (result){
+ postCommitAjaxRequest({
+ "action" : 'submit-delete-cve'
+ });
+ }
+ });
+
/* Set the report link */
diff --git a/lib/srtgui/templates/cves-select-toastertable.html b/lib/srtgui/templates/cves-select-toastertable.html
index 38828359..d29a2b92 100644
--- a/lib/srtgui/templates/cves-select-toastertable.html
+++ b/lib/srtgui/templates/cves-select-toastertable.html
@@ -1,4 +1,7 @@
{% extends 'base.html' %}
+{% load projecttags %}
+{% load humanize %}
+
{% load static %}
@@ -29,7 +32,7 @@
/* Define the columns that floats next to each other */
.column1 {
float: left;
- width: 280px;
+ width: 380px;
padding: 10px;
}
.column2 {
@@ -48,7 +51,7 @@
{% endblock %}
-{% block title %} Select CVE's - SRTool {% endblock %}
+{% block title %} Select CVEs - SRTool {% endblock %}
{% block pagecontent %}
@@ -58,8 +61,8 @@
<ul class="breadcrumb" id="breadcrumb">
<li><a href="{% url 'landing' %}">Home</a></li><span class="divider">&rarr;</span>
<li><a href="{% url 'manage' %}">Management</a></li><span class="divider">&rarr;</span>
- <li><a href="{% url 'triage_cves' %}">Triage CVE's</a></li><span class="divider">&rarr;</span>
- <li>Select CVE's</li>
+ <li><a href="{% url 'triage_cves' %}">Triage CVEs</a></li><span class="divider">&rarr;</span>
+ <li>Select CVEs</li>
</ul>
</div>
</div>
@@ -79,17 +82,18 @@
</div>
-->
- <div id="details-isvulnerable" style="display:none;">
+ <div id="details-isvulnerable-investigate" style="display:none;">
<p><p>
- <button class="execute" id="submit-isvulnerable"> Submit Vulnerable CVE's </button>
+ <button class="execute" id="submit-isvulnerable-investigate"> Submit Vulnerable CVEs </button>
<div class="row">
<div class="column1">
<p><b><label id="products_count">Optional: Add Vulnerable Products (0):</label></b>
- <div id="all-products" class="scrolling" style="width: 250px;">
- {% for product in products %}
+ <div id="all-products" class="scrolling" style="width: 350px;">
+ {% for product in products|dictsort:"order" %}
<div class="checkbox">
<label>
<input class="checkbox-products" name="{{product.pk}}" type="checkbox">{{product.long_name}}
+ &nbsp;({{product.defect_tags|get_strdict_value:"found_version"}})
</label>
<p>
</div>
@@ -100,10 +104,13 @@
<p><b><label id="priority">Set Priority:</label></b>
<div id="priority-list" class="scrolling" style="width: 120px;">
<div class="checkbox"> <label>
+ <input type="radio" name="priority" value="99" type="checkbox" checked="yes"> Auto <span class="glyphicon glyphicon-question-sign get-help" title="CVSSv3, else CVSSv2, else Medium"></span>
+ </label><p></div>
+ <div class="checkbox"> <label>
<input type="radio" name="priority" value="4" type="checkbox"> High
</label><p></div>
<div class="checkbox"> <label>
- <input type="radio" name="priority" value="3" type="checkbox" checked="yes"> Medium
+ <input type="radio" name="priority" value="3" type="checkbox"> Medium
</label><p></div>
<div class="checkbox"> <label>
<input type="radio" name="priority" value="2" type="checkbox"> Low
@@ -127,11 +134,23 @@
</div>
<p><input id="create_defects" type="checkbox"> Create Defect(s) </input>
- Reason: <input type="text" id="input-defect-reason" name="defect-reason" size="20" placeholder="(optional)"> (e.g. "Security Advisory [- REASON -] CVE-2020-1234")
+ Reason: <input type="text" id="input-defect-reason" name="defect-reason" size="20" placeholder="(optional)"> (e.g. Defect summary: "Security Advisory [- REASON -] CVE-2020-1234")
&nbsp;&nbsp;<input id="create_notifications" type="checkbox" checked>&nbsp;Create Notifications</input>
<p><b><big>Reason: </big></b>
<input type="text" id="input-isvulnerable-reason" name="reason" size="40">&nbsp;&nbsp;<input id="markPublishIs" type="checkbox">&nbsp;Mark for Publish</input>&nbsp;&nbsp;<input id="markFor" type="checkbox"> Add Keywords to 'For' </input>
+ <p><b><big>Affected Components: </big></b>
+ <input type="text" id="input-isvulnerable-components" name="components" size="40"> (e.g. space-separated list of packages, recipes, sub-system list, applications, )
+
+ <div id="published-date-list">
+ <p><i>Acknowledge Date</i> =
+ <select name="Acknowledge_Date" id="select-acknowledge-date">
+ <option value="today" selected>Today</option>
+ <option value="publish">CVE's original release date</option>
+ <option value="update">CVE's last revised date</option>
+ <option value="no_change">No change</option>
+ </select>
+ </div>
<div id="group_vulnerability" style="border: 1px solid #dddddd; padding: 0 5px; width: 400px; margin-bottom: 10px; margin-top: 0px; " >
<div class="checkbox"> <label>
@@ -151,15 +170,15 @@
<div id="details-notvulnerable" style="display:none;">
<p><p>
- <button class="execute" id="submit-notvulnerable"> Submit Not-vulnerable CVE's </button>
- <input id="notship" type="checkbox"> "We do not ship ..." </input>
+ <button class="execute" id="submit-notvulnerable"> Submit Not-vulnerable CVEs </button>
+ <input id="notship" type="checkbox"> "{{doesnotimpact_text|safe}}" </input>
<p><b><big>Reason: </big></b>
<p><input type="text" id="input-notvulnerable-reason" name="reason" size="40">&nbsp;&nbsp;<input id="markPublishNot" type="checkbox">&nbsp;Mark for Publish</input>&nbsp;&nbsp;<input id="markAgainst" type="checkbox"> Add Keywords to 'Against' </input>
</div>
<div id="details-investigate" style="display:none;">
<p><p>
- <button class="execute" id="submit-investigate"> Submit Investigation CVE's </button>
+ <button class="execute" id="submit-investigate"> Submit Investigation CVEs </button>
</div>
<div id="details-other" style="display:none;">
@@ -251,13 +270,11 @@
}
document.getElementById("unselect-these").innerText = "Un-select "+cve_checked_count+" checked";
if (0 == cve_checked_count) {
- //$("#submit-isvulnerable").attr("disabled","disabled");
document.getElementById("submit-notvulnerable").disabled = true;
- document.getElementById("submit-isvulnerable").disabled = true;
+ document.getElementById("submit-isvulnerable-investigate").disabled = true;
} else {
- //$("#submit-isvulnerable").removeAttr("disabled");
document.getElementById("submit-notvulnerable").disabled = false;
- document.getElementById("submit-isvulnerable").disabled = false;
+ document.getElementById("submit-isvulnerable-investigate").disabled = false;
}
}
@@ -282,19 +299,20 @@
$("#select-these").removeAttr("disabled");
$("#unselect-these").removeAttr("disabled");
$("#select-notvulnerable").removeAttr("disabled");
- $("#select-investigate").removeAttr("disabled");
$("#select-other").removeAttr("disabled");
- $("#details-isvulnerable").slideUp();
+ $("#details-isvulnerable-investigate").slideUp();
} else {
selected_isvulnerable=true;
$("#select-these").attr("disabled","disabled");
$("#unselect-these").attr("disabled","disabled");
$("#select-notvulnerable").attr("disabled","disabled");
- $("#select-investigate").attr("disabled","disabled");
$("#select-other").attr("disabled","disabled");
- $("#input-isvulnerable-reason").val($("#search-input-selectcvetable").val())
+ /* preset reason/components from search */
+ /* $("#input-isvulnerable-reason").val($("#search-input-selectcvetable").val()) */
+ /* $("#input-isvulnerable-components").val($("#search-input-selectcvetable").val()) */
update_vulnerable_status();
- $("#details-isvulnerable").slideDown();
+ document.getElementById("submit-isvulnerable-investigate").innerText = " Submit Vulnerable CVEs ";
+ $("#details-isvulnerable-investigate").slideDown();
}
});
@@ -330,7 +348,7 @@
$("#select-isvulnerable").removeAttr("disabled");
$("#select-notvulnerable").removeAttr("disabled");
$("#select-other").removeAttr("disabled");
- $("#details-investigate").slideUp();
+ $("#details-isvulnerable-investigate").slideUp();
} else {
selected_investigate=true;
$("#select-these").attr("disabled","disabled");
@@ -339,7 +357,8 @@
$("#select-notvulnerable").attr("disabled","disabled");
$("#select-other").attr("disabled","disabled");
update_vulnerable_status();
- $("#details-investigate").slideDown();
+ document.getElementById("submit-isvulnerable-investigate").innerText = " Submit Investigation CVEs ";
+ $("#details-isvulnerable-investigate").slideDown();
}
});
@@ -367,19 +386,19 @@
$('#notship').click(function(){
- not_ship = "We do not ship '";
+ not_ship = "{{doesnotimpact_text|safe}}";
var reason = $("#input-notvulnerable-reason").val().trim();
reason = reason.replace(not_ship,'');
- if ("'" == reason.slice(-1)) {
- reason = reason.slice(0,-1)
- }
if ($('#notship').is(':checked')) {
- reason = not_ship + reason + "'";
+ reason = not_ship + reason;
}
$("#input-notvulnerable-reason").val(reason);
});
- $('#submit-isvulnerable').click(function(){
+ $('#submit-isvulnerable-investigate').click(function(){
+
+ /* Disable the button so the we do not get double clicks */
+ $("#submit-isvulnerable-investigate").attr("disabled","disabled");
var cve_list=[];
$('#selectcvetable input:checked').each(function(){
@@ -387,7 +406,7 @@
});
cve_list = cve_list.join(",");
if ("" == cve_list) {
- alert("No CVE's were selected");
+ alert("No CVEs were selected");
return;
}
var product_list=[];
@@ -409,9 +428,15 @@
$('#group_vulnerability input:checked').each(function(){
group_vulnerability = $(this).attr('value');
});
+ if (selected_isvulnerable) {
+ action = 'submit-isvulnerable';
+ } else {
+ action = 'submit-investigate';
+ }
postCommitAjaxRequest({
- "action" : 'submit-isvulnerable',
+ "action" : action,
"reason" : $("#input-isvulnerable-reason").val(),
+ "affected_components" : $("#input-isvulnerable-components").val(),
"defect_reason" : $("#input-defect-reason").val(),
"cves" : cve_list,
"products": product_list,
@@ -420,6 +445,7 @@
"pub" : $('#markPublishIs').is(':checked') ? "yes" : "no",
"for" : $('#markFor').is(':checked') ? "yes" : "no",
"mk_d" : $('#create_defects').is(':checked') ? "yes" : "no",
+ "acknowledge_date" : $('#select-acknowledge-date').val(),
"vul_group": group_vulnerability,
"vul_name": $("#vulnerability_name").val(),
"notify" : $('#create_notifications').is(':checked') ? "yes" : "no",
@@ -435,36 +461,20 @@
});
cve_list = cve_list.join(",");
if ("" == cve_list) {
- alert("No CVE's were selected");
+ alert("No CVEs were selected");
return;
}
+ reason = $("#input-notvulnerable-reason").val()
+ reason = reason.replace(/:$/, "");
postCommitAjaxRequest({
"action" : 'submit-notvulnerable',
- "reason" : $("#input-notvulnerable-reason").val(),
+ "reason" : reason,
"cves" : cve_list,
"pub" : $('#markPublishNot').is(':checked') ? "yes" : "no",
"against" : $('#markAgainst').is(':checked') ? "yes" : "no",
});
});
- $('#submit-investigate').click(function(){
- var cve_list=[];
- $('#selectcvetable input').each(function(){
- if ($(this).is(':checked')) {
- cve_list.push($(this).prop('name'));
- }
- });
- cve_list = cve_list.join(",");
- if ("" == cve_list) {
- alert("No CVE's were selected");
- return;
- }
- postCommitAjaxRequest({
- "action" : 'submit-investigate',
- "cves" : cve_list,
- });
- });
-
$('#submit-other').click(function(){
var cve_list=[];
$('#selectcvetable input').each(function(){
@@ -474,7 +484,7 @@
});
cve_list = cve_list.join(",");
if ("" == cve_list) {
- alert("No CVE's were selected");
+ alert("No CVEs were selected");
return;
}
var status=$('#select-status-state').val();
@@ -501,11 +511,11 @@
var titleElt = $("[data-role='page-title']");
tableElt.on("table-done", function (e, total, tableParams) {
- var title = "Triage CVE's";
+ var title = "Triage CVEs";
if (tableParams.search || tableParams.filter) {
if (total === 0) {
- title = "No CVE's found";
+ title = "No CVEs found";
}
else if (total > 0) {
title = total + " CVE" + (total > 1 ? "'s" : '') + " found";
diff --git a/lib/srtgui/templates/defect.html b/lib/srtgui/templates/defect.html
index aa13b0dd..ed00fd90 100644
--- a/lib/srtgui/templates/defect.html
+++ b/lib/srtgui/templates/defect.html
@@ -38,14 +38,30 @@
<dt>URL:</dt>
<dd><a href="{{object.url}}" id="dataid_{{object.id}}" target="_blank">{{object.url}}</a></dd>
- <dt>Priority:</dt>
+ <dt>Defect Priority:</dt>
+ <dd>{{object.get_defect_priority_text}}</dd>
+
+ <dt>Defect Status:</dt>
+ <dd>{{object.get_defect_status_text}}</dd>
+
+ <dt>Defect Resolution:</dt>
+ <dd>{{object.get_defect_resolution_text}}</dd>
+
+ <dt>Duplicate Of:</dt>
+ <dd>
+ {% if object.duplicate_of %}
+ <a href="{% url 'defect_name' object.duplicate_of %}">{{object.duplicate_of}}</a>&nbsp;(<a href="{{SRTOOL_DEFECT_URLBASE}}/{{object.duplicate_of}}">{{SRTOOL_DEFECT_URLBASE}}/{{object.duplicate_of}}</a>)
+ {% endif %}
+ </dd>
+
+ <dt>SRTool Priority:</dt>
<dd>{{object.get_priority_text}}</dd>
- <dt>Status:</dt>
+ <dt>SRTool Status:</dt>
<dd>{{object.get_status_text}}</dd>
- <dt>Resolution:</dt>
- <dd>{{object.get_resolution_text}}</dd>
+ <dt>SRTool Outcome:</dt>
+ <dd>{{object.get_outcome_text}}</dd>
<dt>Publish:</dt>
<dd>{{object.publish}}</dd>
@@ -74,6 +90,36 @@
</div>
</div>
+<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 object.defect_history.all %}
+ {% for c in object.defect_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%">
+Updated={{object.srt_updated}}
+
+
<!-- Javascript support -->
<script>
diff --git a/lib/srtgui/templates/detail_search_header.html b/lib/srtgui/templates/detail_search_header.html
index 7a986590..6c61996a 100644
--- a/lib/srtgui/templates/detail_search_header.html
+++ b/lib/srtgui/templates/detail_search_header.html
@@ -30,12 +30,13 @@ $(document).ready(function() {
<div class="form-group">
<div class="btn-group">
- <input id="search" class="form-control" type="text" placeholder="Search {{search_what}}" name="search" value="{% if request.GET.search %}{{request.GET.search}}{% endif %}">
+ <input id="search" class="form-control" type="text" placeholder="@Search {{search_what}}" name="search" value="{% if request.GET.search %}{{request.GET.search}}{% endif %}">
<input type="hidden" value="name:+" name="orderby">
<input type="hidden" value="l" name="page">
{% if request.GET.search %}
<span class="remove-search-btn-detail-search search-clear glyphicon glyphicon-remove-circle"></span>
{% endif %}
+ <span class="glyphicon glyphicon-question-sign get-help" title="Default is an 'and' search; use 'OR' keyword to 'or' the terms"></span>
</div>
</div>
<button type="submit" class="btn btn-default">Search</button>
diff --git a/lib/srtgui/templates/history-cve-toastertable.html b/lib/srtgui/templates/history-cve-toastertable.html
new file mode 100755
index 00000000..78319466
--- /dev/null
+++ b/lib/srtgui/templates/history-cve-toastertable.html
@@ -0,0 +1,73 @@
+{% 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 %} CVE Histories - 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">&rarr;</span>
+ <li><a href="{% url 'manage' %}">Management</a></li><span class="divider">&rarr;</span>
+ <li><a href="{% url 'maintenance' %}">Maintenance</a></li><span class="divider">&rarr;</span>
+ <li>History CVE</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 = "History CVE";
+
+ if (tableParams.search || tableParams.filter) {
+ if (total === 0) {
+ title = "No History CVE found";
+ }
+ else if (total > 0) {
+ title = total + " History CVE" + (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/history-defect-toastertable.html b/lib/srtgui/templates/history-defect-toastertable.html
new file mode 100755
index 00000000..63e9ea4d
--- /dev/null
+++ b/lib/srtgui/templates/history-defect-toastertable.html
@@ -0,0 +1,73 @@
+{% 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 %} Defect Histories - 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">&rarr;</span>
+ <li><a href="{% url 'manage' %}">Management</a></li><span class="divider">&rarr;</span>
+ <li><a href="{% url 'maintenance' %}">Maintenance</a></li><span class="divider">&rarr;</span>
+ <li>History Defect</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 = "History Defect";
+
+ if (tableParams.search || tableParams.filter) {
+ if (total === 0) {
+ title = "No History Defect found";
+ }
+ else if (total > 0) {
+ title = total + " History Defect" + (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/history-investigation-toastertable.html b/lib/srtgui/templates/history-investigation-toastertable.html
new file mode 100755
index 00000000..bde11ec7
--- /dev/null
+++ b/lib/srtgui/templates/history-investigation-toastertable.html
@@ -0,0 +1,73 @@
+{% 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 %} Investigation Histories - 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">&rarr;</span>
+ <li><a href="{% url 'manage' %}">Management</a></li><span class="divider">&rarr;</span>
+ <li><a href="{% url 'maintenance' %}">Maintenance</a></li><span class="divider">&rarr;</span>
+ <li>History Investigation</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 = "History Investigation";
+
+ if (tableParams.search || tableParams.filter) {
+ if (total === 0) {
+ title = "No History Investigation found";
+ }
+ else if (total > 0) {
+ title = total + " History Investigation" + (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/history-vulnerability-toastertable.html b/lib/srtgui/templates/history-vulnerability-toastertable.html
new file mode 100755
index 00000000..bc3b7881
--- /dev/null
+++ b/lib/srtgui/templates/history-vulnerability-toastertable.html
@@ -0,0 +1,73 @@
+{% 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 %} Vulnerability Histories - 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">&rarr;</span>
+ <li><a href="{% url 'manage' %}">Management</a></li><span class="divider">&rarr;</span>
+ <li><a href="{% url 'maintenance' %}">Maintenance</a></li><span class="divider">&rarr;</span>
+ <li>History Vulnerability</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 = "History Vulnerability";
+
+ if (tableParams.search || tableParams.filter) {
+ if (total === 0) {
+ title = "No History Vulnerability found";
+ }
+ else if (total > 0) {
+ title = total + " History Vulnerabilit" + (total > 1 ? 'ies' : 'y') + " 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/investigation.html b/lib/srtgui/templates/investigation.html
index b662c5e1..f934d052 100644
--- a/lib/srtgui/templates/investigation.html
+++ b/lib/srtgui/templates/investigation.html
@@ -110,13 +110,16 @@
<p><b><label id="priority">Set Priority:</label></b>
<div id="priority-list" class="scrolling" style="width: 120px;">
<div class="checkbox"> <label>
- <input type="radio" name="priority" value="4" type="checkbox"> High
+ <input type="radio" name="priority" value="4" type="checkbox"> P1
</label><p></div>
<div class="checkbox"> <label>
- <input type="radio" name="priority" value="3" type="checkbox" checked="yes"> Medium
+ <input type="radio" name="priority" value="3" type="checkbox" checked="yes"> P2
</label><p></div>
<div class="checkbox"> <label>
- <input type="radio" name="priority" value="2" type="checkbox"> Low
+ <input type="radio" name="priority" value="2" type="checkbox"> P3
+ </label><p></div>
+ <div class="checkbox"> <label>
+ <input type="radio" name="priority" value="1" type="checkbox"> P4
</label><p></div>
</div>
</div>
@@ -135,8 +138,14 @@
</div>
</div>
</div>
- Reason: <input type="text" id="input-defect-reason" name="defect-reason" size="20" placeholder="(optional)"> (e.g. "Security Advisory [- REASON -] CVE-2020-1234")
+ Defect Reason: <input type="text" id="input-defect-reason" name="defect-reason" size="20" placeholder="(optional)"> (e.g. "Security Advisory [- REASON -] CVE-2020-1234")
+ <p><p>
+ <b><big>Affected Components: </big></b>
+ <input type="text" id="input-affected-components" name="components" size="40" value="{{affected_components}}"> (e.g. space-separated list of packages, recipes, sub-system list, applications, )
<p><p>
+ Found Version: {{found_version}}
+ <p><p>
+
</div>
</div>
@@ -163,7 +172,7 @@
<td>{{ id.defect.summary }}</td>
<td>{{ id.defect.get_priority_text }}</td>
<td>{{ id.defect.get_status_text }}</td>
- <td>{{ id.defect.get_resolution_text }}</td>
+ <td>{{ id.defect.get_defect_resolution_text }}</td>
<td>{{ id.defect.release_version }}</td>
<td><a href="{{id.defect.url}}" id="dataid_{{id.defect.id}}" target="_blank">{{id.defect.url}}</a></td>
{% if request.user.is_creator %}
@@ -268,10 +277,10 @@
<td>{{ u.author }}</td>
<td>
<span id="attachment_entry_'+{{u.id}}+'" class="js-config-var-name"></span>
- <form id="downloadbanner" enctype="multipart/form-data" method="post" >{% csrf_token %}
+ <form id="downloadbanner-{{forloop.counter}}" enctype="multipart/form-data" method="post" >{% csrf_token %}
<input type="hidden" id="action" name="action" value="download">
<input type="hidden" id="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>
+ <span class="glyphicon glyphicon-download-alt submit-downloadattachment" id="attachment_download_'+{{u.id}}+'" x-data="{{forloop.counter}}"></span>
{% if request.user.is_creator %}
<span class="glyphicon glyphicon-trash trash-attachment" id="attachment_trash_'+{{u.id}}+'" x-data="{{u.id}}"></span>
{% endif %}
@@ -442,6 +451,9 @@
</table>
</div>
+<HR ALIGN="center" WIDTH="100%">
+Created={{object.srt_created}} Updated={{object.srt_updated}}
+
<script>
var selected_newcomment=false;
var selected_addusernotify=false;
@@ -539,6 +551,7 @@
postCommitAjaxRequest({
"action" : 'submit-createdefect',
"defect_reason" : $("#input-defect-reason").val(),
+ "affected_components" : $("#input-affected-components").val(),
"components": component_list,
"priority": priority,
});
@@ -614,8 +627,8 @@
}
});
- $('.submit-downloadattachment').click(function() {
- $("#downloadbanner").submit();
+ $('.submit-downloadattac hment').click(function() {
+ $("#downloadbanner-"+this.getAttribute("x-data")).submit();
});
$('.trash-attachment').click(function() {
@@ -728,16 +741,18 @@
$('#submit-quickedit').click(function(){
var note=$('#text-note').val().trim()
var private_note=$('#text-private-note').val().trim()
+ var tags=$('#text-tags').val().trim();
var priority=$('#select-priority-state').val();
var status=$('#select-status-state').val();
var outcome=$('#select-outcome-state').val();
postCommitAjaxRequest({
- "action" : 'submit-quickedit',
- "note" : note,
- "private_note" : private_note,
- "status" : status,
- "outcome" : outcome,
- "priority" : priority,
+ "action" : 'submit-quickedit',
+ "priority" : priority,
+ "status" : status,
+ "note" : note,
+ "private_note" : private_note,
+ "tags" : tags,
+ "outcome" : outcome,
});
});
diff --git a/lib/srtgui/templates/maintenance.html b/lib/srtgui/templates/maintenance.html
new file mode 100755
index 00000000..63c60f33
--- /dev/null
+++ b/lib/srtgui/templates/maintenance.html
@@ -0,0 +1,78 @@
+{% extends "base.html" %}
+
+{% load static %}
+{% load projecttags %}
+{% load humanize %}
+
+{% block title %} Maintenance tools {% endblock %}
+{% block pagecontent %}
+ <div class="row">
+ <div class="col-md-7" style="padding-left: 50px;">
+ <h1>Maintenance</h1>
+ </div>
+ </div>
+ <div class="row">
+ <div class="jumbotron well-transparent">
+
+ <div class="col-md-6">
+ <div>
+ <table class="table table-striped table-condensed" data-testid="landing-hyperlinks-table">
+ <thead>
+ <tr>
+ <th>Action</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+
+ <tr>
+ <td><a class="btn btn-info btn-lg" href="{% url 'history_cve' %}">History CVE</a></td>
+ <td>Examine History for CVEs</td>
+ </tr>
+ <tr>
+ <td><a class="btn btn-info btn-lg" href="{% url 'history_vulnerability' %}">History Vulnerabilities</a></td>
+ <td>Examine History for Vulnerabilities</td>
+ </tr>
+ <tr>
+ <td><a class="btn btn-info btn-lg" href="{% url 'history_investigation' %}">History Investigations</a></td>
+ <td>Examine History for Investigations</td>
+ </tr>
+ <tr>
+ <td><a class="btn btn-info btn-lg" href="{% url 'history_defect' %}">History Defects</a></td>
+ <td>Examine History for Defects</td>
+ </tr>
+
+ </table>
+ </div>
+
+ </div>
+
+ <div class="col-md-5">
+ <b>Quick Info</b>
+ <div class="well">
+ <dl class="dl-horizontal">
+
+ <dt>CVE History: Total Count =</dt>
+ <dd>
+ {{history_cve_total}}
+ </dd>
+ <dt>Vulnerability History: Total Count =</dt>
+ <dd>
+ {{history_vulnerability_total}}
+ </dd>
+ <dt>Investigation: Total Count =</dt>
+ <dd>
+ {{history_investigation_total}}
+ </dd>
+ <dt>Defect: Total Count =</dt>
+ <dd>
+ {{defect_investigation_total}}
+ </dd>
+
+ </dl>
+ </div>
+ </div>
+
+ </div>
+ </div>
+
+{% endblock %}
diff --git a/lib/srtgui/templates/management.html b/lib/srtgui/templates/management.html
index fe40ecb3..9b1e6456 100644
--- a/lib/srtgui/templates/management.html
+++ b/lib/srtgui/templates/management.html
@@ -40,8 +40,8 @@
</tr>
<tr>
- <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>
+ <td><a class="btn btn-info btn-lg" href="{% url 'publish' %}">Publish Reports</a></td>
+ <td>Process items to be published from the SRTool</td>
</tr>
{% if request.user.is_admin %}
@@ -54,6 +54,11 @@
<td><a class="btn btn-info btn-lg" href="{% url 'sources' %}?nocache=1">Manage Sources</a></td>
<td>Manage source list, perform manual pulls</td>
</tr>
+
+ <tr>
+ <td><a class="btn btn-info btn-lg" href="{% url 'maintenance' %}?nocache=1">Maintenance</a></td>
+ <td>Maintenance utilities</td>
+ </tr>
{% endif %}
</table>
@@ -75,15 +80,15 @@
</dd>
<dt>Investigate =</dt>
<dd>
- <a href="{% url 'cves' %}?limit=25&page=1&orderby=name&filter=is_status:new&default_orderby=name&filter_value=on&"> {{cve_investigate}} </a>
+ <a href="{% url 'cves' %}?limit=25&page=1&orderby=name&filter=is_status:investigate&default_orderby=name&filter_value=on&"> {{cve_investigate}} </a>
</dd>
<dt>Vulnerable =</dt>
<dd>
- <a href="{% url 'cves' %}?limit=25&page=1&orderby=name&filter=is_status:new&default_orderby=name&filter_value=on&"> {{cve_vulnerable}} </a>
+ <a href="{% url 'cves' %}?limit=25&page=1&orderby=name&filter=is_status:vulnerable&default_orderby=name&filter_value=on&"> {{cve_vulnerable}} </a>
</dd>
<dt>Not Vulnerable =</dt>
<dd>
- <a href="{% url 'cves' %}?limit=25&page=1&orderby=name&filter=is_status:new&default_orderby=name&filter_value=on&"> {{cve_not_vulnerable}} </a>
+ <a href="{% url 'cves' %}?limit=25&page=1&orderby=name&filter=is_status:not_vulnerable&default_orderby=name&filter_value=on&"> {{cve_not_vulnerable}} </a>
</dd>
<dt>Vulnerabilities: Total Count =</dt>
<dd>
@@ -93,17 +98,17 @@
<dd>
<a href="{% url 'vulnerabilities' %}?limit=25&page=1&orderby=name&filter=is_outcome:open&default_orderby=name&filter_value=on&"> {{vulnerability_open}} </a>
</dd>
- <dt>High active =</dt>
+ <dt>Critical active =</dt>
<dd>
- <a href="{% url 'vulnerabilities' %}?limit=25&page=1&orderby=name&filter=is_severity:high&default_orderby=name&filter_value=on&" %}> {{vulnerability_high}} </a>
+ <a href="{% url 'vulnerabilities' %}?limit=25&page=1&orderby=name&filter=is_priority:critical&default_orderby=name&filter_value=on&" %}> {{vulnerability_critical}} </a>
</dd>
- <dt>Medium active =</dt>
+ <dt>High active =</dt>
<dd>
- <a href="{% url 'vulnerabilities' %}?limit=25&page=1&orderby=name&filter=is_severity:medium&default_orderby=name&filter_value=on&" %}> {{vulnerability_medium}} </a>
+ <a href="{% url 'vulnerabilities' %}?limit=25&page=1&orderby=name&filter=is_priority:high&default_orderby=name&filter_value=on&" %}> {{vulnerability_high}} </a>
</dd>
- <dt>Low active =</dt>
+ <dt>Medium active =</dt>
<dd>
- <a href="{% url 'vulnerabilities' %}?limit=25&page=1&orderby=name&filter=is_severity:low&default_orderby=name&filter_value=on&" %}> {{vulnerability_low}} </a>
+ <a href="{% url 'vulnerabilities' %}?limit=25&page=1&orderby=name&filter=is_priority:medium&default_orderby=name&filter_value=on&" %}> {{vulnerability_medium}} </a>
</dd>
<dt>Investigations: Total Count =</dt>
@@ -114,17 +119,17 @@
<dd>
<a href="{% url 'investigations' %}?limit=25&page=1&orderby=name&filter=is_outcome:open&default_orderby=name&filter_value=on&" %}> {{investigation_open}} </a>
</dd>
- <dt>High active =</dt>
+ <dt>Critical active =</dt>
<dd>
- <a href="{% url 'investigations' %}?limit=25&page=1&orderby=name&filter=is_severity:high&default_orderby=name&filter_value=on&" %}> {{investigation_high}} </a>
+ <a href="{% url 'investigations' %}?limit=25&page=1&orderby=name&filter=is_priority:critical&default_orderby=name&filter_value=on&" %}> {{investigation_critical}} </a>
</dd>
- <dt>Medium active =</dt>
+ <dt>High active =</dt>
<dd>
- <a href="{% url 'investigations' %}?limit=25&page=1&orderby=name&filter=is_severity:medium&default_orderby=name&filter_value=on&" %}> {{investigation_medium}} </a>
+ <a href="{% url 'investigations' %}?limit=25&page=1&orderby=name&filter=is_priority:high&default_orderby=name&filter_value=on&" %}> {{investigation_high}} </a>
</dd>
- <dt>Low active =</dt>
+ <dt>Medium active =</dt>
<dd>
- <a href="{% url 'investigations' %}?limit=25&page=1&orderby=name&filter=is_severity:low&default_orderby=name&filter_value=on&" %}> {{investigation_low}} </a>
+ <a href="{% url 'investigations' %}?limit=25&page=1&orderby=name&filter=is_priority:medium&default_orderby=name&filter_value=on&" %}> {{investigation_medium}} </a>
</dd>
<dt>Defects: Total Count =</dt>
@@ -133,19 +138,19 @@
</dd>
<dt>Open =</dt>
<dd>
- <a href="{% url 'defects' %}?limit=25&page=1&orderby=-priority&filter=is_status:open&default_orderby=name&filter_value=on&" %}> {{defect_open}} </a>
+ <a href="{% url 'defects' %}?limit=25&page=1&orderby=-priority&filter=is_srt_outcome:open&default_orderby=name&filter_value=on&" %}> {{defect_open}} </a>
</dd>
<dt>InProgress =</dt>
<dd>
- <a href="{% url 'defects' %}?limit=25&page=1&orderby=-priority&filter=is_status:in_progress&default_orderby=name&filter_value=on&" %}> {{defect_inprogress}} </a>
+ <a href="{% url 'defects' %}?limit=25&page=1&orderby=-priority&filter=is_defect_status:in_progress&default_orderby=name&filter_value=on&" %}> {{defect_inprogress}} </a>
</dd>
<dt>P1 active =</dt>
<dd>
- <a href="{% url 'defects' %}?limit=25&page=1&orderby=-priority&filter=is_status:in_progress&default_orderby=name&filter_value=on&" %}> {{defect_p1}} </a>
+ <a href="{% url 'defects' %}?limit=25&page=1&orderby=-priority&filter=is_defect_priority:critical&default_orderby=name&filter_value=on&" %}> {{defect_p1}} </a>
</dd>
<dt>P2 active =</dt>
<dd>
- <a href="{% url 'defects' %}?limit=25&page=1&orderby=-priority&filter=is_status:in_progress&default_orderby=name&filter_value=on&" %}> {{defect_p2}} </a>
+ <a href="{% url 'defects' %}?limit=25&page=1&orderby=-priority&filter=is_defect_priority:high&default_orderby=name&filter_value=on&" %}> {{defect_p2}} </a>
</dd>
<dt>Packages: Affected=</dt>
diff --git a/lib/srtgui/templates/notifications-toastertable.html b/lib/srtgui/templates/notifications-toastertable.html
index dde76482..c9e572df 100755
--- a/lib/srtgui/templates/notifications-toastertable.html
+++ b/lib/srtgui/templates/notifications-toastertable.html
@@ -19,7 +19,7 @@
<ul class="breadcrumb" id="breadcrumb">
<li><a href="{% url 'landing' %}">Home</a></li><span class="divider">&rarr;</span>
<li><a href="{% url 'manage' %}">Management</a></li><span class="divider">&rarr;</span>
- <li>Pending To-do Notifications</li>
+ <li>Pending Notifications</li>
</ul>
</div>
</div>
diff --git a/lib/srtgui/templates/publish-cve-toastertable.html b/lib/srtgui/templates/publish-cve-toastertable.html
new file mode 100755
index 00000000..c46128cc
--- /dev/null
+++ b/lib/srtgui/templates/publish-cve-toastertable.html
@@ -0,0 +1,162 @@
+{% 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 %} Publish Table via CVEs {% endblock %}
+
+{% block pagecontent %}
+
+<div class="row">
+ <!-- Breadcrumbs -->
+ <div class="col-md-12">
+ <ul class="breadcrumb" id="breadcrumb">
+ <li><a href="{% url 'landing' %}">Home</a></li><span class="divider">&rarr;</span>
+ <li><a href="{% url 'manage' %}">Management</a></li><span class="divider">&rarr;</span>
+ <li><a href="{% url 'publish' %}">Publish</a></li><span class="divider">&rarr;</span>
+ <li>Publish Table via CVEs</li>
+ </ul>
+ </div>
+</div>
+
+<div > <!--class="form-inline" -->
+ <b><big>Actions: </big></b>
+ <button id="mark-new" class="btn btn-default" type="button">Mark New</button>
+ <button id="mark-modified" class="btn btn-default" type="button">Mark Updated</button>
+ <button id="unmark" class="btn btn-default" type="button">Unmark</button>
+</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>
+
+ <!-- Javascript support -->
+ <script>
+ var selected_notifyedit=false;
+
+ $(document).ready(function () {
+ var tableElt = $("#{{table_name}}");
+ var titleElt = $("[data-role='page-title']");
+
+ tableElt.on("table-done", function (e, total, tableParams) {
+ var title = "Publish Table via CVEs";
+
+ if (tableParams.search || tableParams.filter) {
+ if (total === 0) {
+ title = "Publish CVEs found";
+ }
+ else if (total > 0) {
+ title = total + " Publish CVE" + (total > 1 ? 's' : '') + " found";
+ }
+ }
+
+ titleElt.text(title);
+ });
+
+ 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;
+ } else if (('results_msg' in data) && ("" != data.results_msg)) {
+ alert("Results: " + data.results_msg);
+ }
+ // 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_publish'%}",
+ headers: { 'X-CSRFToken': $.cookie("csrftoken")},
+ success: onCommitAjaxSuccess,
+ error: onCommitAjaxError,
+ })
+ }
+
+ $('#mark-new').click(function(){
+ var cve_list=[];
+ $('#publishcvetable input').each(function(){
+ if ($(this).is(':checked')) {
+ cve_list.push($(this).prop('name'));
+ }
+ });
+ cve_list = cve_list.join(",");
+ if ("" == cve_list) {
+ alert("No CVE's were selected");
+ return;
+ }
+ postCommitAjaxRequest({
+ "action" : 'mark-new',
+ "cves" : cve_list,
+ });
+ });
+
+ $('#mark-modified').click(function(){
+ var cve_list=[];
+ $('#publishcvetable input').each(function(){
+ if ($(this).is(':checked')) {
+ cve_list.push($(this).prop('name'));
+ }
+ });
+ cve_list = cve_list.join(",");
+ if ("" == cve_list) {
+ alert("No CVE's were selected");
+ return;
+ }
+ postCommitAjaxRequest({
+ "action" : 'mark-modified',
+ "cves" : cve_list,
+ });
+ });
+
+ $('#unmark').click(function(){
+ var cve_list=[];
+ $('#publishcvetable input').each(function(){
+ if ($(this).is(':checked')) {
+ cve_list.push($(this).prop('name'));
+ }
+ });
+ cve_list = cve_list.join(",");
+ if ("" == cve_list) {
+ alert("No CVE's were selected");
+ return;
+ }
+ postCommitAjaxRequest({
+ "action" : 'unmark',
+ "cves" : cve_list,
+ });
+ });
+
+
+ }); <!-- $(document).ready() -->
+
+ </script>
+{% endblock %}
diff --git a/lib/srtgui/templates/publish-defect-toastertable.html b/lib/srtgui/templates/publish-defect-toastertable.html
new file mode 100755
index 00000000..c31e3b6a
--- /dev/null
+++ b/lib/srtgui/templates/publish-defect-toastertable.html
@@ -0,0 +1,168 @@
+{% 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 %} Publish Table via Defects {% endblock %}
+
+{% block pagecontent %}
+
+<div class="row">
+ <!-- Breadcrumbs -->
+ <div class="col-md-12">
+ <ul class="breadcrumb" id="breadcrumb">
+ <li><a href="{% url 'landing' %}">Home</a></li><span class="divider">&rarr;</span>
+ <li><a href="{% url 'manage' %}">Management</a></li><span class="divider">&rarr;</span>
+ <li><a href="{% url 'publish' %}">Publish</a></li><span class="divider">&rarr;</span>
+ <li>Publish Table via Defects</li>
+ </ul>
+ </div>
+</div>
+
+<div > <!--class="form-inline" -->
+ <b><big>Actions: </big></b>
+ <button id="mark-new" class="btn btn-default" type="button">Mark New</button>
+ <button id="mark-modified" class="btn btn-default" type="button">Mark Updated</button>
+ <button id="unmark" class="btn btn-default" type="button">Unmark</button>
+</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>
+
+<div id="table-loading">
+<h3><font color="blue">[ Table Loading... ]</font></h3>
+</div>
+
+ <!-- Javascript support -->
+ <script>
+ var selected_notifyedit=false;
+
+ $(document).ready(function () {
+ var tableElt = $("#{{table_name}}");
+ var titleElt = $("[data-role='page-title']");
+
+ $("#table-loading").slideDown();
+ tableElt.on("table-done", function (e, total, tableParams) {
+ var title = "Publish Table via Defects";
+
+ if (tableParams.search || tableParams.filter) {
+ if (total === 0) {
+ title = "Publish CVEs found";
+ }
+ else if (total > 0) {
+ title = total + " Publish CVEs via Defect" + (total > 1 ? 's' : '') + " found";
+ }
+ }
+
+ titleElt.text(title);
+ $("#table-loading").slideUp();
+ });
+
+ 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;
+ } else if (('results_msg' in data) && ("" != data.results_msg)) {
+ alert("Results: " + data.results_msg);
+ }
+ // 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_publish'%}",
+ headers: { 'X-CSRFToken': $.cookie("csrftoken")},
+ success: onCommitAjaxSuccess,
+ error: onCommitAjaxError,
+ })
+ }
+
+ $('#mark-new').click(function(){
+ var defect_list=[];
+ $('#publishdefecttable input').each(function(){
+ if ($(this).is(':checked')) {
+ defect_list.push($(this).prop('name'));
+ }
+ });
+ defect_list = defect_list.join(",");
+ if ("" == defect_list) {
+ alert("No Defects were selected");
+ return;
+ }
+ postCommitAjaxRequest({
+ "action" : 'mark-new',
+ "defects" : defect_list,
+ });
+ });
+
+ $('#mark-modified').click(function(){
+ var defect_list=[];
+ $('#publishdefecttable input').each(function(){
+ if ($(this).is(':checked')) {
+ defect_list.push($(this).prop('name'));
+ }
+ });
+ defect_list = defect_list.join(",");
+ if ("" == defect_list) {
+ alert("No Defects were selected");
+ return;
+ }
+ postCommitAjaxRequest({
+ "action" : 'mark-modified',
+ "defects" : defect_list,
+ });
+ });
+
+ $('#unmark').click(function(){
+ var defect_list=[];
+ $('#publishdefecttable input').each(function(){
+ if ($(this).is(':checked')) {
+ defect_list.push($(this).prop('name'));
+ }
+ });
+ defect_list = defect_list.join(",");
+ if ("" == defect_list) {
+ alert("No Defects were selected");
+ return;
+ }
+ postCommitAjaxRequest({
+ "action" : 'unmark',
+ "defects" : defect_list,
+ });
+ });
+
+
+ }); <!-- $(document).ready() -->
+
+ </script>
+{% endblock %}
diff --git a/lib/srtgui/templates/publish-list-toastertable.html b/lib/srtgui/templates/publish-list-toastertable.html
new file mode 100755
index 00000000..b5a88323
--- /dev/null
+++ b/lib/srtgui/templates/publish-list-toastertable.html
@@ -0,0 +1,162 @@
+{% 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 %} Publish Table {% endblock %}
+
+{% block pagecontent %}
+
+<div class="row">
+ <!-- Breadcrumbs -->
+ <div class="col-md-12">
+ <ul class="breadcrumb" id="breadcrumb">
+ <li><a href="{% url 'landing' %}">Home</a></li><span class="divider">&rarr;</span>
+ <li><a href="{% url 'manage' %}">Management</a></li><span class="divider">&rarr;</span>
+ <li><a href="{% url 'publish' %}">Publish</a></li><span class="divider">&rarr;</span>
+ <li>Publish Table</li>
+ </ul>
+ </div>
+</div>
+
+<div > <!--class="form-inline" -->
+ <b><big>Actions: </big></b>
+ <button id="mark-new" class="btn btn-default" type="button">Mark New</button>
+ <button id="mark-modified" class="btn btn-default" type="button">Mark Updated</button>
+ <button id="unmark" class="btn btn-default" type="button">Unmark</button>
+</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>
+
+ <!-- Javascript support -->
+ <script>
+ var selected_notifyedit=false;
+
+ $(document).ready(function () {
+ var tableElt = $("#{{table_name}}");
+ var titleElt = $("[data-role='page-title']");
+
+ tableElt.on("table-done", function (e, total, tableParams) {
+ var title = "Publish Table";
+
+ if (tableParams.search || tableParams.filter) {
+ if (total === 0) {
+ title = "Publish CVEs found";
+ }
+ else if (total > 0) {
+ title = total + " Publish CVE" + (total > 1 ? 's' : '') + " found";
+ }
+ }
+
+ titleElt.text(title);
+ });
+
+ 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;
+ } else if (('results_msg' in data) && ("" != data.results_msg)) {
+ alert("Results: " + data.results_msg);
+ }
+ // 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_publish'%}",
+ headers: { 'X-CSRFToken': $.cookie("csrftoken")},
+ success: onCommitAjaxSuccess,
+ error: onCommitAjaxError,
+ })
+ }
+
+ $('#mark-new').click(function(){
+ var cve_list=[];
+ $('#publishlisttable input').each(function(){
+ if ($(this).is(':checked')) {
+ cve_list.push($(this).prop('name'));
+ }
+ });
+ cve_list = cve_list.join(",");
+ if ("" == cve_list) {
+ alert("No CVE's were selected");
+ return;
+ }
+ postCommitAjaxRequest({
+ "action" : 'mark-new',
+ "cves" : cve_list,
+ });
+ });
+
+ $('#mark-modified').click(function(){
+ var cve_list=[];
+ $('#publishlisttable input').each(function(){
+ if ($(this).is(':checked')) {
+ cve_list.push($(this).prop('name'));
+ }
+ });
+ cve_list = cve_list.join(",");
+ if ("" == cve_list) {
+ alert("No CVE's were selected");
+ return;
+ }
+ postCommitAjaxRequest({
+ "action" : 'mark-modified',
+ "cves" : cve_list,
+ });
+ });
+
+ $('#unmark').click(function(){
+ var cve_list=[];
+ $('#publishlisttable input').each(function(){
+ if ($(this).is(':checked')) {
+ cve_list.push($(this).prop('name'));
+ }
+ });
+ cve_list = cve_list.join(",");
+ if ("" == cve_list) {
+ alert("No CVE's were selected");
+ return;
+ }
+ postCommitAjaxRequest({
+ "action" : 'unmark',
+ "cves" : cve_list,
+ });
+ });
+
+
+ }); <!-- $(document).ready() -->
+
+ </script>
+{% endblock %}
diff --git a/lib/srtgui/templates/publish.html b/lib/srtgui/templates/publish.html
index b1f3d83f..cf0f2294 100644
--- a/lib/srtgui/templates/publish.html
+++ b/lib/srtgui/templates/publish.html
@@ -13,32 +13,315 @@
<ul class="breadcrumb" id="breadcrumb">
<li><a href="{% url 'landing' %}">Home</a></li><span class="divider">&rarr;</span>
<li><a href="{% url 'manage' %}">Management</a></li><span class="divider">&rarr;</span>
- <li>Publish (Proposals)</li>
+ <li>Publish Report Management</li>
</ul>
</div>
</div>
-<h2> Manage Publish Requests</h2>
+<h2>Publish Report Management</h2>
<ul>
- <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>
+ <li>The SRTool supports exporting new and updated CVEs to external publishing tools</li>
</ul>
-<h2> Publishing Actions</h2>
+<hr>
+
+<h2>Publish Via Database Snapshots</h2>
+<h3> On Demand</h3>
<ul>
- <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>
+ <li>This extracts the changes from a 'base' database backup snapshot to more recent 'top' snapshot</li>
+ <li>The 'start' and 'stop' dates can extract a subset of those changes. Normally they are set to the 'base' and 'top' dates</li>
</ul>
+<div style="padding-left:30px;">
+ <div>
+ <label> Start Snapshot: </label>
+ <select id="snap_date_base">
+ {% for snap in snapshot_list %}
+ <option value="{{snap.date}}" {% if snap_start_index == snap.index %}selected{% endif %}>
+ ({{snap.mode}}) {{snap.date}} {{snap.time}} | {{snap.day}}
+ </option>
+ {% endfor %}
+ </select>
+ </div>
+ <div>
+ <label> Stop Snapshot: </label>
+ <select id="snap_date_top">
+ {% for snap in snapshot_list %}
+ <option value="{{snap.date}}" {% if snap_stop_index == snap.index %}selected{% endif %}>
+ ({{snap.mode}}) {{snap.date}} {{snap.time}} | {{snap.day}}
+ </option>
+ {% endfor %}
+ </select>
+ </div>
+ <div>
+ Start Date: <input type="text" id="snap_date_start" value="{{snap_date_start}}">&nbsp;&nbsp;
+ Stop Date: <input type="text" id="snap_date_stop" value="{{snap_date_stop}}">&nbsp;&nbsp;
+ <I>(Format: yyyy-mm-dd)</I>
+ </div>
+<br>
+</div>
+
+<div>
+ <span style="padding-left:30px;"><button id="export-snapshot" class="btn btn-default" type="button">Generate</button></span>
+ <!--<button type="submit" name="action" value="export-snapshot">Export</button> -->
+ <span id="export-snapshot-text">Generate the publish table on-demand (using snapshots)</span>
+ <span id="generating-report" hidden style="color:red"><I>... Generating the report - this will take a few minutes ...</I></span>
+</div>
+<br>
+
+<form method="POST"> {% csrf_token %}
+<h3>Automatic (Under Development)</h3>
+<div style="padding-left: 25px;">
+ <label> Frequency: </label>
+ <select id="snap_frequency">
+ {% for snap in snapshot_frequency_list %}
+ <option value="{{snap}}" {% if snap == snap_frequency_select %}selected{% endif %}>
+ {{snap}}
+ </option>
+ {% endfor %}
+ </select>
+ <span style="padding-left:30px;"><button id="export-snapshot" class="btn btn-default" type="button" disabled>Save</button></span>
+ <!--<button type="submit" name="action" value="export-snapshot">Export</button> -->
+ Save the automatic publishing frequency
+</div>
+</form>
+
+<h3>Generated Reports</h3>
+<div style="padding-left: 25px;">
+ <table class="table table-striped table-condensed" data-testid="vuln-hyperlinks-table">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Size</th>
+ <th>Date</th>
+ <th>Manage</th>
+ </tr>
+ </thead>
+ {% if generated_report_list %}
+ {% for report in generated_report_list %}
+ <tr>
+ <td>{{report.name}}</td>
+ <td>{{report.size}}</td>
+ <td>{{report.date}}</td>
+ <td>
+ <span id="attachment_entry_'+{{report.name}}+'" class="js-config-var-name"></span>
+ <form id="downloadbanner-{{forloop.counter}}" enctype="multipart/form-data" method="post" >{% csrf_token %}
+ <input type="hidden" id="action" name="action" value="download">
+ <input type="hidden" id="report_id" name="report_name" value={{report.name}}>
+ <span class="glyphicon glyphicon-download-alt submit-downloadreport" id="report_download_'+{{report.name}}+'" x-data="{{forloop.counter}}"></span>
+ {% if request.user.is_creator %}
+ <span class="glyphicon glyphicon-trash trash-report" id="report_trash_'+{{report.name}}+'" x-data="{{report.name}}"></span>
+ {% endif %}
+ </form>
+ </td>
+ </tr>
+ {% endfor %}
+ {% else %}
+ <tr>
+ <td>No report files found</td>
+ </tr>
+ {% endif %}
+ </table>
+ (last report = {{snap_last_calc}})
+</div>
+
+<hr>
+
+<form method="POST"> {% csrf_token %}
+<h2>Publish Via History Tables (Under development)</h2>
+<ul>
+ <li>These tools can be used to (a) gather the candidate CVEs, (b) review and edit the list if needed, (c) generate the report when ready</li>
+ <li>The user can explicitly include and exclude CVEs from the "New" list and the "Updated" list, in case the automatic caltulations need adjustment</li>
+ <li>These mark-ups are inserted into the respective CVE's history at a mid-point date of the period, so they are both persistent and period-specific</li>
+ <li>The user can clear the markups from the given period and start over, but this will not affect any other period</li>
+</ul>
+<h3> Publish Preparation</h3>
+<ul>
+ <div>
+ Start Date: <input type="text" name="date_start" value="{{date_start}}">&nbsp;&nbsp;
+ Stop Date: <input type="text" name="date_stop" value="{{date_stop}}">
+ </div>
+ <br>
+ <div>
+ Product filter:
+ <select name="product-filter" id="select-product-filter">
+ <option value="0">WR Linux Suported Products</option>
+ </select>
+ </div>
+ <br>
+ <div>
+ <button type="submit" name="action" value="recalculate">Recalculate publish table</button>
+ Gather the items for this period to be published from SRTool, with user changes (last done {{last_calc}})
+ </div>
+ <br>
+ <div>
+ <button type="submit" name="action" value="reset">Reset user edits, Recalculate</button>
+ Remove the user changes for this period, recalculate the table
+ </div>
+</ul>
+<h3> Publish Preview and Modifications</h3>
+<ul>
+ <div>
+ <button type="submit" name="action" value="view">View the publish table</button>
+ View the publish table, prune entries
+ </div>
+ <br>
+ <div>
+ <button type="submit" name="action" value="add-cve">Add via CVEs</button>
+ Add recent CVEs to the table
+ </div>
+ <br>
+ <div>
+ <button type="submit" name="action" value="add-defect">Add via defects</button>
+ Add CVEs of recent defects to the table
+ </div>
+ <br>
+</ul>
+<h3> Publish the Report</h3>
+<ul>
+ <div>
+ <button type="submit" name="action" value="export">Export</button>
+ Export the publish table (using history)
+ </div>
+ <br>
+</ul>
+</form>
+
+<script>
+ var selected_newcomment=false;
+
+ $(document).ready(function() {
+
+ function onCommitAjaxSuccess(data, textstatus) {
+ document.getElementById("export-snapshot").disabled = false;
+ /* document.getElementById("download-snapshot").disabled = false;*/
+ document.getElementById("export-snapshot-text").innerText = "Generate the publish table on-demand (using snapshots)";
+ document.getElementById("generating-report").style.display = "block";
+ if (window.console && window.console.log) {
+ console.log("XHR returned:", data, "(" + textstatus + ")");
+ } else {
+ alert("NO CONSOLE:\n");
+ return;
+ }
+ if (data.error != "ok") {
+ alert("error on request:\n" + data.error);
+ return;
+ }
+ // reload the page with the updated tables
+ location.reload(true);
+ }
+
+ function onCommitAjaxError(jqXHR, textstatus, error) {
+ console.log("ERROR:"+error+"|"+textstatus);
+ alert("XHR errored1:\n" + error + "\n(" + textstatus + ")");
+ document.getElementById("export-snapshot").disabled = false;
+ document.getElementById("export-snapshot-text").innerText = "Generate the publish table on-demand (using snapshots)";
+ /* document.getElementById("download-snapshot").disabled = false; */
+ document.getElementById("generating-report").style.display = "block";
+ }
+
+ /* ensure cookie exists {% csrf_token %} */
+ function postCommitAjaxRequest(reqdata) {
+ var ajax = $.ajax({
+ type:"POST",
+ data: reqdata,
+ url:"{% url 'xhr_publish' %}",
+ headers: { 'X-CSRFToken': $.cookie("csrftoken")},
+ success: onCommitAjaxSuccess,
+ error: onCommitAjaxError,
+ });
+ }
+
+ $("#snap_date_base").change(function(){
+ snap_date_base = $("#snap_date_base").val();
+ snap_date_top = $("#snap_date_top").val();
+ if (snap_date_base > snap_date_top) {
+ $("#snap_date_base").val(snap_date_top);
+ $("#snap_date_top").val(snap_date_base);
+ $("#snap_date_start").val(snap_date_top);
+ $("#snap_date_stop").val(snap_date_base);
+ } else {
+ snap_date_start = $("#snap_date_start").val();
+ snap_date_stop = $("#snap_date_stop").val();
+ $("#snap_date_start").val(snap_date_base);
+ if (snap_date_stop < snap_date_base) {
+ $("#snap_date_stop").val(snap_date_top);
+ }
+ }
+ });
+
+ $("#snap_date_top").change(function(){
+ snap_date_base = $("#snap_date_base").val();
+ snap_date_top = $("#snap_date_top").val();
+ if (snap_date_base > snap_date_top) {
+ $("#snap_date_base").val(snap_date_top);
+ $("#snap_date_top").val(snap_date_base);
+ $("#snap_date_start").val(snap_date_top);
+ $("#snap_date_stop").val(snap_date_base);
+ } else {
+ snap_date_start = $("#snap_date_start").val();
+ snap_date_stop = $("#snap_date_stop").val();
+ if (snap_date_start > snap_date_top) {
+ $("#snap_date_start").val(snap_date_base);
+ }
+ $("#snap_date_stop").val(snap_date_top);
+ }
+ });
+
+ $('#export-snapshot').click(function(){
+ snap_date_base = $("#snap_date_base").val();
+ snap_date_top = $("#snap_date_top").val();
+ snap_date_start = $("#snap_date_start").val();
+ snap_date_stop = $("#snap_date_stop").val();
+ if (snap_date_start > snap_date_stop) {
+ alert("Error: the start date is after the stop date");
+ return;
+ }
+ if (snap_date_start < snap_date_base) {
+ alert("Error: the start date is before the snapshot base date");
+ return;
+ }
+ if (snap_date_stop > snap_date_top) {
+ alert("Error: the stop date is after the snapshot top date");
+ return;
+ }
+ var result = confirm("Generate the report? This will take several minutes.");
+ if (result){
+ document.getElementById("export-snapshot").disabled = true;
+ document.getElementById("export-snapshot-text").innerText = "... Generating the report - this will take a few minutes ...";
+
+ /* document.getElementById("download-snapshot").disabled = true; */
+ document.getElementById("generating-report").style.display = "none";
+ postCommitAjaxRequest({
+ "action" : 'export-snapshot',
+ "snap_date_base" : snap_date_base,
+ "snap_date_top" : snap_date_top,
+ "snap_date_start" : snap_date_start,
+ "snap_date_stop" : snap_date_stop
+ });
+ }
+ });
+
+
+ /* Manage report files */
+
+ $('.submit-downloadreport').click(function() {
+ $("#downloadbanner-"+this.getAttribute("x-data")).submit();
+ });
+
+ $('.trash-report').click(function() {
+ var result = confirm("Are you sure?");
+ if (result){
+ postCommitAjaxRequest({
+ "action" : 'submit-trashreport',
+ "report_name" : $(this).attr('x-data'),
+ });
+ }
+ });
+
+
+
+ });
+</script>
+
{% endblock %}
diff --git a/lib/srtgui/templates/report.html b/lib/srtgui/templates/report.html
index d4d27f76..4c2b2450 100644
--- a/lib/srtgui/templates/report.html
+++ b/lib/srtgui/templates/report.html
@@ -13,10 +13,16 @@
</div>
</div>
- <form method="POST">{% csrf_token %}
+ <form method="POST"> {% csrf_token %}
<input type="hidden" name="parent_page" value="{{parent_page}}">
<input type="hidden" name="record_list" value="{{record_list}}">
+ {% if error_message %}
+ <br>
+ <font size="3" color="red">{{error_message}}</font>
+ <br>
+ {% endif %}
+
{% if report_type_list %}
<hr>
Report Type:<br>
@@ -53,6 +59,12 @@
<hr>
{% endif %}
+ {% if report_date_list %}
+ Date Range (mm/dd/yyyy):<br>
+ {{report_date_list|safe}}
+ <hr>
+ {% endif %}
+
{% if report_custom_list %}
Page Specific Settings:<br>
{{report_custom_list|safe}}
@@ -68,6 +80,31 @@
</form>
-<br>
+ <br>
+ <!-- <input type="submit" id="submit-report-button" class="btn btn-primary btn-lg" value="Generate and Download Report"/> -->
+ <!-- <button type="button" id="submit-report-button" class="btn btn-primary btn-lg"> Generate and Download Report </button> -->
+ <!-- <button id="select-these" class="btn btn-default" type="button">Select these</button> -->
+
+ <!-- Javascript support -->
+ <script>
+
+ $(document).ready(function() {
+
+ /* Handle the post button */
+ $('#test-submit-report-button').click(function(){
+ document.getElementById("submit-report-button").innerText = " ... working ... ";
+ data = {
+ "action" : 'foo',
+ "pub" : $('#bar').is(':checked') ? "yes" : "no",
+ }
+
+ $.post("wr/report/management", data, function(data, status){
+ alert("Data: " + data + "\nStatus: " + status);
+ });
+ });
+
+ });
+ </script>
+
{% endblock %}
diff --git a/lib/srtgui/templates/srtool_metadata_include.html b/lib/srtgui/templates/srtool_metadata_include.html
index 7471f0f9..eb83c05f 100755
--- a/lib/srtgui/templates/srtool_metadata_include.html
+++ b/lib/srtgui/templates/srtool_metadata_include.html
@@ -9,15 +9,12 @@
<i>Status:</i> {{object.get_status_text}},&nbsp;&nbsp;
{% if default_category == "CVE" %}
{% if request.user.is_creator %}
- <i>Publish</i> = {{object.get_publish_text}}, <i>Publish Date</i> = {{object.publish_date}}
+ <i>Publish</i> = {{object.get_publish_text}}, <i>Publish Date</i> = {{object.publish_date}}, <i>Acknowledge Date</i> = {{object.acknowledge_date|date:'Y-m-d'}}, <i>Initial Release</i> = {{object.publishedDate}}, <i>Last Modified</i> = {{object.lastModifiedDate}}
<!--<a class="btn btn-default navbar-btn " id="login-button" href="">Publish Now</a> -->
{% else %}
<i>Publish = {{object.get_publish_text}}</i>
{% endif %}
</LI>
- <LI>
- <i>Packages:</i> {{object.packages}}
- </LI>
{% else %}
<i>Outcome:</i> {{object.get_outcome_text}}
<p>
@@ -26,9 +23,17 @@
<i>Public Notes:</i> {{object.comments}}
</LI>
{% if request.user.is_creator %}
+ <LI>
+ <i>Private Notes:</i> {{object.comments_private}}
+ </LI>
+ {% endif %}
<LI>
- <i>Private Notes:</i> {{object.comments_private}}
+ <i>Tags:</i> {{object.tags}}
</LI>
+ {% if default_category == "CVE" %}
+ <LI>
+ <i>Affected Components:</i> {{object.packages}}
+ </LI>
{% endif %}
</UL>
</fieldset>
@@ -42,10 +47,10 @@
<p><i>Priority</i> =
<select name="Priority" id="select-priority-state">
<option value="0" {% if 0 == object.priority %}selected{% endif %}>Undefined</option>
- <option value="1" {% if 1 == object.priority %}selected{% endif %}>Minor</option>
- <option value="2" {% if 2 == object.priority %}selected{% endif %}>Low</option>
- <option value="3" {% if 3 == object.priority %}selected{% endif %}>Medium</option>
- <option value="4" {% if 4 == object.priority %}selected{% endif %}>High</option>
+ <option value="1" {% if 1 == object.priority %}selected{% endif %}>Low</option>
+ <option value="2" {% if 2 == object.priority %}selected{% endif %}>Medium</option>
+ <option value="3" {% if 3 == object.priority %}selected{% endif %}>High</option>
+ <option value="4" {% if 4 == object.priority %}selected{% endif %}>Critical</option>
</select>
&nbsp;&nbsp;
<i>Status</i> =
@@ -56,6 +61,10 @@
<option value="3" {% if 3 == object.status %}selected{% endif %}>Investigate</option>
<option value="4" {% if 4 == object.status %}selected{% endif %}>Vulnerable</option>
<option value="5" {% if 5 == object.status %}selected{% endif %}>Not Vulnerable</option>
+ <option value="6" {% if 6 == object.status %}selected{% endif %}>(New)</option>
+ <option value="7" {% if 7 == object.status %}selected{% endif %}>(Investigate)</option>
+ <option value="8" {% if 8 == object.status %}selected{% endif %}>(Vulnerable)</option>
+ <option value="9" {% if 9 == object.status %}selected{% endif %}>(Not Vulnerable)</option>
</select>
<p>
{% if default_category == "CVE" %}
@@ -78,9 +87,14 @@
<option value="3" {% if 3 == object.outcome_state %}selected{% endif %}>Closed (Won't Fix)</option>
</select>
{% endif %}
- <p>Note: <input type="text" placeholder="Edit Note" id="text-note" size="80" value="{{object.comments}}"></p>
+ <p>Comments: <input type="text" placeholder="Edit comments" id="text-note" size="80" value="{{object.comments}}"></p>
{% if request.user.is_creator %}
- <p>Private Note: <input type="text" placeholder="Edit Private Note" id="text-private-note" size="80" value="{{object.comments_private}}"></p>
+ <p>Private Comments: <input type="text" placeholder="Edit private comments" id="text-private-note" size="80" value="{{object.comments_private}}"></p>
+ {% endif %}
+ <p>Tags: <input type="text" placeholder="Edit tags" id="text-tags" size="80" value="{{object.tags}}"></p>
+ {% if default_category == "CVE" %}
+ <p>Affected Components: <input type="text" placeholder="Edit affected components" id="text-affected-components" size="80" value="{{object.packages}}"></p>
+ <i>Acknowledge Date</i> = <input type="text" placeholder="Acknowledge Date" id="text-acknowledge-date" size="40" value="{{object.acknowledge_date|date:'Y-m-d'}}"> (YYYY-MM-DD, or empty string for None)<p>
{% endif %}
<p><p>
</fieldset>
diff --git a/lib/srtgui/templates/toastertable.html b/lib/srtgui/templates/toastertable.html
index 6882b394..99eb01e2 100644
--- a/lib/srtgui/templates/toastertable.html
+++ b/lib/srtgui/templates/toastertable.html
@@ -69,6 +69,7 @@
</div>
</div>
<button class="btn btn-default" id="search-submit-{{table_name}}" >Search</button>
+ <span class="glyphicon glyphicon-question-sign get-help" title="Default is to 'and' terms. Use 'OR' to 'or' terms. Use '-' to exclude terms. Example:abc OR 'def ghi' AND -jkl"></span>
</form>
<form class="navbar-form navbar-right">
<div clas="form-group">
diff --git a/lib/srtgui/templates/vulnerability.html b/lib/srtgui/templates/vulnerability.html
index 35b2c0e6..9290a1ef 100644
--- a/lib/srtgui/templates/vulnerability.html
+++ b/lib/srtgui/templates/vulnerability.html
@@ -148,26 +148,41 @@
</tr>
</thead>
- {% if object.vulnerability_investigation.all %}
- {% for investigation in object.vulnerability_investigation.all %}
+ <table class="table table-striped table-condensed" data-testid="vuln-hyperlinks-table">
+ <thead>
+ <tr>
+ <th>Product Name</th>
+ <th>Investigation</th>
+ <th>Status</th>
+ <th>Outcome</th>
+ <th>Defect</th>
+ <th>Release Version</th>
+ {% if request.user.is_creator %}
+ <th>Manage</th>
+ {% endif %}
+ </tr>
+ </thead>
+
+ {% if object.investigation_list %}
+ {% for v2i in object.investigation_list %}
<tr>
- <td><a href="{% url 'product' investigation.product.id %}">{{ investigation.product.long_name }}<a></td>
- <td><a href="{% url 'investigation' investigation.id %}">{{ investigation.name }}<a></td>
- <td>{{ investigation.get_status_text }}</td>
- <td>{{ investigation.get_outcome_text }}</td>
+ <td><a href="{% url 'product' v2i.investigation.product.id %}">{{ v2i.investigation.product.long_name }}<a></td>
+ <td><a href="{% url 'investigation' v2i.investigation.id %}">{{ v2i.investigation.name }}<a></td>
+ <td>{{ v2i.investigation.get_status_text }}</td>
+ <td>{{ v2i.investigation.get_outcome_text }}</td>
<td>
- {% for ij in investigation.investigation_to_defect.all %}
+ {% for ij in v2i.investigation.investigation_to_defect.all %}
{% if not forloop.first %}| {% endif %}<a href="{% url 'defect' ij.defect.id %}">{{ij.defect.name}} </a>
{% endfor %}
</td>
<td>
- {% for ij in investigation.investigation_to_defect.all %}
+ {% for ij in v2i.investigation.investigation_to_defect.all %}
{% if not forloop.first %}| {% endif %}<a href="{% url 'defect' ij.defect.id %}">{{ij.defect.release_version}} </a>
{% endfor %}
</td>
{% if request.user.is_creator %}
<td>
- <span class="glyphicon glyphicon-trash trash-investigation" id="affected_trash_'+{{investigation.id}}+'" x-data="{{investigation.id}}"></span>
+ <span class="glyphicon glyphicon-trash trash-investigation" id="affected_trash_'+{{v2i.investigation.id}}+'" x-data="{{v2i.investigation.id}}"></span>
</td>
{% endif %}
</tr>
@@ -268,10 +283,10 @@
<td>{{ u.author }}</td>
<td>
<span id="attachment_entry_'+{{u.id}}+'" class="js-config-var-name"></span>
- <form id="downloadbanner" enctype="multipart/form-data" method="post" >{% csrf_token %}
+ <form id="downloadbanner-{{forloop.counter}}" enctype="multipart/form-data" method="post" >{% csrf_token %}
<input type="hidden" id="action" name="action" value="download">
<input type="hidden" id="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>
+ <span class="glyphicon glyphicon-download-alt submit-downloadattachment" id="attachment_download_'+{{u.id}}+'" x-data="{{forloop.counter}}"></span>
{% if request.user.is_creator %}
<span class="glyphicon glyphicon-trash trash-attachment" id="attachment_trash_'+{{u.id}}+'" x-data="{{u.id}}"></span>
{% endif %}
@@ -440,6 +455,9 @@
</table>
</div>
+<HR ALIGN="center" WIDTH="100%">
+Created={{object.srt_created}} Updated={{object.srt_updated}}
+
<!-- Javascript support -->
<script>
var selected_addrelatedproduct=false;
@@ -583,7 +601,7 @@
});
$('.submit-downloadattachment').click(function() {
- $("#downloadbanner").submit();
+ $("#downloadbanner-"+this.getAttribute("x-data")).submit();
});
$('.trash-attachment').click(function() {
@@ -694,6 +712,7 @@
$('#submit-quickedit').click(function(){
var note=$('#text-note').val().trim()
var private_note=$('#text-private-note').val().trim()
+ var tags=$('#text-tags').val().trim();
var priority=$('#select-priority-state').val();
var status=$('#select-status-state').val();
var outcome=$('#select-outcome-state').val();
@@ -701,6 +720,7 @@
"action" : 'submit-quickedit',
"note" : note,
"private_note" : private_note,
+ "tags" : tags,
"status" : status,
"outcome" : outcome,
"priority" : priority,
diff --git a/lib/srtgui/templatetags/projecttags.py b/lib/srtgui/templatetags/projecttags.py
index d7bc5319..0c5efc29 100644
--- a/lib/srtgui/templatetags/projecttags.py
+++ b/lib/srtgui/templatetags/projecttags.py
@@ -275,6 +275,24 @@ def get_dict_value(dictionary, key):
return ''
@register.filter
+def get_strdict_value(dictionary_str, key):
+ """ return the value of a dictionary key
+ where the dictionary is in string form
+ """
+ try:
+ dictionary = JsonLib.loads(dictionary_str)
+ return dictionary[key]
+ except (KeyError, IndexError):
+ return ''
+
+def get_tag_key(tag,key,default=None):
+ d = json.loads(tag)
+ if key in d:
+ return d[key]
+ return default
+
+
+@register.filter
def is_shaid(text):
""" return True if text length is 40 characters and all hex-digits
"""
diff --git a/lib/srtgui/urls.py b/lib/srtgui/urls.py
index 26c484d8..a4947c51 100644
--- a/lib/srtgui/urls.py
+++ b/lib/srtgui/urls.py
@@ -88,10 +88,19 @@ urlpatterns = [
tables.PackageFilterDetailTable.as_view(template_name="package-filter-detail-toastertable.html"),
name='package-filter-detail'),
+ url(r'^publish-list/$',
+ tables.PublishListTable.as_view(template_name="publish-list-toastertable.html"),
+ name='publish-list'),
+ url(r'^publish-cve/$',
+ tables.PublishCveTable.as_view(template_name="publish-cve-toastertable.html"),
+ name='publish-cve'),
+ url(r'^publish-defect/$',
+ tables.PublishDefectTable.as_view(template_name="publish-defect-toastertable.html"),
+ name='publish-defect'),
+
url(r'^select-publish/$',
tables.SelectPublishTable.as_view(template_name="publish-select-toastertable.html"),
name='select-publish'),
-
url(r'^update-published/$',
tables.UpdatePublishedTable.as_view(template_name="published-select-toastertable.html"),
name='update-published'),
@@ -120,6 +129,8 @@ urlpatterns = [
url(r'^xhr_packages/$', views.xhr_packages,
name='xhr_packages'),
+ url(r'^xhr_publish/$', views.xhr_publish,
+ name='xhr_publish'),
url(r'^manage/$', views.management, name='manage'),
url(r'^manage_cpes/$',
@@ -137,6 +148,19 @@ urlpatterns = [
name='sources'),
url(r'^users/$', views.users, name='users'),
+ url(r'^maintenance/$', views.maintenance, name='maintenance'),
+ url(r'^history_cve/$',
+ tables.HistoryCveTable.as_view(template_name="history-cve-toastertable.html"),
+ name='history_cve'),
+ url(r'^history_vulnerability/$',
+ tables.HistoryVulnerabilityTable.as_view(template_name="history-vulnerability-toastertable.html"),
+ name='history_vulnerability'),
+ url(r'^history_investigation/$',
+ tables.HistoryInvestigationTable.as_view(template_name="history-investigation-toastertable.html"),
+ name='history_investigation'),
+ url(r'^history_defect/$',
+ tables.HistoryDefectTable.as_view(template_name="history-defect-toastertable.html"),
+ name='history_defect'),
url(r'^guided_tour/$', views.guided_tour, name='guided_tour'),
diff --git a/lib/srtgui/views.py b/lib/srtgui/views.py
index d87dca38..79bf7b17 100644
--- a/lib/srtgui/views.py
+++ b/lib/srtgui/views.py
@@ -37,13 +37,15 @@ from orm.models import Investigation, InvestigationHistory, InvestigationToDefec
from orm.models import SrtSetting, Product
from orm.models import Package
from orm.models import DataSource
-from orm.models import Defect, PublishPending
+from orm.models import Defect, DefectHistory, PublishPending, PublishSet
from orm.models import Notify, NotifyAccess, NotifyCategories
+from orm.models import SRTool, Update
from users.models import SrtUser, UserSafe
from srtgui.reports import ReportManager
from srtgui.api import readCveDetails, writeCveDetails, summaryCveDetails, execute_process
+from srtgui.api import publishCalculate, publishReset, publishMarkNew, publishMarkModified, publishMarkNone
from django.urls import reverse, resolve
from django.core.paginator import EmptyPage, PageNotAnInteger
@@ -59,6 +61,10 @@ logger = logging.getLogger("srt")
# quick development/debugging support
from srtgui.api import _log
+#
+# ================= Helper Routines ============================================
+#
+
def get_name_sort(cve_name):
try:
a = cve_name.split('-')
@@ -67,6 +73,10 @@ def get_name_sort(cve_name):
cve_name_sort = cve_name
return cve_name_sort
+#
+# ================= Page Helper Routines ============================================
+#
+
class MimeTypeFinder(object):
# setting this to False enables additional non-standard mimetypes
# to be included in the guess
@@ -450,10 +460,10 @@ def management(request):
return redirect(landing)
# Keep it simple now, later use Q sets
- defect_open = Defect.objects.filter(status=Defect.OPEN)
- defects_inprogress = Defect.objects.filter(status=Defect.IN_PROGRESS)
- defect_p1 = defect_open.filter(priority=Defect.HIGH).count() + defects_inprogress.filter(priority=Defect.HIGH).count()
- defect_p2 = defect_open.filter(priority=Defect.MEDIUM).count() + defects_inprogress.filter(priority=Defect.MEDIUM).count()
+ defect_open = Defect.objects.filter(status=Defect.DEFECT_STATUS_OPEN)
+ defects_inprogress = Defect.objects.filter(status=Defect.DEFECT_STATUS_IN_PROGRESS)
+ defect_p1 = defect_open.filter(priority=Defect.CRITICAL).count() + defects_inprogress.filter(priority=Defect.CRITICAL).count()
+ defect_p2 = defect_open.filter(priority=Defect.HIGH).count() + defects_inprogress.filter(priority=Defect.HIGH).count()
defect_open = defect_open.count()
defects_inprogress = defects_inprogress.count()
@@ -467,15 +477,15 @@ def management(request):
'vulnerability_total' : Vulnerability.objects.all().count(),
'vulnerability_open' : Vulnerability.objects.filter(outcome=Vulnerability.OPEN).count(),
+ 'vulnerability_critical' : Vulnerability.objects.filter(outcome=Vulnerability.OPEN).filter(priority=Vulnerability.CRITICAL).count(),
'vulnerability_high' : Vulnerability.objects.filter(outcome=Vulnerability.OPEN).filter(priority=Vulnerability.HIGH).count(),
'vulnerability_medium' : Vulnerability.objects.filter(outcome=Vulnerability.OPEN).filter(priority=Vulnerability.MEDIUM).count(),
- 'vulnerability_low' : Vulnerability.objects.filter(outcome=Vulnerability.OPEN).filter(priority=Vulnerability.HIGH).count(),
'investigation_total' : Investigation.objects.all().count(),
'investigation_open' : Investigation.objects.filter(outcome=Investigation.OPEN).count(),
+ 'investigation_critical' : Investigation.objects.filter(outcome=Investigation.OPEN).filter(priority=Investigation.CRITICAL).count(),
'investigation_high' : Investigation.objects.filter(outcome=Investigation.OPEN).filter(priority=Investigation.HIGH).count(),
'investigation_medium' : Investigation.objects.filter(outcome=Investigation.OPEN).filter(priority=Investigation.MEDIUM).count(),
- 'investigation_low' : Investigation.objects.filter(outcome=Investigation.OPEN).filter(priority=Investigation.HIGH).count(),
'defect_total' : Defect.objects.all().count(),
'defect_open' : defect_open,
@@ -487,6 +497,20 @@ def management(request):
}
return render(request, 'management.html', context)
+def maintenance(request):
+ # does this user have permission to see this record?
+ if not UserSafe.is_creator(request.user):
+ return redirect(landing)
+
+ context = {
+ 'history_cve_total' : CveHistory.objects.all().count(),
+ 'history_vulnerability_total' : VulnerabilityHistory.objects.all().count(),
+ 'history_investigation_total' : InvestigationHistory.objects.all().count(),
+ 'defect_investigation_total' : DefectHistory.objects.all().count(),
+ }
+ return render(request, 'maintenance.html', context)
+
+
def cve(request, cve_pk, active_tab="1"):
if request.method == "GET":
template = "cve.html"
@@ -520,23 +544,31 @@ def cve(request, cve_pk, active_tab="1"):
cve_index = ord('1')
is_edit = ('Edit' == active_tab)
- # Prepend summary page?
+ # Fetch source tabs list
cve_sources = CveSource.objects.filter(cve=cve_object.id).order_by('datasource__key')
- if True or (1 < len(cve_sources)):
- tab_states[chr(cve_index)] = ''
- cveDetails,cve_html = summaryCveDetails(cve_object,cve_sources)
- cve_list_table.append([cveDetails,tab_states[chr(cve_index)],'Summary',cve_html])
- cve_index += 1
+ # Always pre-pend a summary page
+ tab_states[chr(cve_index)] = 'active'
+ cveDetails,cve_html = summaryCveDetails(cve_object,cve_sources)
+ cve_list_table.append([cveDetails,tab_states[chr(cve_index)],'Summary',cve_html])
+ cve_index += 1
# Add the source/edit tabs
- for cs in cve_sources:
+ for i in range(len(cve_sources)):
+ if (i < (len(cve_sources)-1)) and (cve_sources[i].datasource.source == cve_sources[i+1].datasource.source):
+ # Insure one source per vendor where the highest key wins (e.g. NIST Modified)
+ continue
+ pass
+
+ cs = cve_sources[i]
if active_tab == cs.datasource.name:
active_tab = chr(cve_index)
if ('Edit' == active_tab) and ('Local' == cs.datasource.name):
- tab_states[chr(cve_index)] = 'active'
+ #tab_states[chr(cve_index)] = 'active'
+ tab_states[chr(cve_index)] = ''
cve_list_table.append([readCveDetails(cve_object,cs.datasource),tab_states[chr(cve_index)],'Edit',{}])
else:
- tab_states[chr(cve_index)] = 'active' if (active_tab == chr(cve_index)) else ''
+ tab_states[chr(cve_index)] = ''
+ #tab_states[chr(cve_index)] = 'active' if (active_tab == chr(cve_index)) else ''
cve_list_table.append([readCveDetails(cve_object,cs.datasource),tab_states[chr(cve_index)],cs.datasource.name,{}])
cve_index += 1
if 0 == len(cve_sources):
@@ -552,11 +584,6 @@ def cve(request, cve_pk, active_tab="1"):
tab_states['1'] = 'active'
cve_list_table[0][1] = 'active'
-
- # cve_summary = copy.copy(cve_object)
- # cve_summary_detail = copy.copy(cve_object_detail)
- # cve_summary.source = 'Summary'
- #
context = {
'object' : cve_object,
'cve_list_table' : cve_list_table,
@@ -583,7 +610,7 @@ def cve(request, cve_pk, active_tab="1"):
# Is this not a save?
if not request.POST.get('cve-edit','').startswith('Save'):
- return redirect(cve, cve_object.id, "Local")
+ return redirect(cve, cve_object.id, "Summary")
# does this user have permission to see this record?
if (not cve_object.public) and (not UserSafe.is_admin(request.user)):
@@ -594,7 +621,7 @@ def cve(request, cve_pk, active_tab="1"):
writeCveDetails(cve_object.name,request)
# show the results
- return redirect(cve, cve_object.id, "Local")
+ return redirect(cve, cve_object.id, "Summary")
def cve_edit(request, cve_pk):
_log("CVE_EDIT1(%s):" % cve_pk)
@@ -677,12 +704,14 @@ def vulnerability(request, vulnerability_pk):
_log("EXPORT_POST:'fileupload' does not exist: %s" % e)
try:
- with open(path + "/" + file.name, 'xb+') as destination:
+ local_file_path = path + "/" + file.name
+ with open(local_file_path, 'xb+') as destination:
for line in file:
destination.write(line)
username = UserSafe.user_name(request.user)
- 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=username)
+ VulnerabilityUploads.objects.get_or_create(vulnerability_id=vulnerability_object.id, description=description, path=local_file_path, size=file.size, date=datetime.today().strftime('%Y-%m-%d'), author=username)
+ VulnerabilityHistory.objects.create(vulnerability_id=vulnerability_object.id, comment=Update.ATTACH_DOC % file.name, date=datetime.now().strftime(SRTool.DATE_FORMAT), author=username)
except Exception as e:
_log("EXPORT_POST:FILE ALREADY EXISTS: %s" % e)
return redirect(vulnerability,vulnerability_pk)
@@ -722,16 +751,32 @@ def investigation(request, investigation_pk):
except:
return redirect(landing)
+ ### TO-DO: replace with dynamic lookahead instead of static huge list
defects = Defect.objects.all()
+
+ # Calculate the default 'affected_components' list, if any
+ affected_components = ''
+ affected_components_list = []
+ vulnerability = investigation_object.vulnerability
+ vc_list = vulnerability.vulnerability_to_cve.all()
+ for vc in vc_list:
+ if vc.cve.packages:
+ affected_components_list.append(vc.cve.packages)
+ if affected_components_list:
+ affected_components = ' '.join(affected_components_list)
+
+ # Pass Investigation's defect list
investigation_to_defect = investigation_object.investigation_to_defect.all()
context = {
'object' : investigation_object,
'defects' : defects,
'investigation_to_defect' : investigation_to_defect,
+ 'affected_components' : affected_components,
'defect_example' : SrtSetting.objects.get(name='SRTOOL_DEFECT_SAMPLENAME').value,
'notify_categories' : NotifyCategories.objects.all(),
'users' : UserSafe.get_safe_userlist(True),
'components' : Defect.Components,
+ 'found_version' : investigation_object.product.get_defect_tag('found_version'),
}
return render(request, template, context)
elif request.method == "POST":
@@ -757,11 +802,13 @@ def investigation(request, investigation_pk):
_log("EXPORT_POST:'fileupload' does not exist: %s" % e)
try:
- with open(path + "/" + file.name, 'xb+') as destination:
+ local_file_path = path + "/" + file.name
+ with open(local_file_path, 'xb+') as destination:
for line in file:
destination.write(line)
username = UserSafe.user_name(request.user)
- 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=username)
+ InvestigationUploads.objects.get_or_create(investigation_id=investigation_object.id, description=description, path=local_file_path, size=file.size, date=datetime.today().strftime('%Y-%m-%d'), author=username)
+ InvestigationHistory.objects.create(investigation_id=investigation_object.id, comment=Update.ATTACH_DOC % file.name, date=datetime.now().strftime(SRTool.DATE_FORMAT), author=username)
except Exception as e:
_log("EXPORT_POST:FILE ALREADY EXISTS: %s" % e)
return redirect(investigation,investigation_pk)
@@ -797,6 +844,7 @@ def defect(request, defect_pk):
context = {
'object' : defect_object,
'users' : users,
+ 'SRTOOL_DEFECT_URLBASE' : SrtSetting.objects.get(name='SRTOOL_DEFECT_URLBASE').value
}
return render(request, template, context)
@@ -876,6 +924,7 @@ def users(request):
return render(request, template, context)
def report(request,page_name):
+ _log("REPORT!:%s" % (request))
if request.method == "GET":
context = ReportManager.get_context_data(page_name,request=request)
record_list = request.GET.get('record_list', '')
@@ -883,7 +932,7 @@ def report(request,page_name):
context['record_list'] = record_list
return render(request, 'report.html', context)
elif request.method == "POST":
- _log("EXPORT_POST!:%s|%s" % (request,request.FILES))
+ _log("EXPORT_POST!:%s" % (request))
parent_page = request.POST.get('parent_page', '')
file_name,response_file_name = ReportManager.exec_report(parent_page,request=request)
@@ -926,13 +975,153 @@ def create_vulnerability(request):
context = {}
return render(request, 'create_vulnerability.html', context)
+class Snap():
+ def __init__(self,snap_index=0,snap_mode='None',snap_dir='',snap_date='',snap_time='',snap_day=''):
+ self.index = '%02d' % snap_index
+ self.mode = snap_mode
+ self.dir = snap_dir
+ self.date = snap_date
+ self.time = snap_time
+ self.day = snap_day
+
+class ReportFile():
+ def __init__(self,name='',size=0,date=None):
+ self.name = name
+ self.size = size
+ self.date = date
+
def publish(request):
# does this user have permission to see this record?
if not UserSafe.is_creator(request.user):
return redirect(landing)
+ if request.method == "GET":
+
+ # Prepare available snapshots
+ snapshot_list = []
+ snap_start_index = 0
+ snap_stop_index = 0
+ snap_date_base = SrtSetting.get_setting('publish_snap_date_base','2019-06-08')
+ snap_date_top = SrtSetting.get_setting('publish_snap_date_top','2019-06-16')
+ snap_date_start = SrtSetting.get_setting('publish_snap_date_start','2019-06-08')
+ snap_date_stop = SrtSetting.get_setting('publish_snap_date_stop','2019-06-16')
+ snap_last_calc = SrtSetting.get_setting('publish_snap_last_calc','')
+ backup_returncode,backup_stdout,backup_result = execute_process('bin/common/srtool_backup.py','--list-backups-db')
+ for i,line in enumerate(backup_stdout.decode("utf-8").splitlines()):
+ # Week|backup_2019_19|2019-05-18|12:51:51|Saturday, May 18 2019
+ backup_mode,backup_dir,backup_date,backup_time,backup_day = line.split('|')
+ if 'Now' != backup_mode:
+ snap = Snap(i,backup_mode,backup_dir,backup_date,backup_time,backup_day)
+ snapshot_list.append(snap)
+ if snap_date_base == snap.date:
+ snap_start_index = i
+ if snap_date_start < snap.date:
+ snap_date_start = snap.date
+ if snap_date_stop < snap.date:
+ snap_date_stop = snap.date
+ if snap_date_top == snap.date:
+ snap_stop_index = i
+ if snap_date_stop > snap.date:
+ snap_date_stop = snap.date
+ if not snap_stop_index:
+ snap_stop_index = i
+ if snap_date_stop < snap.date:
+ snap_date_stop = snap.date
+ # Report automation
+ snap_frequency_select = SrtSetting.get_setting('publish_snap_frequency','Off')
+ snapshot_frequency_list = [
+ 'Off',
+ 'Monthly',
+ 'Bi-monthly',
+ 'Weekly',
+ 'Daily',
+ ]
+ # List of available reports
+ generated_report_list = []
+ for entry in os.scandir('data/wr'):
+ if entry.name.startswith('cve-svns-srtool'):
+ generated_report_list.append(ReportFile(entry.name,entry.stat().st_size,datetime.fromtimestamp(entry.stat().st_mtime)))
+# generated_report_list.sort()
+ generated_report_list = sorted(generated_report_list,key=lambda x: x.name)
+
+ # Prepare History data
+ last_calc = SrtSetting.get_setting('publish_last_calc','06/08/2019')
+ date_start = SrtSetting.get_setting('publish_date_start','06/08/2019')
+ date_stop = SrtSetting.get_setting('publish_date_stop','06/21/2019')
+
+ context = {
+ 'date_start' : date_start,
+ 'date_stop' : date_stop,
+ 'last_calc' : last_calc,
+
+ 'snap_date_start' : snap_date_start,
+ 'snap_date_stop' : snap_date_stop,
+ 'snap_date_base' : snap_date_base,
+ 'snap_date_top' : snap_date_top,
+ 'snapshot_list' : snapshot_list,
+ 'snap_start_index' : '%02d' % snap_start_index,
+ 'snap_stop_index' : '%02d' % snap_stop_index,
+ 'snap_last_calc' : snap_last_calc,
+ 'generated_report_list' : generated_report_list,
+
+ 'snapshot_frequency_list' : snapshot_frequency_list,
+ 'snap_frequency_select' : snap_frequency_select,
+ }
+ return render(request, 'publish.html', context)
+ elif request.method == "POST":
+ action = request.POST['action']
+
+ if request.POST["action"] == "download":
+ report_name = request.POST['report_name']
+ file_path = 'data/wr/%s' % report_name
+ if file_path:
+ fsock = open(file_path, "rb")
+ content_type = MimeTypeFinder.get_mimetype(file_path)
+ response = HttpResponse(fsock, content_type = content_type)
+ disposition = 'attachment; filename="{}"'.format(file_path)
+ response['Content-Disposition'] = 'attachment; filename="{}"'.format(file_path)
+ _log("EXPORT_POST_Q{%s} %s || %s " % (response, response['Content-Disposition'], disposition))
+ return response
+ else:
+ return render(request, "unavailable_artifact.html", context={})
+
+ # Dates (make as no timezone)
+ msg = ''
+ try:
+ msg = 'Start:%s' % request.POST.get('date_start', '')
+ date_start = datetime.strptime(request.POST.get('date_start', ''), '%m/%d/%Y')
+ msg = 'Stop:%s' % request.POST.get('date_stop', '')
+ date_stop = datetime.strptime(request.POST.get('date_stop', ''), '%m/%d/%Y')
+ if date_stop < date_start:
+# return 'Error:stop date is before start date'
+ _log('Error:stop date is before start date')
+ pass
+ except Exception as e:
+# return 'Error:bad format for dates (must be mm/dd/yyyy) (%s)(%s)' % (msg,e),''
+ _log('Error:bad format for dates (must be mm/dd/yyyy) (%s)(%s)' % (msg,e))
+ pass
+ SrtSetting.set_setting('publish_date_start',date_start.strftime('%m/%d/%Y'))
+ SrtSetting.set_setting('publish_date_stop',date_stop.strftime('%m/%d/%Y'))
+ if 'recalculate' == action:
+ # Calculate
+ publishCalculate(date_start,date_stop)
+ return redirect('publish')
+ if 'view' == action:
+ # Go to publish list page
+ return redirect('publish-list')
+ if 'add-cve' == action:
+ # Go to publish list page
+ return redirect('publish-cve')
+ if 'add-defect' == action:
+ # Go to publish list page
+ return redirect('publish-defect')
+ if 'reset' == action:
+ publishReset(date_start,date_stop)
+ publishCalculate(date_start,date_stop)
+ return redirect('publish')
+ if 'export' == action:
+ return redirect('/wr/report/publish')
+ return redirect('publish')
- context = {}
- return render(request, 'publish.html', context)
def manage_report(request):
# does this user have permission to see this record?
@@ -952,31 +1141,66 @@ def guided_tour(request):
def quicklink(request):
return redirect("/srtgui/select-publish")
-def _create_defect(investigation,defect_reason,components):
- _log("SRT_DEFECT=%s|%s|%s|" % (investigation.name,defect_reason,components))
+# Return defect_name,isCreated
+def _create_defect(investigation,reason,defect_reason,domain_components,affected_components,username):
+ _log("SRT_DEFECT=%s|%s|%s|%s|" % (investigation.name,defect_reason,domain_components,affected_components))
+
+ # Check to see if defect creation is allowed for this product
+ if 'no' == investigation.product.get_defect_tag('auto_create','yes'):
+ _log("SRT_DEFECT_SKIPPED:NO_auto_create:%s" % (investigation.product.defect_tags))
+ return '(%s skipped)' % investigation.product.key,False
+
+ # Check to see if a defect already is created for this investigation
+ try:
+ for id in InvestigationToDefect.objects.filter(investigation=investigation):
+ # First defect wins
+ _log("SRT_DEFECT_EXISTING:%s" % (id.defect.name))
+ return id.defect.name, False
+ except:
+ pass
vulnerability = investigation.vulnerability
vc_list = vulnerability.vulnerability_to_cve.all()
- # gather name(s) and link(s) of parent CVE(s)
+ # Gather name(s) and link(s) of parent CVE(s)
cve_list = [vc.cve.name for vc in vc_list]
cves = ','.join(cve_list)
+
+ # Offer a default defect description
description = ['%s\n' % vc.cve.description for vc in vc_list]
+
### TODO: normal NIST link might not always work
- link_list = ['https://nvd.nist.gov/vuln/detail/%s' % vc.cve.name for vc in vc_list]
- links = ','.join(cve_list)
+ link_list = []
+ for vc in vc_list:
+ link_list.append('https://nvd.nist.gov/vuln/detail/%s' % vc.cve.name)
+
+ # Fix links to make if Jira friendly
+ # CREATE(Triage): {Link=https://nvd.nist.gov/vuln/detail/CVE-2019-8934 User=admin}
+# links = "%s {%sLink=%s User=%s}" % (Update.CREATE_STR % Update.SOURCE_TRIAGE,"Reason='%s' " % reason if reason else '',' '.join(link_list),username)
+ # CREATE(Triage):(User=admin) [CVE-2019-8934|https://nvd.nist.gov/vuln/detail/CVE-2019-8934]
+ links = "%s%s(User=%s)" % (Update.CREATE_STR % Update.SOURCE_TRIAGE,"(Reason='%s')" % reason if reason else '',username)
+ for link in link_list:
+ links += ' [%s|%s]' % (os.path.basename(link),link)
# Assign the defect the same priority as the Investigation
priority = investigation.get_priority_text
+ _log("_create_defect:%s:%s:%s" % (investigation.name,priority,links))
- # Component string (e.g. 'kernel', 'userspace', ...)
- if not components:
- components = 'unknown'
- # Offer a defect summary
+ # Offer a default defect summary
+ if not defect_reason:
+ defect_reason = affected_components
if defect_reason:
summary = "Security Advisory - %s - %s" % (defect_reason,cves)
else:
summary = "Security Advisory %s" % (cves)
+
+ # Add the affect components
+ if affected_components:
+ affected_components.replace(',',' ').replace(';',' ').replace(' ',' ')
+ components = "%s {COMPONENTS:%s}" % (domain_components,affected_components)
+ else:
+ components = domain_components
+
defect_tool = SrtSetting.objects.get(name='SRTOOL_DEFECT_TOOL').value
result_returncode,result_stdout,result_stderr = execute_process(
defect_tool, '--new',
@@ -1019,6 +1243,11 @@ def _create_defect(investigation,defect_reason,components):
d = Defect.objects.create(name=d_name)
d.summary = summary
d.priority = investigation.priority
+ d.status = Defect.DEFECT_STATUS_OPEN
+ d.resolution = Defect.DEFECT_UNRESOLVED
+ d.srt_priority = investigation.priority
+ d.srt_status = Defect.VULNERABLE
+ d.srt_outcome = Defect.OPEN
d.product = investigation.product
d.url = d_url
d.save()
@@ -1026,7 +1255,24 @@ def _create_defect(investigation,defect_reason,components):
# Create Investigation to Defect
id = InvestigationToDefect.objects.create(investigation=investigation,defect=d,product=investigation.product)
id.save()
- return d.name
+ return d.name,True
+
+def _auto_map_cve_priority(cve,force=True):
+ if not force and (SRTool.UNDEFINED != cve.priority):
+ return(cve.priority)
+ severity = cve.cvssV3_baseSeverity.strip()
+ if not severity:
+ severity = cve.cvssV2_severity.strip()
+ if not severity:
+ severity = 'MEDIUM'
+ if 'CRITICAL' == severity:
+ return(SRTool.CRITICAL)
+ elif 'HIGH' == severity:
+ return(SRTool.HIGH)
+ elif 'MEDIUM' == severity:
+ return(SRTool.MEDIUM)
+ else:
+ return(SRTool.LOW)
def xhr_triage_commit(request):
_log("xhr_triage_commit(%s)" % request.POST)
@@ -1035,7 +1281,8 @@ def xhr_triage_commit(request):
try:
username = UserSafe.user_name(request.user)
action = request.POST['action']
- today = datetime.today().strftime("%Y-%m-%d")
+ srtool_today_time = datetime.today()
+ srtool_today = datetime.today().strftime("%Y-%m-%d")
if 'submit-notvulnerable' == action:
reason = request.POST['reason']
cves = request.POST['cves']
@@ -1044,85 +1291,83 @@ def xhr_triage_commit(request):
created_list = ''
for cve_name in cves.split(','):
cve = Cve.objects.get(name=cve_name)
+ history_update = []
+ history_update.append(Update.STATUS % (SRTool.status_text(cve.status),SRTool.status_text(Cve.NOT_VULNERABLE)))
+ cve.priority = _auto_map_cve_priority(cve,False)
cve.status = Cve.NOT_VULNERABLE
if cve.comments:
cve.comments += ', ' + reason
else:
cve.comments = reason
+ cve.acknowledge_date = srtool_today_time
cve.save()
created_list += ' %s' % cve_name
# add audit comment
cc = CveHistory.objects.create(cve=cve)
- cc.date = today
- cc.comment = "ACTION: marked not vulnerable, reason='%s'" % (reason)
+ cc.date = srtool_today
+ cc.comment = "%s%s {%s}" % (Update.UPDATE_STR % Update.SOURCE_USER,';'.join(history_update),"Set by triage, reason='%s'" % reason)
cc.author = username
cc.save()
if created_list:
created_list = "NotVulnerable:" + created_list
- if 'submit-investigate' == action:
- cves = request.POST['cves']
- created_list = ''
- for cve_name in cves.split(','):
- cve = Cve.objects.get(name=cve_name)
- cve.status = Cve.INVESTIGATE
- cve.save()
- created_list += ' %s' % cve_name
- # add audit comment
- cc = CveHistory.objects.create(cve=cve)
- cc.date = today
- cc.comment = "ACTION: marked investigate"
- cc.author = username
- cc.save()
- if created_list:
- created_list = "Investigate:" + created_list
-
if 'submit-other' == action:
cves = request.POST['cves']
status = int(request.POST['status'])
created_list = ''
for cve_name in cves.split(','):
cve = Cve.objects.get(name=cve_name)
+ history_update = []
+ history_update.append(Update.STATUS % (SRTool.status_text(cve.status),SRTool.status_text(status)))
+ cve.priority = _auto_map_cve_priority(cve,False)
cve.status = status
+ cve.acknowledge_date = srtool_today_time
cve.save()
created_list += ' %s' % cve_name
# add audit comment
cc = CveHistory.objects.create(cve=cve)
- cc.date = today
- cc.comment = "ACTION: set status to %s" % cve.get_status_text
+ cc.date = srtool_today
+ cc.comment = "%s%s {%s}" % (Update.UPDATE_STR % Update.SOURCE_USER,';'.join(history_update),"Set by triage")
cc.author = username
cc.save()
if created_list:
created_list = "Status=%s:%s" % (cve.get_status_text,created_list)
- if 'submit-isvulnerable' == action:
+ if action in ('submit-isvulnerable','submit-investigate'):
+ if 'submit-isvulnerable' == action:
+ notify_message = 'Triage:Vulnerable:'
+ new_status = SRTool.VULNERABLE
+ elif 'submit-investigate' == action:
+ notify_message = 'Triage:Investigate:'
+ new_status = SRTool.INVESTIGATE
reason = request.POST['reason'].strip()
defect_reason = request.POST['defect_reason'].strip()
cves = request.POST['cves']
products = request.POST['products']
components = request.POST['components']
- priority = request.POST['priority']
+ affected_components = request.POST['affected_components'].strip()
+ priority = int(request.POST['priority'])
make_defects = ('yes' == request.POST['mk_d'])
mark_publish = ('yes' == request.POST['pub'])
group_vulnerability = int(request.POST['vul_group'])
group_vulnerability_name = request.POST['vul_name'].strip()
notifications = ('yes' == request.POST['notify'])
+ acknowledge_date = request.POST['acknowledge_date']
add_for = request.POST['for']
_log("xhr_triage_commit:IS:%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|" % (reason,defect_reason,cves,products,components,make_defects,mark_publish,add_for,priority,group_vulnerability,group_vulnerability_name))
# Set up
- investigation_names = {}
created_list = ''
- notify_message = 'Triage:Vulnerable:'
+
# Map vulnerability grouping
- v = None
+ vulnerability = None
if 2 == group_vulnerability:
# Existing V all C
first_vulnerability = False
group_vulnerabilities = True
try:
- v = Vulnerability.objects.get(name=group_vulnerability_name)
- created_list += ' %s(found)' % v.name
- notify_message += ' Found:%s' % v.name
+ vulnerability = Vulnerability.objects.get(name=group_vulnerability_name)
+ created_list += ' %s(found)' % vulnerability.name
+ notify_message += ' Found:%s' % vulnerability.name
except Exception as e:
_log("xhr_triage_commit:No such Vulnerability name found (%s,%s)" % (group_vulnerability_name,e))
return HttpResponse(json.dumps({"error":"No such Vulnerability name found (%s)" % (group_vulnerability_name)}), content_type = "application/json")
@@ -1134,91 +1379,159 @@ def xhr_triage_commit(request):
# One V per C
first_vulnerability = True
group_vulnerabilities = False
+
# Process the CVE list
for cve_name in cves.split(','):
# update CVE
cve = Cve.objects.get(name=cve_name)
- cve.status = Cve.VULNERABLE
- cve.priority = priority
+ # Auto priority?
+ cve_priority = _auto_map_cve_priority(cve) if 99 == priority else priority
if cve.comments:
- cve.comments += ', ' + reason
+ cve_comments = '%s, %s' % (cve.comments,reason)
else:
- cve.comments = reason
+ cve_comments = reason
+ # Acknowledge date selection
+ try:
+ if ('publish' == acknowledge_date) and cve.publishedDate:
+ cve_acknowledge_date = datetime.strptime(cve.publishedDate, '%Y-%m-%d')
+ elif ('update' == acknowledge_date) and cve.lastModifiedDate:
+ cve_acknowledge_date = datetime.strptime(cve.lastModifiedDate, '%Y-%m-%d')
+ elif ('no_change' == acknowledge_date):
+ cve_acknowledge_date = cve.acknowledge_date
+ else:
+ cve_acknowledge_date = srtool_today_time
+ except:
+ cve_acknowledge_date = srtool_today_time
+ # Update history changes
+ history_update = []
+ if cve.status != new_status:
+ history_update.append(Update.STATUS % (SRTool.status_text(cve.status),SRTool.status_text(new_status)))
+ if cve.priority != cve_priority:
+ history_update.append(Update.PRIORITY % (SRTool.priority_text(cve.priority),SRTool.priority_text(cve_priority)))
+ if cve.acknowledge_date != cve_acknowledge_date:
+ history_update.append(Update.ACKNOWLEDGE_DATE % (cve.acknowledge_date.strftime("%Y/%m/%d") if cve.acknowledge_date else '',cve_acknowledge_date.strftime("%Y/%m/%d")))
+ # Update record
+ cve.status = new_status
+ cve.priority = cve_priority
+ cve.comments = cve_comments
+ cve.acknowledge_date = cve_acknowledge_date
+ cve.packages = affected_components
cve.save()
notify_message += " %s" % cve_name
- # create vulnerability
+ # Add history comment
+ if history_update:
+ cc = CveHistory.objects.create(cve=cve)
+ cc.date = srtool_today
+ cc.comment = "%s%s {%s}" % (Update.UPDATE_STR % Update.SOURCE_USER,';'.join(history_update), "Triage:reason='%s'" % reason)
+ cc.author = username
+ cc.save()
+
+ # Find or create vulnerability
if first_vulnerability or not group_vulnerabilities:
first_vulnerability = False
- v_name = Vulnerability.new_vulnerability_name()
- v = Vulnerability.objects.create(name=v_name)
- v.public = True
- v.status = Vulnerability.VULNERABLE
- v.priority = priority
- v.comments = reason
- v.save()
- notify_message += " %s" % v_name
- created_list += ' %s' % v.name
- _log("Create First Vulnerability:%s" % v.name)
- # add audit comment
- cc = CveHistory.objects.create(cve=cve)
- cc.date = today
- cc.comment = "ACTION: created vulnerability '%s', reason='%s'" % (v.name,reason)
- cc.author = username
- cc.save()
+
+ # Check to see if a vulnerability already is created for this cve
+ vulnerability = None
+ try:
+ for cv in CveToVulnerablility.objects.filter(cve=cve):
+ # First vulnerability wins
+ vulnerability = cv.vulnerability
+ created_list += ' (%s)' % vulnerability.name
+ break
+ except:
+ pass
+
+ if not vulnerability:
+ v_name = Vulnerability.new_vulnerability_name()
+ vulnerability = Vulnerability.objects.create(name=v_name)
+ vulnerability.public = True
+ vulnerability.priority = cve_priority
+ vulnerability.status = new_status
+ vulnerability.outcome = Vulnerability.OPEN
+ vulnerability.comments = reason
+ vulnerability.save()
+ notify_message += " %s" % v_name
+ created_list += ' %s' % vulnerability.name
+ _log("Create First Vulnerability:%s" % vulnerability.name)
+
+ # add audit comment
+ vh = VulnerabilityHistory.objects.create(vulnerability=vulnerability)
+ vh.date = srtool_today
+ vh.comment = "%s {%s}" % (Update.CREATE_STR % Update.SOURCE_TRIAGE,'Created from triage')
+ vh.author = username
+ vh.save()
# map vulnerability to CVE
- cv = CveToVulnerablility.objects.create(vulnerability=v,cve=cve)
- cv.save()
- # add audit comment
- vc = VulnerabilityHistory.objects.create(vulnerability=v)
- vc.date = today
- vc.comment = "ACTION: created vulnerability for '%s', reason='%s'" % (cve.name,reason)
- vc.author = username
- vc.save()
+ cv,created = CveToVulnerablility.objects.get_or_create(vulnerability=vulnerability,cve=cve)
+ if created:
+ cv.save()
if products:
for product_id in products.split(','):
# fetch product
- p = Product.objects.get(pk=product_id)
+ product = Product.objects.get(pk=product_id)
# create (or group) investigation
- investigation_key = "%s-%s" % (v_name,product_id)
- i_name = ''
- if investigation_key in investigation_names:
- i_name = investigation_names[investigation_key]
- if not i_name or not group_vulnerabilities:
+
+ # Check to see if a investigation for this product already is created for this vulnerability
+ investigation = None
+ try:
+ for vi in VulnerabilityToInvestigation.objects.filter(vulnerability=vulnerability,investigation__product=product):
+ # First Investigation for this product wins
+ investigation = vi.investigation
+ created_list += ' (%s)' % investigation.name
+ break
+ except:
+ pass
+
+ if not investigation:
i_name = Investigation.new_investigation_name()
- i = Investigation.objects.create(name=i_name)
- i.vulnerability = v
- i.product = p
- i.priority = priority
- i.save()
- notify_message += " %s" % i_name
- created_list += ' %s' % i.name
- investigation_names[investigation_key] = i_name
+ investigation = Investigation.objects.create(name=i_name)
+ investigation.vulnerability = vulnerability
+ investigation.product = product
+ investigation.priority = cve_priority
+ investigation.outcome = Investigation.OPEN
+ # Check to see if product is active
+ _log("BOO1:")
+ if 'no' == product.get_product_tag('active','yes'):
+ _log("BOO2:%s,%s" % (investigation.status,SRTool.status_to_inactive(new_status)))
+ investigation.status = SRTool.status_to_inactive(new_status)
+ else:
+ _log("BOO3:")
+ investigation.status = new_status
+ _log("BOO4:%s" % investigation.status )
+
+ investigation.save()
+
+ notify_message += " %s" % investigation.name
+ created_list += ' %s' % investigation.name
# map vulnerability to investigation/product
- vi = VulnerabilityToInvestigation.objects.create(vulnerability=v,investigation = i)
+ vi = VulnerabilityToInvestigation.objects.create(vulnerability=vulnerability,investigation=investigation)
vi.save()
- else:
- i = Investigation.objects.get(name=i_name)
- # add audit comment
- ic = InvestigationHistory.objects.create(investigation=i)
- ic.date = today
- ic.comment = "ACTION: created investigation for '%s', reason='%s'" % (cve.name,reason)
- ic.author = username
- ic.save()
+
+ # add audit comment
+ ih = InvestigationHistory.objects.create(investigation=investigation)
+ ih.date = srtool_today
+ ih.comment = "%s {%s}" % (Update.CREATE_STR % Update.SOURCE_TRIAGE,'Created from triage')
+ ih.author = username
+ ih.save()
+
# create defects
if make_defects:
- defect_name = _create_defect(i,defect_reason,components)
- notify_message += " %s" % defect_name
- created_list += ' %s' % defect_name
- _log("NEW_DEFECT:%s|%s|%s|" % (defect_name,components,priority))
+ defect_name,created = _create_defect(investigation,reason,defect_reason,components,affected_components,username)
+ if created:
+ notify_message += ' %s' % defect_name
+ created_list += ' %s' % defect_name
+ else:
+ notify_message += ' (%s)' % defect_name
+ created_list += ' (%s)' % defect_name
+ _log("NEW_DEFECT:%s|%s|%s|" % (defect_name,components,cve_priority))
# Finish up
if notifications:
# Create the notify record
_log("xhr_notifications3")
notify = Notify()
notify.category = 'TRIAGE'
- notify.priority = priority
+ notify.priority = cve_priority
notify.description = notify_message
notify.url = ''
notify.author = username
@@ -1280,35 +1593,64 @@ def xhr_cve_commit(request):
try:
cve = Cve.objects.get(id=request.POST['cve_id'])
action = request.POST['action']
- history_comment = ''
+ history_update = []
new_name = ''
if 'submit-quickedit' == action:
- note = request.POST['note']
priority = int(request.POST['priority'])
status = int(request.POST['status'])
- private_note = request.POST['private_note']
- publish_state = request.POST['publish_state']
- publish_date = request.POST['publish_date']
- if (priority != cve.priority):
+ note = request.POST['note'].strip()
+ private_note = request.POST['private_note'].strip()
+ tags = request.POST['tags'].strip()
+ publish_state = int(request.POST['publish_state'])
+ publish_date = request.POST['publish_date'].strip()
+ acknowledge_date = request.POST['acknowledge_date'].strip()
+ affected_components = request.POST['affected_components'].strip()
+ # Convert simple date back to datetime
+ try:
+ if not acknowledge_date:
+ acknowledge_date = None
+ else:
+ acknowledge_date = datetime.strptime(acknowledge_date, '%Y-%m-%d')
+ except Exception as e:
+ acknowledge_date = cve.acknowledge_date
+ if (cve.priority != priority):
+ history_update.append(Update.PRIORITY % (SRTool.priority_text(cve.priority),SRTool.priority_text(priority)))
cve.priority = priority
- history_comment += "Priority, "
- if (status != cve.status):
+ if (cve.status != status):
+ history_update.append(Update.STATUS % (SRTool.status_text(cve.status),SRTool.status_text(status)))
cve.status = status
- history_comment += "Status, "
- if (note != cve.comments):
+ if (cve.comments != note):
+ history_update.append(Update.NOTE)
cve.comments = note
- history_comment += "Note, "
- if (private_note != cve.comments_private):
+ if (cve.comments_private != private_note):
+ history_update.append(Update.PRIVATE_NOTE)
cve.comments_private = private_note
- history_comment += "Private Note, "
- if (publish_state != cve.publish_state):
+ if ( cve.tags !=tags):
+ history_update.append(Update.TAG)
+ cve.tags = tags
+ if (cve.publish_state != publish_state):
+ history_update.append(Update.PUBLISH_STATE % (SRTool.publish_text(cve.publish_state),SRTool.publish_text(publish_state)))
cve.publish_state = publish_state
- history_comment += "Publish State, "
- if (publish_date != cve.publish_date):
+ if (cve.publish_date != publish_date):
+ history_update.append(Update.PUBLISH_DATE % (SRTool.date_ymd_text(cve.publish_date),SRTool.date_ymd_text(publish_date)))
cve.publish_date = publish_date
- history_comment += "Publish Date, "
+ if (cve.packages != affected_components):
+ history_update.append(Update.AFFECTED_COMPONENT % (cve.packages,affected_components))
+ cve.packages = affected_components
+ # Allow for either acknowledge_date to be empty/None
+ if (cve.acknowledge_date and not acknowledge_date):
+ history_update.append(Update.ACKNOWLEDGE_DATE % (SRTool.date_ymd_text(cve.acknowledge_date),''))
+ cve.acknowledge_date = None
+ elif (not cve.acknowledge_date and acknowledge_date):
+ cve.acknowledge_date = acknowledge_date
+ history_update.append(Update.ACKNOWLEDGE_DATE % (cve.acknowledge_date,SRTool.date_ymd_text(acknowledge_date)))
+ elif (cve.acknowledge_date != acknowledge_date):
+ history_update.append(Update.ACKNOWLEDGE_DATE % (SRTool.date_ymd_text(cve.acknowledge_date),SRTool.date_ymd_text(acknowledge_date)))
+ cve.acknowledge_date = acknowledge_date
+
cve.save()
if 'submit-notification' == action:
+ # Note: no history update
_submit_notification(request)
if 'submit-newname' == action:
old_name = request.POST['old_name']
@@ -1319,6 +1661,7 @@ def xhr_cve_commit(request):
return HttpResponse(json.dumps({"error":"name '%s' is already used\n" % new_name}), content_type = "application/json")
except:
_log("NewName3:%s -> %s" % (old_name,new_name))
+ history_update.append(Update.NEW_NAME % (old_name,new_name))
# Apply this unique name to CVE
cve.name = new_name
cve.name_sort = get_name_sort(new_name)
@@ -1337,6 +1680,7 @@ def xhr_cve_commit(request):
priority = cve.priority,
)
vulnerability.save()
+ history_update.append(Update.ATTACH_INV % (vname))
cve2vul = CveToVulnerablility.objects.create(cve = cve,vulnerability = vulnerability)
cve2vul.save()
_log("SUBMIT-CREATE-VULNERABILITY:%s,%s,%s" % (cve.id,vulnerability.id,cve2vul.id))
@@ -1348,9 +1692,16 @@ def xhr_cve_commit(request):
except Exception as e:
_log("xhr_triage_commit:No such Vulnerability name found (%s,%s)" % (vname,e))
return HttpResponse(json.dumps({"error":"No such Vulnerability name found (%s)" % (vname)}), content_type = "application/json")
+ history_update.append(Update.ATTACH_INV % (vname))
cve2vul = CveToVulnerablility.objects.create(cve = cve,vulnerability = vulnerability)
cve2vul.save()
_log("SUBMIT-CREATE-VULNERABILITY:%s,%s,%s" % (cve.id,vulnerability.id,cve2vul.id))
+ if 'submit-delete-cve' == action:
+ _log("SUBMIT-DELETE-CVE(%s)" % cve.name)
+ #history_update.append(Update.ATTACH_INV % (vname))
+ cve.delete()
+ _log("SUBMIT-DELETED-CVE(%s)!" % cve.name)
+ new_name = 'url:/srtgui/cves'
return_data = {
"error": "ok",
@@ -1358,10 +1709,9 @@ def xhr_cve_commit(request):
}
username = UserSafe.user_name(request.user)
- 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=username)
+ if history_update:
+ update_comment = "%s%s" % (Update.UPDATE_STR % Update.SOURCE_USER,';'.join(history_update))
+ CveHistory.objects.create(cve_id=cve.id, comment=update_comment, date=datetime.now().strftime('%Y-%m-%d'), author=username)
_log("xhr_cve_commit:SUCCESS")
return HttpResponse(json.dumps( return_data ), content_type = "application/json")
@@ -1416,34 +1766,38 @@ def xhr_vulnerability_commit(request):
action = request.POST['action']
v_id = request.POST['vulnerability_id']
username = UserSafe.user_name(request.user)
- history_comment = ''
try:
+ history_update = []
if 'submit-quickedit' == action:
- note = request.POST['note']
- private_note = request.POST['private_note']
+ note = request.POST['note'].strip()
+ private_note = request.POST['private_note'].strip()
+ tags = request.POST['tags'].strip()
+ priority = int(request.POST['priority'])
+ status = int(request.POST['status'])
+ outcome = int(request.POST['outcome'])
v = Vulnerability.objects.get(id=v_id)
+ if (v.priority != priority):
+ history_update.append(Update.PRIORITY % (SRTool.priority_text(v.priority),SRTool.priority_text(priority)))
+ v.priority = priority
+ if (v.status != status):
+ history_update.append(Update.STATUS % (SRTool.status_text(v.status),SRTool.status_text(status)))
+ v.status = status
+ if (v.outcome != outcome):
+ history_update.append(Update.OUTCOME % (SRTool.status_text(v.outcome),SRTool.status_text(outcome)))
+ v.outcome = outcome
if (v.comments != note):
+ history_update.append(Update.NOTE)
v.comments = note
- history_comment += "Note, "
if (v.comments_private != private_note):
+ history_update.append(Update.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.priority != request.POST['priority']):
- v.priority = request.POST['priority']
- history_comment += "Priority, "
- if (history_comment != ''):
- history_comment = history_comment[:-2]
- history_comment += " edited"
+ if (tags != v.tags):
+ history_update.append(Update.TAG)
+ v.tags = tags
v.save()
if 'submit-addproduct' == action:
products = request.POST['products']
- product_names = ''
+ investigation_names = []
vulnerability_obj = Vulnerability.objects.get(id=v_id)
for product_id in products.split(','):
product_obj = Product.objects.get(pk=product_id)
@@ -1460,72 +1814,70 @@ def xhr_vulnerability_commit(request):
)
vul2inv = VulnerabilityToInvestigation.objects.create(vulnerability=vulnerability_obj,investigation=investigation_obj)
vul2inv.save()
- product_names += "%s " % product_obj.long_name
- product_names = product_names[:-2]
- history_comment = product_names + " added to affected products"
+ investigation_names.append(iname)
+ history_update.append(Update.ATTACH_INV % ','.join(investigation_names))
if 'submit-trashinvestigation' == action:
inv_id = request.POST['record_id']
investigation_obj = Investigation.objects.get(pk=inv_id)
vul2inv = VulnerabilityToInvestigation.objects.filter(investigation=investigation_obj)
vul2inv.delete()
- history_comment = investigation_obj.name + " investigation(s) removed"
+ history_update.append(Update.DETACH_INV % (investigation_obj.name))
investigation_obj.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=username)
- history_comment = "New comment submitted"
+ #NOTE: No History for this
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()
+ #NOTE: No History for this
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
+ history_update.append(Update.DETACH_DOC % (upload.path))
upload.delete()
if 'submit-addusernotify' == action:
users = request.POST['users']
- usernames = ''
+ usernames = []
for user_id in users.split(','):
- usernames += SrtUser.objects.get(pk=user_id).name + ', '
+ usernames.append(SrtUser.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"
+ history_update.append(Update.ATTACH_USER_NOTIFY % ','.join(usernames))
if 'submit-trashusernotification' == action:
record_id = request.POST['record_id']
notification_record = VulnerabilityNotification.objects.get(id=record_id)
removed_user = SrtUser.objects.get(pk=notification_record.user_id).name
- history_comment = removed_user + " removed from notifications"
notification_record.delete()
+ history_update.append(Update.DETACH_USER_NOTIFY % removed_user)
if 'submit-adduseraccess' == action:
users = request.POST['users']
- usernames = ''
+ usernames = []
for user_id in users.split(','):
- usernames += SrtUser.objects.get(pk=user_id).name + ', '
+ usernames.append(SrtUser.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"
+ history_update.append(Update.ATTACH_ACCESS % ','.join(usernames))
if 'submit-trashuseraccess' == action:
record_id = request.POST['record_id']
access_record = VulnerabilityAccess.objects.get(id=record_id)
- removed_user = username
- history_comment = removed_user + "'s access removed"
access_record.delete()
+ history_update.append(Update.DETACH_ACCESS % username)
if 'submit-notification' == action:
_submit_notification(request)
+ #NOTE: No History for this
if 'submit-trashvulnerability' == action:
record_id = request.POST['record_id']
vulnerability_obj = Vulnerability.objects.get(pk=record_id)
- history_comment = "Vulnerability '%s' is deleted" % vulnerability_obj.name
+# history_update.append(Update.DETACH_VUL % vulnerability_obj.name)
vulnerability_obj.delete()
- if (history_comment != ''):
- VulnerabilityHistory.objects.create(vulnerability_id=v_id, comment=history_comment, date=datetime.now().strftime('%Y-%m-%d'), author=username)
+ if history_update:
+ update_comment = "%s%s" % (Update.UPDATE_STR % Update.SOURCE_USER,';'.join(history_update))
+ VulnerabilityHistory.objects.create(vulnerability_id=v_id, comment=update_comment, date=datetime.now().strftime('%Y-%m-%d'), author=username)
return_data = {
"error": "ok",
}
@@ -1644,43 +1996,48 @@ def xhr_investigation_commit(request):
action = request.POST['action']
invst_id = request.POST['investigation_id']
username = UserSafe.user_name(request.user)
- history_comment = "Nothing happened."
try:
+ history_update = []
if 'submit-quickedit' == action:
- note = request.POST['note']
- private_note = request.POST['private_note']
+ priority = int(request.POST['priority'])
+ status = int(request.POST['status'])
+ outcome = int(request.POST['outcome'])
+ note = request.POST['note'].strip()
+ private_note = request.POST['private_note'].strip()
+ tags = request.POST['tags'].strip()
invst = Investigation.objects.get(id=invst_id)
+ if (invst.priority != priority):
+ history_update.append(Update.PRIORITY % (SRTool.priority_text(invst.priority),SRTool.priority_text(priority)))
+ invst.priority = priority
+ if (invst.status != request.POST['status']):
+ history_update.append(Update.STATUS % (SRTool.status_text(invst.status),SRTool.status_text(status)))
+ invst.status = request.POST['status']
+ if (invst.outcome != outcome):
+ history_update.append(Update.OUTCOME % (SRTool.status_text(invst.outcome),SRTool.status_text(outcome)))
+ invst.outcome = outcome
if (invst.comments != note):
invst.comments = note
- history_comment += "Note, "
+ history_update.append(Update.NOTE)
if (invst.comments_private != private_note):
invst.comments_private = private_note
- history_comment += "Private Note, "
- if (invst.status != request.POST['status']):
- invst.status = request.POST['status']
- history_comment += "Status, "
- if (invst.outcome != request.POST['outcome']):
- invst.outcome = request.POST['outcome']
- history_comment += "Outcome, "
- if (invst.priority != request.POST['priority']):
- invst.priority = request.POST['priority']
- history_comment += "Priority, "
- if (history_comment != ''):
- history_comment = history_comment[:-2]
- history_comment += " edited"
+ history_update.append(Update.PRIVATE_NOTE)
+ if (invst.tags != tags):
+ invst.tags = tags
+ history_update.append(Update.TAG)
invst.save()
if 'submit-attachdefectlist' == action:
defects = request.POST['defects']
product_id = Investigation.objects.get(id=invst_id).product_id
- defect_names = ""
+ defect_names = []
for defect_id in defects.split(','):
- defect_names += Defect.objects.get(pk=defect_id).name + ", "
+ defect_names.append(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"
+ history_update.append(Update.ATTACH_DEV % ','.join(defect_names))
if 'submit-attachdefect' == action:
query = request.POST['query'].upper()
product_id = Investigation.objects.get(id=invst_id).product_id
+ # Courtesy removal of URL (or other) prefix
+ query = re.sub(r".*/", "", query)
#check if defect already in SRTool data
try:
defect = Defect.objects.get(name=query)
@@ -1697,79 +2054,86 @@ def xhr_investigation_commit(request):
defect = Defect.objects.get(name=query)
except subprocess.CalledProcessError as e:
_log("ERROR:submit-attachdefect:%d:STDOUT='%s':" % (e.returncode, e.output))
- return HttpResponse(json.dumps({"error":str(e) + "\n"}), content_type = "application/json")
+ error_message = "Could not find defect with the name '%s'\n\n(detail:%s)\n" % (query,str(e))
+ return HttpResponse(json.dumps({"error":error_message}), 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
+ # Enforce minimum status on open defects
+ if Defect.DEFECT_UNRESOLVED == defect.resolution:
+ invst = Investigation.objects.get(id=invst_id)
+ if defect.srt_status < invst.status:
+ defect.srt_status = invst.status
+ defect.save()
+ history_update.append(Update.ATTACH_DEV % defect.name)
if 'submit-createdefect' == action:
investigation = Investigation.objects.get(id=invst_id)
defect_reason = request.POST['defect_reason']
components = request.POST['components']
priority = request.POST['priority']
- defect_name = _create_defect(investigation,defect_reason,components)
- history_comment = "New defect '%s' created" % defect_name
+ affected_components = request.POST['affected_components'].strip()
+ defect_name,created = _create_defect(investigation,'',defect_reason,components,affected_components,username)
+ history_update.append(Update.ATTACH_DEV % defect_name)
if 'submit-detachdefect' == action:
defect_name = request.POST['defect']
product_id = Investigation.objects.get(id=invst_id).product_id
defect_id = Defect.objects.get(name=defect_name).id
InvestigationToDefect.objects.get(investigation_id=invst_id, defect_id=defect_id).delete()
- history_comment = defect_name + " detached from investigation"
+ history_update.append(Update.DETACH_DEV % defect_name)
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=username)
- history_comment = "New comment submitted"
+ #NOTE: No History for this
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()
+ #NOTE: No History for this
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
+ history_update.append(Update.DETACH_DOC % (upload.path))
upload.delete()
if 'submit-addusernotify' == action:
users = request.POST['users']
- usernames = ""
+ usernames = []
for user_id in users.split(','):
- usernames += SrtUser.objects.get(pk=user_id).name + ", "
+ usernames.append(SrtUser.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"
+ history_update.append(Update.ATTACH_USER_NOTIFY % ','.join(usernames))
if 'submit-trashusernotification' == action:
record_id = request.POST['record_id']
notification_record = InvestigationNotification.objects.get(id=record_id)
removed_user = SrtUser.objects.get(pk=notification_record.user_id).name
- history_comment = removed_user + " removed from notifications"
+ history_update.append(Update.DETACH_USER_NOTIFY % removed_user)
notification_record.delete()
if 'submit-adduseraccess' == action:
users = request.POST['users']
- usernames = ""
+ usernames = []
for user_id in users.split(','):
- usernames += SrtUser.objects.get(pk=user_id).name + ", "
+ usernames.append(SrtUser.objects.get(pk=user_id).name)
InvestigationAccess.objects.get_or_create(investigation_id=invst_id, user_id=user_id)
- history_comment = usernames + " granted access"
+ history_update.append(Update.ATTACH_ACCESS % ','.join(usernames))
if 'submit-trashuseraccess' == action:
record_id = request.POST['record_id']
access_record = InvestigationAccess.objects.get(id=record_id)
- removed_user = username
- history_comment = removed_user + "'s access removed"
+ history_update.append(Update.DETACH_ACCESS % username)
access_record.delete()
if 'submit-notification' == action:
_submit_notification(request)
- history_comment = ''
+ #NOTE: No History for this
if 'submit-trashinvestigation' == action:
record_id = request.POST['record_id']
investigation_obj = Investigation.objects.get(pk=record_id)
- history_comment = "Investigation '%s' is deleted" % investigation_obj.name
+# history_update.append(Update.DETACH_INV % investigation_obj.name)
investigation_obj.delete()
- if history_comment:
- InvestigationHistory.objects.create(investigation_id=invst_id, comment=history_comment, date=datetime.now().strftime('%Y-%m-%d'), author=username)
+ if history_update:
+ update_comment = "%s%s" % (Update.UPDATE_STR % Update.SOURCE_USER,';'.join(history_update))
+ InvestigationHistory.objects.create(investigation_id=invst_id, comment=update_comment, date=datetime.now().strftime('%Y-%m-%d'), author=username)
return_data = {
"error": "ok",
}
@@ -1779,6 +2143,146 @@ def xhr_investigation_commit(request):
_log("xhr_investigation_commit:no(%s)" % e)
return HttpResponse(json.dumps({"error":str(e) + "\n"}), content_type = "application/json")
+def xhr_publish(request):
+ _log("xhr_publish(%s)" % request.POST)
+
+ def remove_mark(mark,line):
+ pos1 = line.find(mark)
+ if -1 == pos1:
+ return line
+ pos2 = line.find(')',pos1)
+ if -1 == pos2:
+ return line.replace(mark,'')
+ line = line[0:pos1] + line[pos2+1:]
+ return line
+
+ if not 'action' in request.POST:
+ return HttpResponse(json.dumps({"error":"missing action\n"}), content_type = "application/json")
+ try:
+ username = UserSafe.user_name(request.user)
+ action = request.POST['action']
+
+ if 'export-snapshot' == action:
+ snap_date_base = request.POST['snap_date_base']
+ snap_date_top = request.POST['snap_date_top']
+ snap_date_start = request.POST['snap_date_start']
+ snap_date_stop = request.POST['snap_date_stop']
+ _log("xhr_publish:export-snapshot:%s,%s,%s,%s" % (snap_date_base,snap_date_top,snap_date_start,snap_date_stop))
+
+ SrtSetting.set_setting('publish_snap_date_base',snap_date_base)
+ SrtSetting.set_setting('publish_snap_date_top',snap_date_top)
+ SrtSetting.set_setting('publish_snap_date_start',snap_date_start)
+ SrtSetting.set_setting('publish_snap_date_stop',snap_date_stop)
+
+ backup_returncode,backup_stdout,backup_result = execute_process('bin/common/srtool_backup.py','--list-backups-db')
+ base_dir = ''
+ top_dir = ''
+ for i,line in enumerate(backup_stdout.decode("utf-8").splitlines()):
+ # Week|backup_2019_19|2019-05-18|12:51:51|Saturday, May 18 2019
+ backup_mode,backup_dir,backup_date,backup_time,backup_day = line.split('|')
+ if (not base_dir) and (snap_date_base == backup_date):
+ base_dir = 'backups/%s' % backup_dir
+ if (not top_dir) and (snap_date_top == backup_date) and ('Now' != backup_mode):
+ top_dir = 'backups/%s' % backup_dir
+
+ _log('Publish:./bin/wr/srtool_publish.py --srt2update ' + base_dir)
+ report_returncode,report_stdout,report_error = execute_process('./bin/wr/srtool_publish.py','--srt2update',base_dir)
+ if 0 != report_returncode:
+ return_data = {"error": "Error: base dir prep:%s:%s" % (report_error,report_stdout),}
+ return HttpResponse(json.dumps( return_data ), content_type = "application/json")
+ _log('Publish:./bin/wr/srtool_publish.py --srt2update ' + top_dir)
+ report_returncode,report_stdout,report_error = execute_process('./bin/wr/srtool_publish.py','--srt2update',top_dir)
+ if 0 != report_returncode:
+ return_data = {"error": "Error: top dir prep:%s:%s" % (report_error,report_stdout),}
+ return HttpResponse(json.dumps( return_data ), content_type = "application/json")
+ _log('Publish:./bin/wr/srtool_publish.py --validate-update-svns --previous '+base_dir+' --current '+top_dir+' --start '+snap_date_start+' --stop '+snap_date_stop)
+ report_returncode,report_stdout,report_error = execute_process('./bin/wr/srtool_publish.py',
+ '--validate-update-svns','--previous',base_dir,'--current',top_dir,
+ '--start',snap_date_start,'--stop',snap_date_stop)
+ if 0 != report_returncode:
+ return_data = {"error": "Error: publish report:%s:%s" % (report_error,report_stdout),}
+ return HttpResponse(json.dumps( return_data ), content_type = "application/json")
+
+ publish_snap_last_calc = 'Base:%s, Top:%s, Start:%s, Stop:%s, On:%s' % (
+ snap_date_base,snap_date_top,snap_date_start,snap_date_stop,
+ datetime.today().strftime("%Y-%m-%d %H:%M:%S")
+ )
+ SrtSetting.set_setting('publish_snap_last_calc',publish_snap_last_calc)
+
+ _log('Publish:Done!')
+ elif 'submit-trashreport' == action:
+ report_name = request.POST['report_name']
+ os.remove('data/wr/%s' % report_name)
+ else:
+ srtool_today_time = datetime.today()
+ srtool_today = datetime.today().strftime("%Y-%m-%d")
+ reason_map = {}
+ if 'defects' in request.POST:
+ cve_table = []
+ for defect_name in request.POST['defects'].split(','):
+ try:
+ defect = Defect.objects.get(name = defect_name)
+ cve_names = defect.get_cve_names
+ for cve_name in cve_names.split(','):
+ cve_table.append(cve_name)
+ reason_map[cve_name] = defect_name
+ except Exception as e:
+ _log("ERROR:xhr_publish:defectlist:%s" % e)
+ cve_list = ','.join(cve_table)
+ else:
+ cve_list = request.POST['cves']
+ for cve_name in cve_list.split(','):
+ reason_map[cve_name] = ''
+ _log("xhr_publish_defect2cves3:%s:%d" % (cve_list,len(cve_list)))
+
+ date_start = datetime.strptime(SrtSetting.get_setting('publish_date_start','02/15/2019'), '%m/%d/%Y')
+ date_stop = datetime.strptime(SrtSetting.get_setting('publish_date_stop','03/15/2019'), '%m/%d/%Y')
+ # set date_stop to 11:59pm for end of 'incusive' day
+ date_stop = date_stop.replace(hour=11, minute=59)
+ if 'mark-new' == action:
+ for cve_name in cve_list.split(','):
+ _log("xhr_publish_defect2cvesNEW:%s" % (cve_name))
+ cve = Cve.objects.get(name=cve_name)
+ publish_object,created = PublishSet.objects.get_or_create(cve=cve)
+ publish_object.state = PublishSet.PUBLISH_SET_NEW_USER
+ publish_object.reason = remove_mark('Mark_New',publish_object.reason)
+ publish_object.reason = remove_mark('Mark_Updated',publish_object.reason)
+ publish_object.reason += ' Mark_New(%s)' % reason_map[cve_name]
+ publish_object.reason = publish_object.reason.replace(' ',' ').strip()
+ publish_object.save()
+ publishMarkNew(cve_list,reason_map,date_start,date_stop)
+ if 'mark-modified' == action:
+ for cve_name in cve_list.split(','):
+ _log("xhr_publish_defect2cvesMOD:%s" % (cve_name))
+ cve = Cve.objects.get(name=cve_name)
+ publish_object,created = PublishSet.objects.get_or_create(cve=cve)
+ publish_object.state = PublishSet.PUBLISH_SET_MODIFIED_USER
+ publish_object.reason = remove_mark('Mark_New',publish_object.reason)
+ publish_object.reason = remove_mark('Mark_Updated',publish_object.reason)
+ publish_object.reason += ' Mark_Updated(%s)' % reason_map[cve_name]
+ publish_object.reason = publish_object.reason.replace(' ',' ').strip()
+ publish_object.save()
+ publishMarkModified(cve_list,reason_map,date_start,date_stop)
+ if 'unmark' == action:
+ for cve_name in cve_list.split(','):
+ cve = Cve.objects.get(name=cve_name)
+ publish_object,created = PublishSet.objects.get_or_create(cve=cve)
+ publish_object.state = PublishSet.PUBLISH_SET_NONE
+ publish_object.reason = remove_mark('Mark_New',publish_object.reason)
+ publish_object.reason = remove_mark('Mark_Updated',publish_object.reason)
+ publish_object.reason = publish_object.reason.replace(' ',' ').strip()
+ publish_object.save()
+ publishMarkNone(cve_list,date_start,date_stop)
+
+ return_data = {
+ "error": "ok",
+ }
+
+ return HttpResponse(json.dumps( return_data ), content_type = "application/json")
+ except Exception as e:
+ _log("xhr_publish:no(%s)(%s)" % (e,traceback.print_stack()))
+ return HttpResponse(json.dumps({"error":str(e) + "\n"}), content_type = "application/json")
+
def cve_alternates(request, cve_pk):
try:
@@ -1795,6 +2299,15 @@ def cve_alternates(request, cve_pk):
cve_source_object,created = CveSource.objects.get_or_create(cve=cve_object,datasource=ds)
_log("Alternate CVE source %s for %s (created=%s)" % (ds.key,cve_object.name,created))
+ # Force update the CVE summary data from sources
+ result_returncode,result_stdout,result_stderr = execute_process(
+ './bin/nist/srtool_nist.py',
+ '--update-cve-list',
+ cve_object.name,
+ '--force'
+ )
+ _log("CVE_ALT_REFRESH=%s|%s|%s" % (result_returncode,result_stdout,result_stderr))
+
return redirect(cve, cve_pk)
diff --git a/lib/srtmain/management/commands/update.py b/lib/srtmain/management/commands/update.py
index 8304e199..7da17acd 100755
--- a/lib/srtmain/management/commands/update.py
+++ b/lib/srtmain/management/commands/update.py
@@ -7,36 +7,45 @@ class Command(BaseCommand):
help = "Trigger a data source update"
def add_arguments(self, parser):
+ print("UPDATE:add_arguments")
parser.add_argument('--cron-start', action='store_const', const='cron_start', dest='command', help='Start the SRTool backgroud updater')
parser.add_argument('--cron-stop', action='store_const', const='cron_stop', dest='command', help='Stop the SRTool backgroud updater')
parser.add_argument('--list', '-l', action='store_const', const='list', dest='command', help='List data sources')
parser.add_argument('--run-updates', '-u', action='store_const', const='run-updates', dest='command', help='update scheduled data sources')
parser.add_argument('--force', '-f', action='store_true', dest='force', help='Force the update')
- parser.add_argument('--name-filter', '-n', nargs='+', type=str, dest='name_filter', help='Filter for datasource name')
+ parser.add_argument('--update-skip-history', '-H', action='store_true', dest='update_skip_history', help='Skip history updates for cummulative status')
parser.add_argument('--verbose', action='store_true', dest='verbose', help='Debugging: verbose output')
parser.add_argument('--trial', '-t', action='store_true', dest='is_trial', help='Debugging: trial run')
+ # NOTE: we have to do shenanigans with name_filter to support spaces
+ parser.add_argument('--name-filter', '-n', nargs='+', dest='name_filter', help='Filter for datasource name')
+
def handle(self, *args, **options):
- #print("UPDATE:%s|%s" % (str(args),str(options)))
+ print("UPDATE:%s|%s" % (str(args),str(options)))
command = ''
if 'cron_start' == options['command']: command = '--cron-start'
if 'cron_stop' == options['command']: command = '--cron-stop'
if 'list' == options['command']: command = '--list'
if 'run-updates' == options['command']: command = '--run-updates'
-
- # NOTE: we have to do shenanigans with name_filter to support spaces
- name_filter = '--name-filter "%s"' % ' '.join(options['name_filter']) if options['name_filter'] else ''
-
- force = '--force' if options['force'] else ''
- is_trial = '--trial' if options['is_trial'] else ''
- verbose = '--verbose' if options['verbose'] or (options['verbosity'] > 1) else ''
- context = '> /dev/null 2>&1 &' if 'cron_start' == options['command'] else ''
-
- update_command = "./bin/common/srtool_update.py %s %s %s %s %s %s" % (command,name_filter,force,is_trial,verbose,context)
- if verbose:
- print("RUN UPDATE SCRIPT: %s" % (update_command))
- os.chdir(os.environ['SRT_BASE_DIR'])
- os.system("%s" % (update_command))
+ if not command:
+ print("manage update: missing command '%s %s'" % (str(args),str(options)))
+ else:
+ if options['verbose'] or (options['verbosity'] > 1):
+ command += ' --verbose'
+ verbose = True
+ else:
+ verbose = False
+ if options['force']: command += ' --force'
+ if options['update_skip_history']: command += ' --update-skip-history'
+ if options['is_trial']: command += ' --trial'
+ # NOTE: we have to do shenanigans with name_filter to support spaces
+ if options['name_filter']: command += ' --name-filter "%s"' % ' '.join(options['name_filter'])
+ if 'cron_start' == options['command']: command += ' > /dev/null 2>&1 &'
+ update_command = "./bin/common/srtool_update.py %s" % (command)
+ if verbose:
+ print("RUN UPDATE SCRIPT: %s" % (update_command))
+ os.chdir(os.environ['SRT_BASE_DIR'])
+ os.system("%s" % (update_command))
diff --git a/lib/users/models.py b/lib/users/models.py
index e91f317b..b59f9fee 100755
--- a/lib/users/models.py
+++ b/lib/users/models.py
@@ -49,10 +49,25 @@ class SrtUser(AbstractUser):
def get_groups(self):
groups = [ group.name for group in self.groups.all() ]
if not groups:
+ # Dynamically assign a group if none attached, in particular
+ # for (super)users created on command line
if self.is_superuser:
- return 'Superuser'
+ command_line_fixup = False
+ if not self.role:
+ self.role = "Superuser"
+ command_line_fixup = True
+ if not self.last_name:
+ self.last_name = self.username
+ command_line_fixup = True
+ if command_line_fixup:
+ self.save()
+ group = Group.objects.get(name = 'Admin')
+ group.user_set.add(self)
+ return group.name
else:
- return ''
+ group = Group.objects.get(name = 'Reader')
+ group.user_set.add(self)
+ return group.name
return ",".join(groups)
@property
def get_group_perm(self):