From 11f13092c502c8646f6e76d418efe0b8d90e663f Mon Sep 17 00:00:00 2001 From: hblankenship Date: Tue, 22 Oct 2024 13:17:04 -0500 Subject: [PATCH] initial changes, not seeing good test results --- ...ttings_enforce_verified_status_and_more.py | 23 ++++++++++ dojo/forms.py | 2 +- dojo/github.py | 2 +- dojo/jira_link/helper.py | 8 ++-- .../management/commands/jira_async_updates.py | 8 +++- .../commands/push_to_jira_update.py | 5 ++- dojo/metrics/utils.py | 5 ++- dojo/metrics/views.py | 33 ++++++++++----- dojo/models.py | 42 +++++++++++++++---- dojo/reports/views.py | 15 +++++-- dojo/reports/widgets.py | 5 ++- dojo/utils.py | 16 ++++--- 12 files changed, 128 insertions(+), 36 deletions(-) create mode 100644 dojo/db_migrations/0218_system_settings_enforce_verified_status_and_more.py diff --git a/dojo/db_migrations/0218_system_settings_enforce_verified_status_and_more.py b/dojo/db_migrations/0218_system_settings_enforce_verified_status_and_more.py new file mode 100644 index 0000000000..1e47839ef2 --- /dev/null +++ b/dojo/db_migrations/0218_system_settings_enforce_verified_status_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 5.0.9 on 2024-10-22 14:12 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dojo', '0217_jira_project_enabled'), + ] + + operations = [ + migrations.AddField( + model_name='system_settings', + name='enforce_verified_status', + field=models.BooleanField(default=True, help_text='When enabled, features such as product grading, jira integration, metrics, and reports will only interact with verified findings.', verbose_name='Enforce Verified Status'), + ), + migrations.AlterField( + model_name='jira_project', + name='push_all_issues', + field=models.BooleanField(blank=True, default=False, help_text='Automatically create JIRA tickets for verified findings, assuming enforce_verified_status is True, or for all findings otherwise. Once linked, the JIRA ticket will continue to sync, regardless of status in DefectDojo.'), + ), + ] diff --git a/dojo/forms.py b/dojo/forms.py index 6fe83668d1..372224a4b3 100644 --- a/dojo/forms.py +++ b/dojo/forms.py @@ -3062,7 +3062,7 @@ def clean(self): elif self.cleaned_data.get("push_to_jira", None): active = self.finding_form["active"].value() verified = self.finding_form["verified"].value() - if not active or not verified: + if not active or (not verified and get_system_setting("enforce_verified_status", True)): logger.debug("Findings must be active and verified to be pushed to JIRA") error_message = "Findings must be active and verified to be pushed to JIRA" self.add_error("push_to_jira", ValidationError(error_message, code="not_active_or_verified")) diff --git a/dojo/github.py b/dojo/github.py index 5fe1ca35c1..dc1f865b12 100644 --- a/dojo/github.py +++ b/dojo/github.py @@ -123,7 +123,7 @@ def add_external_issue_github(find, prod, eng): github_conf = github_pkey.git_conf # We push only active and verified issues - if "Active" in find.status() and "Verified" in find.status(): + if "Active" in find.status() and ("Verified" in find.status() and get_system_setting("enforce_verified_status", True)): eng = Engagement.objects.get(test=find.test) prod = Product.objects.get(engagement=eng) github_product_key = GITHUB_PKey.objects.get(product=prod) diff --git a/dojo/jira_link/helper.py b/dojo/jira_link/helper.py index fb0eab686e..d9ca700ed9 100644 --- a/dojo/jira_link/helper.py +++ b/dojo/jira_link/helper.py @@ -145,9 +145,11 @@ def can_be_pushed_to_jira(obj, form=None): logger.debug("can_be_pushed_to_jira: %s, %s, %s", active, verified, severity) - if not active or not verified: - logger.debug("Findings must be active and verified to be pushed to JIRA") - return False, "Findings must be active and verified to be pushed to JIRA", "not_active_or_verified" + isenforced = get_system_setting("enforce_verified_status", True) + + if not active or (not verified and isenforced): + logger.debug("Findings must be active and verified, if enforced by system settings, to be pushed to JIRA") + return False, "Findings must be active and verified, if enforced by system settings, to be pushed to JIRA", "not_active_or_verified" jira_minimum_threshold = None if System_Settings.objects.get().jira_minimum_severity: diff --git a/dojo/management/commands/jira_async_updates.py b/dojo/management/commands/jira_async_updates.py index 78ea8cef6c..a967f1ca99 100644 --- a/dojo/management/commands/jira_async_updates.py +++ b/dojo/management/commands/jira_async_updates.py @@ -1,11 +1,12 @@ import logging from django.core.management.base import BaseCommand -from django.utils import timezone +from django.utils import get_system_setting from jira.exceptions import JIRAError import dojo.jira_link.helper as jira_helper from dojo.models import Dojo_User, Finding, Notes, User +from dojo.utils import timezone """ Author: Aaron Weaver @@ -22,7 +23,10 @@ class Command(BaseCommand): def handle(self, *args, **options): findings = Finding.objects.exclude(jira_issue__isnull=True) - findings = findings.filter(verified=True, active=True) + isverified = get_system_setting("enforce_verified_status", True) + if not isverified: + isverified = None + findings = findings.filter(verified=isverified, active=True) findings = findings.prefetch_related("jira_issue") # finding = Finding.objects.get(id=1) for finding in findings: diff --git a/dojo/management/commands/push_to_jira_update.py b/dojo/management/commands/push_to_jira_update.py index c44fbe68f1..da394eb4a3 100644 --- a/dojo/management/commands/push_to_jira_update.py +++ b/dojo/management/commands/push_to_jira_update.py @@ -23,7 +23,10 @@ class Command(BaseCommand): def handle(self, *args, **options): findings = Finding.objects.exclude(jira_issue__isnull=True) - findings = findings.filter(verified=True, active=True) + isverified = get_system_setting("enforce_verified_status", True) + if not isverified: + isverified = None + findings = findings.filter(verified=isverified, active=True) for finding in findings: logger.info("Checking issue:" + str(finding.id)) diff --git a/dojo/metrics/utils.py b/dojo/metrics/utils.py index 191c454b6e..68deb9402f 100644 --- a/dojo/metrics/utils.py +++ b/dojo/metrics/utils.py @@ -108,7 +108,10 @@ def finding_queries( weekly_counts = query_counts_for_period(MetricsPeriod.WEEK, weeks_between) top_ten = get_authorized_products(Permissions.Product_View) - top_ten = top_ten.filter(engagement__test__finding__verified=True, + isverified = get_system_setting("enforce_verified_status", True) + if not isverified: + isverified = None + top_ten = top_ten.filter(engagement__test__finding__verified=isverified, engagement__test__finding__false_p=False, engagement__test__finding__duplicate=False, engagement__test__finding__out_of_scope=False, diff --git a/dojo/metrics/views.py b/dojo/metrics/views.py index 631130b58d..a98c94ac8a 100644 --- a/dojo/metrics/views.py +++ b/dojo/metrics/views.py @@ -189,8 +189,11 @@ def simple_metrics(request): total_opened = [] findings_broken_out = {} + isverified = get_system_setting("enforce_verified_status", True) + if not isverified: + isverified = None total = Finding.objects.filter(test__engagement__product__prod_type=pt, - verified=True, + verified=isverified, false_p=False, duplicate=False, out_of_scope=False, @@ -304,8 +307,12 @@ def product_type_counts(request): then=Value(1)), output_field=IntegerField())))["total"] + isverified = get_system_setting("enforce_verified_status", True) + if not isverified: + isverified = None + overall_in_pt = Finding.objects.filter(date__lt=end_date, - verified=True, + verified=isverified, false_p=False, duplicate=False, out_of_scope=False, @@ -315,7 +322,7 @@ def product_type_counts(request): "numerical_severity").annotate(Count("numerical_severity")).order_by("numerical_severity") total_overall_in_pt = Finding.objects.filter(date__lte=end_date, - verified=True, + verified=isverified, false_p=False, duplicate=False, out_of_scope=False, @@ -328,7 +335,7 @@ def product_type_counts(request): output_field=IntegerField())))["total"] all_current_in_pt = Finding.objects.filter(date__lte=end_date, - verified=True, + verified=isverified, false_p=False, duplicate=False, out_of_scope=False, @@ -343,7 +350,7 @@ def product_type_counts(request): "numerical_severity") top_ten = Product.objects.filter(engagement__test__finding__date__lte=end_date, - engagement__test__finding__verified=True, + engagement__test__finding__verified=isverified, engagement__test__finding__false_p=False, engagement__test__finding__duplicate=False, engagement__test__finding__out_of_scope=False, @@ -459,8 +466,11 @@ def product_tag_counts(request): then=Value(1)), output_field=IntegerField())))["total"] + isverified = get_system_setting("enforce_verified_status", True) + if not isverified: + isverified = None overall_in_pt = Finding.objects.filter(date__lt=end_date, - verified=True, + verified=isverified, false_p=False, duplicate=False, out_of_scope=False, @@ -471,7 +481,7 @@ def product_tag_counts(request): "numerical_severity").annotate(Count("numerical_severity")).order_by("numerical_severity") total_overall_in_pt = Finding.objects.filter(date__lte=end_date, - verified=True, + verified=isverified, false_p=False, duplicate=False, out_of_scope=False, @@ -485,7 +495,7 @@ def product_tag_counts(request): output_field=IntegerField())))["total"] all_current_in_pt = Finding.objects.filter(date__lte=end_date, - verified=True, + verified=isverified, false_p=False, duplicate=False, out_of_scope=False, @@ -501,7 +511,7 @@ def product_tag_counts(request): "numerical_severity") top_ten = Product.objects.filter(engagement__test__finding__date__lte=end_date, - engagement__test__finding__verified=True, + engagement__test__finding__verified=isverified, engagement__test__finding__false_p=False, engagement__test__finding__duplicate=False, engagement__test__finding__out_of_scope=False, @@ -586,7 +596,10 @@ def view_engineer(request, eid): raise PermissionDenied now = timezone.now() - findings = Finding.objects.filter(reporter=user, verified=True) + isverified = get_system_setting("enforce_verified_status", True) + if not isverified: + isverified = None + findings = Finding.objects.filter(reporter=user, verified=isverified) closed_findings = Finding.objects.filter(mitigated_by=user) open_findings = findings.exclude(mitigated__isnull=False) open_month = findings.filter(date__year=now.year, date__month=now.month) diff --git a/dojo/models.py b/dojo/models.py index a5bbe7c01f..b89fbb4361 100644 --- a/dojo/models.py +++ b/dojo/models.py @@ -359,6 +359,15 @@ class System_Settings(models.Model): webhooks_notifications_timeout = models.IntegerField(default=10, help_text=_("How many seconds will DefectDojo waits for response from webhook endpoint")) + enforce_verified_status = models.BooleanField( + default=True, + verbose_name=_("Enforce Verified Status"), + help_text=_("When enabled, features such as product grading, jira " + "integration, metrics, and reports will only interact " + "with verified findings.", + ), + ) + false_positive_history = models.BooleanField( default=False, help_text=_( "(EXPERIMENTAL) DefectDojo will automatically mark the finding as a " @@ -1196,9 +1205,14 @@ def endpoint_count(self): def open_findings(self, start_date=None, end_date=None): if start_date is None or end_date is None: return {} + isverified = True + from dojo.utils import get_system_setting + if not get_system_setting("enforce_verified_status", True): + isverified = None + critical = Finding.objects.filter(test__engagement__product=self, mitigated__isnull=True, - verified=True, + verified=isverified, false_p=False, duplicate=False, out_of_scope=False, @@ -1207,7 +1221,7 @@ def open_findings(self, start_date=None, end_date=None): end_date]).count() high = Finding.objects.filter(test__engagement__product=self, mitigated__isnull=True, - verified=True, + verified=isverified, false_p=False, duplicate=False, out_of_scope=False, @@ -1216,7 +1230,7 @@ def open_findings(self, start_date=None, end_date=None): end_date]).count() medium = Finding.objects.filter(test__engagement__product=self, mitigated__isnull=True, - verified=True, + verified=isverified, false_p=False, duplicate=False, out_of_scope=False, @@ -1225,7 +1239,7 @@ def open_findings(self, start_date=None, end_date=None): end_date]).count() low = Finding.objects.filter(test__engagement__product=self, mitigated__isnull=True, - verified=True, + verified=isverified, false_p=False, duplicate=False, out_of_scope=False, @@ -1544,7 +1558,11 @@ def get_breadcrumbs(self): # only used by bulk risk acceptance api @property def unaccepted_open_findings(self): - return Finding.objects.filter(risk_accepted=False, active=True, verified=True, duplicate=False, test__engagement=self) + from dojo.utils import get_system_setting + isverified = get_system_setting("enforce_verified_status", True) + if not isverified: + isverified = None + return Finding.objects.filter(risk_accepted=False, active=True, verified=isverified, duplicate=False, test__engagement=self) def accept_risks(self, accepted_risks): self.risk_acceptance.add(*accepted_risks) @@ -2114,7 +2132,11 @@ def copy(self, engagement=None): # only used by bulk risk acceptance api @property def unaccepted_open_findings(self): - return Finding.objects.filter(risk_accepted=False, active=True, verified=True, duplicate=False, test=self) + from dojo.utils import get_system_setting + isverified = get_system_setting("enforce_verified_status", True) + if not isverified: + isverified = None + return Finding.objects.filter(risk_accepted=False, active=True, verified=isverified, duplicate=False, test=self) def accept_risks(self, accepted_risks): self.engagement.risk_acceptance.add(*accepted_risks) @@ -2738,7 +2760,11 @@ def delete(self, *args, **kwargs): # only used by bulk risk acceptance api @classmethod def unaccepted_open_findings(cls): - return cls.objects.filter(active=True, verified=True, duplicate=False, risk_accepted=False) + from dojo.utils import get_system_setting + isverified = get_system_setting("enforce_verified_status", True) + if not isverified: + isverified = None + return cls.objects.filter(active=True, verified=isverified, duplicate=False, risk_accepted=False) @property def risk_acceptance(self): @@ -3912,7 +3938,7 @@ class JIRA_Project(models.Model): verbose_name=_("Add vulnerability Id as a JIRA label"), blank=False) push_all_issues = models.BooleanField(default=False, blank=True, - help_text=_("Automatically create JIRA tickets for verified findings. Once linked, the JIRA ticket will continue to sync, regardless of status in DefectDojo.")) + help_text=_("Automatically create JIRA tickets for verified findings, assuming enforce_verified_status is True, or for all findings otherwise. Once linked, the JIRA ticket will continue to sync, regardless of status in DefectDojo.")) enable_engagement_epic_mapping = models.BooleanField(default=False, blank=True) epic_issue_type_name = models.CharField(max_length=64, blank=True, default="Epic", help_text=_("The name of the of structure that represents an Epic")) diff --git a/dojo/reports/views.py b/dojo/reports/views.py index aacf436933..0ca67851b7 100644 --- a/dojo/reports/views.py +++ b/dojo/reports/views.py @@ -84,8 +84,11 @@ def get_findings(self, request: HttpRequest): return filter_class(self.request.GET, queryset=findings) def get_endpoints(self, request: HttpRequest): + isverified = get_system_setting("enforce_verified_status", True) + if not isverified: + isverified = None endpoints = Endpoint.objects.filter(finding__active=True, - finding__verified=True, + finding__verified=isverified, finding__false_p=False, finding__duplicate=False, finding__out_of_scope=False, @@ -186,8 +189,11 @@ def report_findings(request): def report_endpoints(request): + isverified = get_system_setting("enforce_verified_status", True) + if not isverified: + isverified = None endpoints = Endpoint.objects.filter(finding__active=True, - finding__verified=True, + finding__verified=isverified, finding__false_p=False, finding__duplicate=False, finding__out_of_scope=False, @@ -261,9 +267,12 @@ def endpoint_host_report(request, eid): @user_is_authorized(Product, Permissions.Product_View, "pid") def product_endpoint_report(request, pid): product = get_object_or_404(Product.objects.all().prefetch_related("engagement_set__test_set__test_type", "engagement_set__test_set__environment"), id=pid) + isverified = get_system_setting("enforce_verified_status", True) + if not isverified: + isverified = None endpoint_ids = Endpoint.objects.filter(product=product, finding__active=True, - finding__verified=True, + finding__verified=isverified, finding__false_p=False, finding__duplicate=False, finding__out_of_scope=False, diff --git a/dojo/reports/widgets.py b/dojo/reports/widgets.py index 828adada0a..b720beb59a 100644 --- a/dojo/reports/widgets.py +++ b/dojo/reports/widgets.py @@ -367,12 +367,15 @@ def report_widget_factory(json_data=None, request=None, user=None, finding_notes host=None): selected_widgets = OrderedDict() widgets = json.loads(json_data) + isverified = get_system_setting("enforce_verified_status", True) + if not isverified: + isverified = None for idx, widget in enumerate(widgets): if list(widget.keys())[0] == "page-break": selected_widgets[list(widget.keys())[0] + "-" + str(idx)] = PageBreak() if list(widget.keys())[0] == "endpoint-list": endpoints = Endpoint.objects.filter(finding__active=True, - finding__verified=True, + finding__verified=isverified, finding__false_p=False, finding__duplicate=False, finding__out_of_scope=False, diff --git a/dojo/utils.py b/dojo/utils.py index 470d860772..e8552b6488 100644 --- a/dojo/utils.py +++ b/dojo/utils.py @@ -1136,10 +1136,13 @@ def opened_in_period(start_date, end_date, **kwargs): end_date.month, end_date.day, tzinfo=timezone.get_current_timezone()) + isverified = get_system_setting("enforce_verified_status", True) + if not isverified: + isverified = None opened_in_period = Finding.objects.filter( date__range=[start_date, end_date], **kwargs, - verified=True, + verified=isverified, false_p=False, duplicate=False, out_of_scope=False, @@ -1151,7 +1154,7 @@ def opened_in_period(start_date, end_date, **kwargs): total_opened_in_period = Finding.objects.filter( date__range=[start_date, end_date], **kwargs, - verified=True, + verified=isverified, false_p=False, duplicate=False, out_of_scope=False, @@ -1193,7 +1196,7 @@ def opened_in_period(start_date, end_date, **kwargs): "to_date_total": Finding.objects.filter( date__lte=end_date.date(), - verified=True, + verified=isverified, false_p=False, duplicate=False, out_of_scope=False, @@ -1515,13 +1518,16 @@ def calculate_grade(product, *args, **kwargs): logger.warning("ignoring calculate product for product None!") return + isverified = get_system_setting("enforce_verified_status", True) + if not isverified: + isverified = None if system_settings.enable_product_grade: - logger.debug("calculating product grade for %s:%s", product.id, product.name) + logger.debug("calculating product grade for %s:%s", product.id, product.name) severity_values = Finding.objects.filter( ~Q(severity="Info"), active=True, duplicate=False, - verified=True, + verified=isverified, false_p=False, test__engagement__product=product).values("severity").annotate( Count("numerical_severity")).order_by()