From 60c86dccb1e0e55906c82840d144702064af7862 Mon Sep 17 00:00:00 2001 From: Cody Maffucci <46459665+Maffooch@users.noreply.github.com> Date: Wed, 15 Jul 2026 12:30:44 -0600 Subject: [PATCH] docs: document DD_EDITABLE_MITIGATED_DATA and hide ignored close-finding fields Audit and document the DD_EDITABLE_MITIGATED_DATA setting, which gates editing of a finding's mitigated date / mitigated by. Editing requires both the flag enabled and a superuser (via can_edit_mitigated_data), and is off by default because backdating a mitigation can distort SLA-compliance metrics. - Add an "Editing the Mitigated Date and Mitigated By" section to the Editing Findings docs covering the default-off behavior, the superuser requirement, the SLA-backdating trade-off, the audit trail, the restart requirement, and a Cloud/Pro contact-support note. - Expand the settings.dist.py comment to note the superuser gate, UI+API scope, SLA rationale, and restart requirement. - Hide the mitigated / mitigated_by fields on CloseFindingForm when the setting is disabled, mirroring FindingForm, so users are no longer shown a Mitigated date input whose value is silently ignored on close. - Add a default-off test covering the hidden fields and that a backdated mitigated value is ignored (finding closes with the current date). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../findings_workflows/editing_findings.md | 11 ++++ dojo/finding/ui/forms.py | 5 ++ dojo/settings/settings.dist.py | 5 +- unittests/test_system_settings.py | 62 +++++++++++++++++++ 4 files changed, 82 insertions(+), 1 deletion(-) diff --git a/docs/content/triage_findings/findings_workflows/editing_findings.md b/docs/content/triage_findings/findings_workflows/editing_findings.md index 368ade22a8d..5e44049bb66 100644 --- a/docs/content/triage_findings/findings_workflows/editing_findings.md +++ b/docs/content/triage_findings/findings_workflows/editing_findings.md @@ -37,6 +37,17 @@ This will open the **Edit Finding** form, where you can edit the metadata, chang ​ * **SAST / DAST** are labels used to organize your Findings into the context they were discovered in. Generally, this label is populated based on the scanning tool used, but you can adjust this to a more accurate level (for example, if the Finding was found by both a SAST and a DAST tool). +### Editing the Mitigated Date and Mitigated By + +By default, a Finding's **Mitigated Date** and **Mitigated By** values are **not editable**. These fields are hidden from both the Edit Finding form and the Close Finding dialog, and the Mitigated Date is always set automatically to the moment the Finding is closed. Attempting to set or backdate these values through the API is rejected for the same reason. + +Editing can be turned on with the `DD_EDITABLE_MITIGATED_DATA` server setting. When it is enabled, the **Mitigated Date** and **Mitigated By** fields appear in the Edit Finding form and the Close Finding dialog, and can also be set through the API — but only for users with **superuser** status. In other words, editing requires *both* the setting to be enabled *and* the acting user to be a superuser. + +* **Why it's off by default:** allowing a mitigation to be backdated can misrepresent SLA compliance — a Finding that was actually remediated *outside* its SLA window could be recorded as though it had been mitigated *within* SLA. Enabling the setting is forward-looking only; it does **not** change the Mitigated Date or age of any existing Findings. +* **Everything stays auditable:** every change to a Finding, including edits to the Mitigated Date and Mitigated By, is captured in the Finding's history log — who made the change, when, and the previous and new values. +* **Applying the setting:** `DD_EDITABLE_MITIGATED_DATA` is a server-level environment variable (see [Configuration](/get_started/open_source/configuration/)). Changing it requires a service restart to take effect. +* **DefectDojo Cloud / Pro:** this setting cannot be changed from the UI. Contact DefectDojo Support to have it enabled for your instance. + ## Bulk Edit Findings Findings can be edited in bulk from a Finding List, which can be found either on the Findings page itself, or from within a Test. diff --git a/dojo/finding/ui/forms.py b/dojo/finding/ui/forms.py index 79f3e317059..76c63bdaf16 100644 --- a/dojo/finding/ui/forms.py +++ b/dojo/finding/ui/forms.py @@ -921,6 +921,11 @@ def __init__(self, *args, **kwargs): self.fields["mitigated_by"].queryset = get_authorized_users("edit") self.fields["mitigated"].initial = self.instance.mitigated self.fields["mitigated_by"].initial = self.instance.mitigated_by + else: + # mitigated data is not editable: hide the fields so users are not + # presented with inputs whose values would be silently ignored + del self.fields["mitigated"] + del self.fields["mitigated_by"] if disclaimer := get_system_setting("disclaimer_notes"): self.disclaimer = disclaimer.strip() diff --git a/dojo/settings/settings.dist.py b/dojo/settings/settings.dist.py index c7db5c0c22a..819052008bc 100644 --- a/dojo/settings/settings.dist.py +++ b/dojo/settings/settings.dist.py @@ -212,7 +212,10 @@ # we limit the amount of duplicates that can be deleted in a single run of that job # to prevent overlapping runs of that job from occurrring DD_DUPE_DELETE_MAX_PER_RUN=(int, 200), - # when enabled 'mitigated date' and 'mitigated by' of a finding become editable + # When enabled, superusers can edit a finding's 'mitigated date' and 'mitigated by' + # fields (e.g. backdate a mitigation) from both the UI and the API. Off by default + # because backdating a mitigation can distort SLA-compliance metrics. Changing this + # value requires a service restart to take effect. DD_EDITABLE_MITIGATED_DATA=(bool, False), # new feature that tracks history across multiple reimports for the same test DD_TRACK_IMPORT_HISTORY=(bool, True), diff --git a/unittests/test_system_settings.py b/unittests/test_system_settings.py index e127bec8f59..9099349a483 100644 --- a/unittests/test_system_settings.py +++ b/unittests/test_system_settings.py @@ -93,6 +93,68 @@ def test_post_request_initializes_form_with_finding_instance(self): self.assertIn(response.status_code, [200, 302]) +@override_settings(DD_EDITABLE_MITIGATED_DATA=False) +class CloseFindingViewMitigatedDataDisabledTest(TestCase): + + """ + When DD_EDITABLE_MITIGATED_DATA is disabled (the default), the Close Finding + form must not expose the mitigated/mitigated_by fields, and any mitigated value + supplied in the payload must be ignored so the finding is closed with the + current date rather than a backdated one. + """ + + def setUp(self): + self.user = User.objects.create_user( + username="tester-mitigated-disabled", + password="pass", # noqa: S106 + is_staff=True, + is_superuser=True, + ) + self.client.force_login(self.user) + self.product_type = Product_Type.objects.create(name="Disabled Product Type") + self.product = Product.objects.create(name="Disabled Product", description="test", prod_type=self.product_type) + self.engagement = Engagement.objects.create( + name="Disabled Engagement", + product=self.product, + target_start=now(), + target_end=now(), + ) + self.test_type = Test_Type.objects.create(name="Disabled Unit Test Type") + self.test = Test.objects.create( + engagement=self.engagement, + test_type=self.test_type, + title="Test for Finding", + target_start=now(), + target_end=now(), + ) + self.finding = Finding.objects.create( + title="Close Finding Disabled Test", + active=True, + test=self.test, + reporter=self.user, + ) + self.url = reverse("close_finding", args=[self.finding.id]) + + def test_close_form_hides_mitigated_fields(self): + response = self.client.get(self.url) + self.assertEqual(response.status_code, 200) + form = response.context["form"] + self.assertNotIn("mitigated", form.fields) + self.assertNotIn("mitigated_by", form.fields) + + def test_backdated_mitigated_is_ignored(self): + response = self.client.post( + self.url, + {"entry": "Closing this finding", "mitigated": "2020-01-01"}, + ) + self.assertEqual(response.status_code, 302) + self.finding.refresh_from_db() + self.assertTrue(self.finding.is_mitigated) + self.assertIsNotNone(self.finding.mitigated) + # the backdated value must be ignored: mitigation date is "now", not 2020 + self.assertEqual(self.finding.mitigated.year, now().year) + + class TestSystemSettingsMiddlewareIntegration(DojoTestCase): """Integration tests for DojoSytemSettingsMiddleware using RequestFactory."""