diff options
Diffstat (limited to 'lib')
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">→</span> <li><a href="{% url 'manage' %}">Management</a></li><span class="divider">→</span> - <li><a href="{% url 'triage_cves' %}">Triage CVE's</a></li><span class="divider">→</span> - <li>Select CVE's</li> + <li><a href="{% url 'triage_cves' %}">Triage CVEs</a></li><span class="divider">→</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}} + ({{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") <input id="create_notifications" type="checkbox" checked> Create Notifications</input> <p><b><big>Reason: </big></b> <input type="text" id="input-isvulnerable-reason" name="reason" size="40"> <input id="markPublishIs" type="checkbox"> Mark for Publish</input> <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"> <input id="markPublishNot" type="checkbox"> Mark for Publish</input> <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> (<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">→</span> + <li><a href="{% url 'manage' %}">Management</a></li><span class="divider">→</span> + <li><a href="{% url 'maintenance' %}">Maintenance</a></li><span class="divider">→</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">→</span> + <li><a href="{% url 'manage' %}">Management</a></li><span class="divider">→</span> + <li><a href="{% url 'maintenance' %}">Maintenance</a></li><span class="divider">→</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">→</span> + <li><a href="{% url 'manage' %}">Management</a></li><span class="divider">→</span> + <li><a href="{% url 'maintenance' %}">Maintenance</a></li><span class="divider">→</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">→</span> + <li><a href="{% url 'manage' %}">Management</a></li><span class="divider">→</span> + <li><a href="{% url 'maintenance' %}">Maintenance</a></li><span class="divider">→</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">→</span> <li><a href="{% url 'manage' %}">Management</a></li><span class="divider">→</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">→</span> + <li><a href="{% url 'manage' %}">Management</a></li><span class="divider">→</span> + <li><a href="{% url 'publish' %}">Publish</a></li><span class="divider">→</span> + <li>Publish 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">→</span> + <li><a href="{% url 'manage' %}">Management</a></li><span class="divider">→</span> + <li><a href="{% url 'publish' %}">Publish</a></li><span class="divider">→</span> + <li>Publish 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">→</span> + <li><a href="{% url 'manage' %}">Management</a></li><span class="divider">→</span> + <li><a href="{% url 'publish' %}">Publish</a></li><span class="divider">→</span> + <li>Publish 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">→</span> <li><a href="{% url 'manage' %}">Management</a></li><span class="divider">→</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}}"> + Stop Date: <input type="text" id="snap_date_stop" value="{{snap_date_stop}}"> + <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}}"> + 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}}, {% 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> <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): |