Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mitigated On/Before/After now use DateTimeFilter #11472

Merged
merged 5 commits into from
Jan 16, 2025
Merged
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
41 changes: 35 additions & 6 deletions dojo/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
CharFilter,
DateFilter,
DateFromToRangeFilter,
DateTimeFilter,
FilterSet,
ModelChoiceFilter,
ModelMultipleChoiceFilter,
Expand Down Expand Up @@ -1456,9 +1457,9 @@ class ApiFindingFilter(DojoFilter):
jira_change = DateRangeFilter(field_name="jira_issue__jira_change")
last_reviewed = DateRangeFilter()
mitigated = DateRangeFilter()
mitigated_on = DateFilter(field_name="mitigated", lookup_expr="exact")
mitigated_before = DateFilter(field_name="mitigated", lookup_expr="lt")
mitigated_after = DateFilter(field_name="mitigated", lookup_expr="gt")
mitigated_on = DateTimeFilter(field_name="mitigated", lookup_expr="exact", method="filter_mitigated_on")
mitigated_before = DateTimeFilter(field_name="mitigated", lookup_expr="lt")
mitigated_after = DateTimeFilter(field_name="mitigated", lookup_expr="gt", label="Mitigated After", method="filter_mitigated_after")
# NumberInFilter
cwe = NumberInFilter(field_name="cwe", lookup_expr="in")
defect_review_requested_by = NumberInFilter(field_name="defect_review_requested_by", lookup_expr="in")
Expand Down Expand Up @@ -1544,6 +1545,20 @@ class Meta:
exclude = ["url", "thread_id", "notes", "files",
"line", "cve"]

def filter_mitigated_after(self, queryset, name, value):
if value.hour == 0 and value.minute == 0 and value.second == 0:
value = value.replace(hour=23, minute=59, second=59)
Comment on lines +1549 to +1550
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic is a little trickier when it comes to the API, because the user could intentionally query for "after 2025-01-01 00:00:00"... But I think this approach strikes the right balance - it seems much more likely that we'll receive dates without timestamps than dates with explicit 00:00:00 timestamps, and this would mirror the UI's behavior as well. In any case, the user can always search for "after 2024-12-31" to achieve the same thing if that's truly what they want.


return queryset.filter(mitigated__gt=value)

def filter_mitigated_on(self, queryset, name, value):
if value.hour == 0 and value.minute == 0 and value.second == 0:
# we have a simple date without a time, lets get a range from this morning to tonight at 23:59:59:999
nextday = value + timedelta(days=1)
return queryset.filter(mitigated__gte=value, mitigated__lt=nextday)

return queryset.filter(mitigated=value)


class PercentageFilter(NumberFilter):
def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -1587,9 +1602,9 @@ class FindingFilterHelper(FilterSet):
duplicate = ReportBooleanFilter()
is_mitigated = ReportBooleanFilter()
mitigated = DateRangeFilter(field_name="mitigated", label="Mitigated Date")
mitigated_on = DateFilter(field_name="mitigated", lookup_expr="exact", label="Mitigated On")
mitigated_before = DateFilter(field_name="mitigated", lookup_expr="lt", label="Mitigated Before")
mitigated_after = DateFilter(field_name="mitigated", lookup_expr="gt", label="Mitigated After")
mitigated_on = DateTimeFilter(field_name="mitigated", lookup_expr="exact", label="Mitigated On", method="filter_mitigated_on")
mitigated_before = DateTimeFilter(field_name="mitigated", lookup_expr="lt", label="Mitigated Before")
mitigated_after = DateTimeFilter(field_name="mitigated", lookup_expr="gt", label="Mitigated After", method="filter_mitigated_after")
planned_remediation_date = DateRangeOmniFilter()
planned_remediation_version = CharFilter(lookup_expr="icontains", label=_("Planned remediation version"))
file_path = CharFilter(lookup_expr="icontains")
Expand Down Expand Up @@ -1705,6 +1720,20 @@ def set_date_fields(self, *args: list, **kwargs: dict):
self.form.fields["mitigated_after"].widget = date_input_widget
self.form.fields["cwe"].choices = cwe_options(self.queryset)

def filter_mitigated_after(self, queryset, name, value):
if value.hour == 0 and value.minute == 0 and value.second == 0:
value = value.replace(hour=23, minute=59, second=59)
Comment on lines +1724 to +1725
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like the right way to handle "after" when the user provides a simple date without a timestamp. Given that the UI currently uses a simple date picker without the ability to specify time (which seems fine), this would do what I would intuitively expect. We don't have to worry about the possibility of the user asking for "after 2025-01-01 00:00:00" here unless they're fiddling around in the Developer Console or something.

I suppose we could include microseconds here or use gte <date+1> 00:00:00 to deal with edge cases around the last second of the day...


return queryset.filter(mitigated__gt=value)

def filter_mitigated_on(self, queryset, name, value):
if value.hour == 0 and value.minute == 0 and value.second == 0:
# we have a simple date without a time, lets get a range from this morning to tonight at 23:59:59:999
nextday = value + timedelta(days=1)
return queryset.filter(mitigated__gte=value, mitigated__lt=nextday)

return queryset.filter(mitigated=value)


class FindingFilterWithoutObjectLookups(FindingFilterHelper, FindingTagStringFilter):
test__engagement__product__prod_type = NumberFilter(widget=HiddenInput())
Expand Down
Loading