Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions dojo/finding/helper.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging
from contextlib import suppress
from datetime import datetime
from time import strftime

from django.conf import settings
Expand All @@ -9,6 +10,7 @@
from django.dispatch.dispatcher import receiver
from django.urls import reverse
from django.utils import timezone
from django.utils.timezone import is_naive, make_aware, now
from fieldsignals import pre_save_changed

import dojo.jira_link.helper as jira_helper
Expand Down Expand Up @@ -740,6 +742,17 @@ def save_vulnerability_ids_template(finding_template, vulnerability_ids):
finding_template.cve = None


def normalize_datetime(value):
"""Ensure value is timezone-aware datetime."""
if value:
if not isinstance(value, datetime):
value = datetime.combine(value, datetime.min.time())
# Make timezone-aware if naive
if is_naive(value):
value = make_aware(value)
return value


def close_finding(
*,
finding,
Expand All @@ -761,15 +774,16 @@ def close_finding(
"""
# Core status updates
finding.is_mitigated = is_mitigated
now = timezone.now()
finding.mitigated = mitigated or now
current_time = now()
mitigated_date = normalize_datetime(mitigated) or current_time
finding.mitigated = mitigated_date
finding.mitigated_by = mitigated_by or user
finding.active = False
finding.false_p = bool(false_p)
finding.out_of_scope = bool(out_of_scope)
finding.duplicate = bool(duplicate)
finding.under_review = False
finding.last_reviewed = finding.mitigated
finding.last_reviewed = mitigated_date
finding.last_reviewed_by = user

# Create note if provided
Expand All @@ -779,16 +793,16 @@ def close_finding(
entry=note_entry,
author=user,
note_type=note_type,
date=finding.mitigated,
date=mitigated_date,
)
finding.notes.add(new_note)

# Endpoint statuses
for status in finding.status_finding.all():
status.mitigated_by = finding.mitigated_by
status.mitigated_time = finding.mitigated
status.mitigated_time = mitigated_date
status.mitigated = True
status.last_modified = timezone.now()
status.last_modified = current_time
status.save()

# Risk acceptance
Expand Down
94 changes: 91 additions & 3 deletions unittests/test_finding_model.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,103 @@
from datetime import datetime, timedelta
from datetime import date, datetime, timedelta

from crum import impersonate

from dojo.models import DojoMeta, Engagement, Finding, Test, User
from django.utils.timezone import is_naive, now

from dojo.finding.helper import close_finding
from dojo.models import (
DojoMeta,
Endpoint,
Endpoint_Status,
Engagement,
Finding,
Note_Type,
Notes,
Product,
Product_Type,
Test,
Test_Type,
User,
)

from .dojo_test_case import DojoTestCase


class TestFindingModel(DojoTestCase):
fixtures = ["dojo_testdata.json"]

def setUp(self):
self.user = User.objects.first() # Use a user from fixtures
self.product_type = Product_Type.objects.create(name="Test Product Type")
self.product = Product.objects.create(name="Test Product", prod_type=self.product_type)
self.engagement = Engagement.objects.create(
name="Test Engagement",
product=self.product,
target_start=now(),
target_end=now(),
)
self.test_type = Test_Type.objects.create(name="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 Test", active=True, test=self.test)
self.endpoint = Endpoint.objects.create(host="test.local")
self.endpoint_status = Endpoint_Status.objects.create(finding=self.finding, endpoint=self.endpoint)
self.finding.status_finding.add(self.endpoint_status)

def test_close_finding_with_naive_date(self):
note_type_obj = Note_Type.objects.create(name="General")
naive_date = date.today() # No timezone
close_finding(
finding=self.finding,
user=self.user,
is_mitigated=True,
mitigated=naive_date,
mitigated_by=None,
false_p=False,
out_of_scope=False,
duplicate=False,
note_entry="Mitigation note",
note_type=note_type_obj,
)
self.assertFalse(is_naive(self.finding.mitigated))
note = Notes.objects.filter(finding=self.finding).first()
self.assertIsNotNone(note)
self.assertFalse(is_naive(note.date))
status = Endpoint_Status.objects.filter(finding=self.finding).first()
self.assertTrue(status.mitigated)
self.assertFalse(is_naive(status.mitigated_time))

def test_close_finding_with_naive_datetime(self):
naive_datetime = datetime(2025, 11, 12, 0, 0, 0)
close_finding(
finding=self.finding,
user=self.user,
is_mitigated=True,
mitigated=naive_datetime,
mitigated_by=None,
false_p=False,
out_of_scope=False,
duplicate=False,
)
self.assertFalse(is_naive(self.finding.mitigated))

def test_close_finding_with_none_mitigated(self):
close_finding(
finding=self.finding,
user=self.user,
is_mitigated=True,
mitigated=None,
mitigated_by=None,
false_p=False,
out_of_scope=False,
duplicate=False,
)
self.assertFalse(is_naive(self.finding.mitigated))

def test_get_sast_source_file_path_with_link_no_file_path(self):
finding = Finding()
self.assertEqual(None, finding.get_sast_source_file_path_with_link())
Expand Down