Skip to content
Open
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
4 changes: 4 additions & 0 deletions dojo/engagement/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1552,6 +1552,10 @@ def engagement_ics(request, eid):
eng = get_object_or_404(Engagement, id=eid)
start_date = datetime.combine(eng.target_start, datetime.min.time())
end_date = datetime.combine(eng.target_end, datetime.max.time())
if timezone.is_naive(start_date):
start_date = timezone.make_aware(start_date)
if timezone.is_naive(end_date):
end_date = timezone.make_aware(end_date)
uid = f"dojo_eng_{eng.id}_{eng.product.id}"
cal = get_cal_event(
start_date,
Expand Down
68 changes: 34 additions & 34 deletions dojo/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
RangeFilter,
)
from django_filters import rest_framework as filters
from django_filters.filters import ChoiceFilter, _truncate
from django_filters.filters import ChoiceFilter
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field
from polymorphic.base import ManagerInheritanceWarning
Expand Down Expand Up @@ -92,7 +92,7 @@
from dojo.risk_acceptance.queries import get_authorized_risk_acceptances
from dojo.test.queries import get_authorized_tests
from dojo.user.queries import get_authorized_users
from dojo.utils import get_system_setting, is_finding_groups_enabled
from dojo.utils import get_system_setting, is_finding_groups_enabled, truncate_timezone_aware

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -194,8 +194,8 @@ def filter(self, qs, value):
if earliest_finding is not None:
start_date = datetime.combine(
earliest_finding.date, datetime.min.time()).replace(tzinfo=tzinfo())
self.start_date = _truncate(start_date - timedelta(days=1))
self.end_date = _truncate(now() + timedelta(days=1))
self.start_date = truncate_timezone_aware(start_date - timedelta(days=1))
self.end_date = truncate_timezone_aware(now() + timedelta(days=1))
try:
value = int(value)
except (ValueError, TypeError):
Expand Down Expand Up @@ -654,16 +654,16 @@ class DateRangeFilter(ChoiceFilter):
f"{name}__day": now().day,
})),
2: (_("Past 7 days"), lambda qs, name: qs.filter(**{
f"{name}__gte": _truncate(now() - timedelta(days=7)),
f"{name}__lt": _truncate(now() + timedelta(days=1)),
f"{name}__gte": truncate_timezone_aware(now() - timedelta(days=7)),
f"{name}__lt": truncate_timezone_aware(now() + timedelta(days=1)),
})),
3: (_("Past 30 days"), lambda qs, name: qs.filter(**{
f"{name}__gte": _truncate(now() - timedelta(days=30)),
f"{name}__lt": _truncate(now() + timedelta(days=1)),
f"{name}__gte": truncate_timezone_aware(now() - timedelta(days=30)),
f"{name}__lt": truncate_timezone_aware(now() + timedelta(days=1)),
})),
4: (_("Past 90 days"), lambda qs, name: qs.filter(**{
f"{name}__gte": _truncate(now() - timedelta(days=90)),
f"{name}__lt": _truncate(now() + timedelta(days=1)),
f"{name}__gte": truncate_timezone_aware(now() - timedelta(days=90)),
f"{name}__lt": truncate_timezone_aware(now() + timedelta(days=1)),
})),
5: (_("Current month"), lambda qs, name: qs.filter(**{
f"{name}__year": now().year,
Expand All @@ -673,8 +673,8 @@ class DateRangeFilter(ChoiceFilter):
f"{name}__year": now().year,
})),
7: (_("Past year"), lambda qs, name: qs.filter(**{
f"{name}__gte": _truncate(now() - timedelta(days=365)),
f"{name}__lt": _truncate(now() + timedelta(days=1)),
f"{name}__gte": truncate_timezone_aware(now() - timedelta(days=365)),
f"{name}__lt": truncate_timezone_aware(now() + timedelta(days=1)),
})),
}

Expand All @@ -700,43 +700,43 @@ class DateRangeOmniFilter(ChoiceFilter):
f"{name}__day": now().day,
})),
2: (_("Next 7 days"), lambda qs, name: qs.filter(**{
f"{name}__gte": _truncate(now() + timedelta(days=1)),
f"{name}__lt": _truncate(now() + timedelta(days=7)),
f"{name}__gte": truncate_timezone_aware(now() + timedelta(days=1)),
f"{name}__lt": truncate_timezone_aware(now() + timedelta(days=7)),
})),
3: (_("Next 30 days"), lambda qs, name: qs.filter(**{
f"{name}__gte": _truncate(now() + timedelta(days=1)),
f"{name}__lt": _truncate(now() + timedelta(days=30)),
f"{name}__gte": truncate_timezone_aware(now() + timedelta(days=1)),
f"{name}__lt": truncate_timezone_aware(now() + timedelta(days=30)),
})),
4: (_("Next 90 days"), lambda qs, name: qs.filter(**{
f"{name}__gte": _truncate(now() + timedelta(days=1)),
f"{name}__lt": _truncate(now() + timedelta(days=90)),
f"{name}__gte": truncate_timezone_aware(now() + timedelta(days=1)),
f"{name}__lt": truncate_timezone_aware(now() + timedelta(days=90)),
})),
5: (_("Past 7 days"), lambda qs, name: qs.filter(**{
f"{name}__gte": _truncate(now() - timedelta(days=7)),
f"{name}__lt": _truncate(now() + timedelta(days=1)),
f"{name}__gte": truncate_timezone_aware(now() - timedelta(days=7)),
f"{name}__lt": truncate_timezone_aware(now() + timedelta(days=1)),
})),
6: (_("Past 30 days"), lambda qs, name: qs.filter(**{
f"{name}__gte": _truncate(now() - timedelta(days=30)),
f"{name}__lt": _truncate(now() + timedelta(days=1)),
f"{name}__gte": truncate_timezone_aware(now() - timedelta(days=30)),
f"{name}__lt": truncate_timezone_aware(now() + timedelta(days=1)),
})),
7: (_("Past 90 days"), lambda qs, name: qs.filter(**{
f"{name}__gte": _truncate(now() - timedelta(days=90)),
f"{name}__lt": _truncate(now() + timedelta(days=1)),
f"{name}__gte": truncate_timezone_aware(now() - timedelta(days=90)),
f"{name}__lt": truncate_timezone_aware(now() + timedelta(days=1)),
})),
8: (_("Current month"), lambda qs, name: qs.filter(**{
f"{name}__year": now().year,
f"{name}__month": now().month,
})),
9: (_("Past year"), lambda qs, name: qs.filter(**{
f"{name}__gte": _truncate(now() - timedelta(days=365)),
f"{name}__lt": _truncate(now() + timedelta(days=1)),
f"{name}__gte": truncate_timezone_aware(now() - timedelta(days=365)),
f"{name}__lt": truncate_timezone_aware(now() + timedelta(days=1)),
})),
10: (_("Current year"), lambda qs, name: qs.filter(**{
f"{name}__year": now().year,
})),
11: (_("Next year"), lambda qs, name: qs.filter(**{
f"{name}__gte": _truncate(now() + timedelta(days=1)),
f"{name}__lt": _truncate(now() + timedelta(days=365)),
f"{name}__gte": truncate_timezone_aware(now() + timedelta(days=1)),
f"{name}__lt": truncate_timezone_aware(now() + timedelta(days=365)),
})),
}

Expand Down Expand Up @@ -818,8 +818,8 @@ def any(self, qs, name):
if earliest_finding is not None:
start_date = datetime.combine(
earliest_finding.date, datetime.min.time()).replace(tzinfo=tzinfo())
self.start_date = _truncate(start_date - timedelta(days=1))
self.end_date = _truncate(now() + timedelta(days=1))
self.start_date = truncate_timezone_aware(start_date - timedelta(days=1))
self.end_date = truncate_timezone_aware(now() + timedelta(days=1))
return qs.all()
return None

Expand All @@ -839,8 +839,8 @@ def current_year(self, qs, name):
})

def past_x_days(self, qs, name, days):
self.start_date = _truncate(now() - timedelta(days=days))
self.end_date = _truncate(now() + timedelta(days=1))
self.start_date = truncate_timezone_aware(now() - timedelta(days=days))
self.end_date = truncate_timezone_aware(now() + timedelta(days=1))
return qs.filter(**{
f"{name}__gte": self.start_date,
f"{name}__lt": self.end_date,
Expand Down Expand Up @@ -884,8 +884,8 @@ def filter(self, qs, value):
if earliest_finding is not None:
start_date = datetime.combine(
earliest_finding.date, datetime.min.time()).replace(tzinfo=tzinfo())
self.start_date = _truncate(start_date - timedelta(days=1))
self.end_date = _truncate(now() + timedelta(days=1))
self.start_date = truncate_timezone_aware(start_date - timedelta(days=1))
self.end_date = truncate_timezone_aware(now() + timedelta(days=1))
try:
value = int(value)
except (ValueError, TypeError):
Expand Down
2 changes: 2 additions & 0 deletions dojo/metrics/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,8 @@ def js_epoch(
"""
if isinstance(d, date):
d = datetime.combine(d, datetime.min.time())
if timezone.is_naive(d):
d = timezone.make_aware(d)
return int(d.timestamp()) * 1000


Expand Down
4 changes: 4 additions & 0 deletions dojo/test/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,10 @@ def test_ics(request, tid):
test = get_object_or_404(Test, id=tid)
start_date = datetime.combine(test.target_start, datetime.min.time())
end_date = datetime.combine(test.target_end, datetime.max.time())
if timezone.is_naive(start_date):
start_date = timezone.make_aware(start_date)
if timezone.is_naive(end_date):
end_date = timezone.make_aware(end_date)
uid = f"dojo_test_{test.id}_{test.engagement.id}_{test.engagement.product.id}"
cal = get_cal_event(
start_date,
Expand Down
15 changes: 8 additions & 7 deletions dojo/tools/cyberwatch_galeax/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import cvss.parser
from cvss.cvss3 import CVSS3
from django.utils import timezone

from dojo.models import Endpoint, Endpoint_Status, Finding

Expand Down Expand Up @@ -202,7 +203,7 @@ def build_findings_for_cve(self, cve_code, c_data, test):
products = c_data["products"]

if not products:
mitigated_date = datetime.now()
mitigated_date = timezone.now()
mitigation = f"Fixed At: {mitigated_date}"
endpoints = [Endpoint(host=e) for e in c_data["no_product_endpoints"]]

Expand Down Expand Up @@ -269,7 +270,7 @@ def determine_product_finding_state(self, p_data):
active_status = any(am[0] for am in p_data["active_mitigated_data"])
mitigated_date = (max(am[1] for am in p_data["active_mitigated_data"] if am[1])
if [am[1] for am in p_data["active_mitigated_data"] if am[1]] and not active_status
else (datetime.now() if not active_status else None))
else (timezone.now() if not active_status else None))
return component_version_str, active_status, mitigated_date

def create_finding(
Expand Down Expand Up @@ -466,7 +467,7 @@ def process_servers_for_security_issue(self, servers):
active_status = True
mitigated_date = None
else:
mitigated_date = datetime.now()
mitigated_date = timezone.now()
mitigated_dates.append(mitigated_date)

detected_at_str = server.get("detected_at")
Expand All @@ -483,15 +484,15 @@ def process_servers_for_security_issue(self, servers):
)
unsaved_endpoint_status.append(endpoint_status)

mitigated_date = (max(mitigated_dates) if mitigated_dates else datetime.now()) if not active_status else None
mitigated_date = (max(mitigated_dates) if mitigated_dates else timezone.now()) if not active_status else None
return unsaved_endpoints, unsaved_endpoint_status, active_status, mitigated_date

def parse_detected_at(self, detected_at_str):
"""Parse the detected_at field for a security issue server."""
try:
return datetime.strptime(detected_at_str, "%Y-%m-%dT%H:%M:%S.%fZ")
except (ValueError, TypeError):
return datetime.now()
return timezone.now()

def parse_fixed_at(self, fixed_at_str):
"""Parse fixed_at datetime, defaulting to now if parsing fails."""
Expand All @@ -500,7 +501,7 @@ def parse_fixed_at(self, fixed_at_str):
return datetime.strptime(fixed_at_str, "%Y-%m-%dT%H:%M:%S.%f%z")
except ValueError as e:
logger.error(f'Error parsing fixed_at date "{fixed_at_str}": {e}')
return datetime.now()
return timezone.now()

def parse_datetime(self, dt_str):
"""Parse a datetime string with fallback to now on error."""
Expand All @@ -509,7 +510,7 @@ def parse_datetime(self, dt_str):
return datetime.strptime(dt_str, "%Y-%m-%dT%H:%M:%S.%f%z")
except (ValueError, TypeError):
logger.error(f'Error parsing datetime "{dt_str}"')
return datetime.now()
return timezone.now()

def parse_cvss(self, cvss_v3_vector, json_data):
if cvss_v3_vector:
Expand Down
21 changes: 20 additions & 1 deletion dojo/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2465,7 +2465,8 @@ def get_open_findings_burndown(product):
findings = Finding.objects.filter(test__engagement__product=product, duplicate=False)
f_list = list(findings)

curr_date = datetime.combine(datetime.now(), datetime.min.time())
curr_date = datetime.combine(timezone.now().date(), datetime.min.time())
curr_date = timezone.make_aware(curr_date)
start_date = curr_date - timedelta(days=90)

critical_count = 0
Expand Down Expand Up @@ -2703,3 +2704,21 @@ def parse_cvss_data(cvss_vector_string: str) -> dict:
}
logger.debug("No valid CVSS3 or CVSS4 vector found in %s", cvss_vector_string)
return {}


def truncate_timezone_aware(dt):
"""
Truncate datetime to date and make it timezone-aware.
This replaces the django_filters._truncate function which creates naive datetimes.
"""
if dt is None:
return None

# Get the date part and create a new datetime at midnight
truncated = datetime.combine(dt.date(), datetime.min.time())

# Make it timezone-aware if it isn't already
if timezone.is_naive(truncated):
truncated = timezone.make_aware(truncated)

return truncated
6 changes: 6 additions & 0 deletions tests/check_various_pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ def test_finding_group_open_filtered_status(self):
driver = self.driver
driver.get(self.base_url + "finding_group/open?name=CVE&severity=Medium&engagement=14&product=6")

def test_date_filter(self):
driver = self.driver
# can result in an error about date not having timezone information
driver.get(self.base_url + "finding/open?last_status_update=2")


def suite():
suite = unittest.TestSuite()
Expand All @@ -42,6 +47,7 @@ def suite():
suite.addTest(VariousPagesTest("test_finding_group_all_status"))
suite.addTest(VariousPagesTest("test_finding_group_closed_status"))
suite.addTest(VariousPagesTest("test_finding_group_open_filtered_status"))
suite.addTest(VariousPagesTest("test_date_filter"))
return suite


Expand Down