From b6f127d452b266f69bdf581f9b803d7bba7eeb27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?In=C3=AAs=20Martins?= <123470320+inesmartins-swordhealth@users.noreply.github.com> Date: Thu, 16 Nov 2023 11:04:07 +0000 Subject: [PATCH] Adds triage date field to finding (#95) --- .../0192_add_finding_verified_date.py | 22 +++++++++++++++++++ dojo/filters.py | 7 ++++++ dojo/finding/helper.py | 1 + dojo/finding/views.py | 9 ++++++++ dojo/forms.py | 4 ++-- dojo/models.py | 6 +++++ dojo/utils.py | 1 + 7 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 dojo/db_migrations/0192_add_finding_verified_date.py diff --git a/dojo/db_migrations/0192_add_finding_verified_date.py b/dojo/db_migrations/0192_add_finding_verified_date.py new file mode 100644 index 0000000000..5544b3a703 --- /dev/null +++ b/dojo/db_migrations/0192_add_finding_verified_date.py @@ -0,0 +1,22 @@ +# Generated by Django 4.1.13 on 2023-11-14 17:22 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dojo', '0191_alter_notifications_risk_acceptance_expiration'), + ] + + operations = [ + migrations.AddField( + model_name='finding', + name='verified_date', + field=models.DateTimeField(blank=True, editable=False, help_text='Stores the date when finding was verified.', null=True, verbose_name='Verification Date'), + ), + migrations.AddIndex( + model_name='finding', + index=models.Index(fields=['verified_date'], name='dojo_findin_verifie_34fe49_idx'), + ), + ] diff --git a/dojo/filters.py b/dojo/filters.py index 131184add0..951b0fc896 100644 --- a/dojo/filters.py +++ b/dojo/filters.py @@ -307,6 +307,7 @@ def get_finding_filterset_fields(metrics=False, similar=False): 'status', 'active', 'verified', + 'verified_date', 'duplicate', 'is_mitigated', 'out_of_scope', @@ -1199,6 +1200,7 @@ class ApiFindingFilter(DojoFilter): jira_change = DateRangeFilter(field_name='jira_issue__jira_change') last_reviewed = DateRangeFilter() mitigated = DateRangeFilter() + verified_date = DateRangeFilter() # NumberInFilter cwe = NumberInFilter(field_name='cwe', lookup_expr='in') defect_review_requested_by = NumberInFilter(field_name='defect_review_requested_by', lookup_expr='in') @@ -1276,6 +1278,7 @@ class ApiFindingFilter(DojoFilter): ('under_defect_review', 'under_defect_review'), ('under_review', 'under_review'), ('verified', 'verified'), + ('verified_date', 'verified_date') ), ) @@ -1301,6 +1304,7 @@ class FindingFilter(FindingFilterWithTags): duplicate = ReportBooleanFilter() is_mitigated = ReportBooleanFilter() mitigated = DateRangeFilter(label="Mitigated Date") + verified_date = DateRangeFilter(label="Verified Date") planned_remediation_date = DateRangeOmniFilter() planned_remediation_version = CharFilter(lookup_expr='icontains', label=_('Planned remediation version')) @@ -1432,6 +1436,7 @@ class FindingFilter(FindingFilterWithTags): ('numerical_severity', 'numerical_severity'), ('date', 'date'), ('mitigated', 'mitigated'), + ('verified_date', 'verified_date'), ('risk_acceptance__created__date', 'risk_acceptance__created__date'), ('last_reviewed', 'last_reviewed'), @@ -1445,6 +1450,7 @@ class FindingFilter(FindingFilterWithTags): 'date': 'Date', 'risk_acceptance__created__date': 'Acceptance Date', 'mitigated': 'Mitigated Date', + 'verified_date': 'Verified Date', 'title': 'Finding Name', 'test__engagement__product__name': 'Product Name', } @@ -2110,6 +2116,7 @@ class ReportFindingFilter(FindingFilterWithTags): active = ReportBooleanFilter() is_mitigated = ReportBooleanFilter() mitigated = DateRangeFilter(label="Mitigated Date") + verified_date = DateRangeFilter(label="Verified Date") verified = ReportBooleanFilter() false_p = ReportBooleanFilter(label="False Positive") risk_acceptance = ReportRiskAcceptanceFilter( diff --git a/dojo/finding/helper.py b/dojo/finding/helper.py index d2d1bd4707..b5553139d8 100644 --- a/dojo/finding/helper.py +++ b/dojo/finding/helper.py @@ -59,6 +59,7 @@ def pre_save_finding_status_change(sender, instance, changed_fields=None, **kwar "id", "active", "verified", + "verified_date", "false_p", "is_mitigated", "mitigated", diff --git a/dojo/finding/views.py b/dojo/finding/views.py index 3610ef405b..810d20fbef 100644 --- a/dojo/finding/views.py +++ b/dojo/finding/views.py @@ -967,6 +967,9 @@ def process_finding_form(self, request: HttpRequest, finding: Finding, context: new_finding.last_reviewed = timezone.now() new_finding.last_reviewed_by = request.user new_finding.tags = context["form"].cleaned_data["tags"] + # Handle verified date + if new_finding.verified is True and new_finding.verified_date is None: + new_finding.verified_date = timezone.now() # Handle group related things if "group" in context["form"].cleaned_data: finding_group = context["form"].cleaned_data["group"] @@ -1338,6 +1341,8 @@ def defect_finding_review(request, fid): else: finding.active = True finding.verified = True + if finding.verified_date is None: + finding.verified_date = now finding.mitigated = None finding.mitigated_by = None finding.is_mitigated = False @@ -2084,6 +2089,8 @@ def promote_to_finding(request, fid): new_finding.duplicate = False new_finding.mitigated = None new_finding.verified = True + if new_finding.verified_date is None: + new_finding.verified_date = timezone.now() new_finding.out_of_scope = False new_finding.save() @@ -2774,6 +2781,8 @@ def finding_bulk_update_all(request, pid=None): # logger.debug('setting status from bulk edit form: %s', form) find.active = form.cleaned_data["active"] find.verified = form.cleaned_data["verified"] + if find.verified is True and find.verified_date is None: + find.verified_date = timezone.now() find.false_p = form.cleaned_data["false_p"] find.out_of_scope = form.cleaned_data["out_of_scope"] find.is_mitigated = form.cleaned_data["is_mitigated"] diff --git a/dojo/forms.py b/dojo/forms.py index e2d31684fc..3f0c528452 100755 --- a/dojo/forms.py +++ b/dojo/forms.py @@ -1126,10 +1126,10 @@ class PromoteFindingForm(forms.ModelForm): widget=forms.widgets.Textarea(attrs={'rows': '3', 'cols': '400'})) references = forms.CharField(widget=forms.Textarea, required=False) - # the onyl reliable way without hacking internal fields to get predicatble ordering is to make it explicit + # the only reliable way without hacking internal fields to get predictable ordering is to make it explicit field_order = ('title', 'group', 'date', 'sla_start_date', 'cwe', 'vulnerability_ids', 'severity', 'cvssv3', 'cvssv3_score', 'description', 'mitigation', 'impact', 'request', 'response', 'steps_to_reproduce', 'severity_justification', 'endpoints', 'endpoints_to_add', 'references', - 'active', 'mitigated', 'mitigated_by', 'verified', 'false_p', 'duplicate', + 'active', 'mitigated', 'mitigated_by', 'verified', 'verified_date', 'false_p', 'duplicate', 'out_of_scope', 'risk_accept', 'under_defect_review') def __init__(self, *args, **kwargs): diff --git a/dojo/models.py b/dojo/models.py index 7f50cbb37d..3113197140 100755 --- a/dojo/models.py +++ b/dojo/models.py @@ -2203,6 +2203,11 @@ class Finding(models.Model): verified = models.BooleanField(default=False, verbose_name=_('Verified'), help_text=_("Denotes if this flaw has been manually verified by the tester.")) + verified_date = models.DateTimeField(editable=False, + null=True, + blank=True, + verbose_name=_('Verification Date'), + help_text=_("Stores the date when finding was verified.")) false_p = models.BooleanField(default=False, verbose_name=_('False Positive'), help_text=_("Denotes if this flaw has been deemed a false positive by the tester.")) @@ -2456,6 +2461,7 @@ class Meta: models.Index(fields=['out_of_scope']), models.Index(fields=['false_p']), models.Index(fields=['verified']), + models.Index(fields=['verified_date']), models.Index(fields=['mitigated']), models.Index(fields=['active']), models.Index(fields=['numerical_severity']), diff --git a/dojo/utils.py b/dojo/utils.py index 62b505e614..9c31558833 100644 --- a/dojo/utils.py +++ b/dojo/utils.py @@ -522,6 +522,7 @@ def set_duplicate_reopen(new_finding, existing_finding): existing_finding.is_mitigated = new_finding.is_mitigated existing_finding.active = new_finding.active existing_finding.verified = new_finding.verified + existing_finding.verified_date = new_finding.verified_date existing_finding.notes.create(author=existing_finding.reporter, entry="This finding has been automatically re-opened as it was found in recent scans.") existing_finding.save()