Skip to content

Commit

Permalink
Merge pull request #11605 from DefectDojo/release/2.42.2
Browse files Browse the repository at this point in the history
Release: Merge release into master from: release/2.42.2
  • Loading branch information
rossops authored Jan 21, 2025
2 parents 6be30ce + 4887f1c commit 01a86ad
Show file tree
Hide file tree
Showing 77 changed files with 29,604 additions and 25,447 deletions.
2 changes: 1 addition & 1 deletion components/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "defectdojo",
"version": "2.42.1",
"version": "2.42.2",
"license" : "BSD-3-Clause",
"private": true,
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion docs/content/en/open_source/upgrading/2.42.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: 'Upgrading to DefectDojo Version 2.42.x'
toc_hide: true
weight: -20241104
weight: -20241202
description: No special instructions.
---
There are no special instructions for upgrading to 2.42.x. Check the [Release Notes](https://github.com/DefectDojo/django-DefectDojo/releases/tag/2.42.0) for the contents of the release.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ weight: 2
Once you have created one or more **Reports** in DefectDojo you can take further actions, including:

* Using a report as a template for subsequent reports
* Re\-running a report with updated data

* Re-running a report with updated data

* Deleting an old or unused report

![image](images/Working_with_Generated_Reports.png)
Expand Down
2 changes: 1 addition & 1 deletion dojo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
# Django starts so that shared_task will use this app.
from .celery import app as celery_app # noqa: F401

__version__ = "2.42.1"
__version__ = "2.42.2"
__url__ = "https://github.com/DefectDojo/django-DefectDojo"
__docs__ = "https://documentation.defectdojo.com"
2 changes: 1 addition & 1 deletion dojo/api_v2/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2286,7 +2286,7 @@ def setup_common_context(self, data: dict) -> dict:
"""
context = dict(data)
# update some vars
context["scan"] = data.pop("file")
context["scan"] = data.pop("file", None)

if context.get("auto_create_context"):
environment = Development_Environment.objects.get_or_create(name=data.get("environment", "Development"))[0]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Generated by Django 5.1.4 on 2025-01-10 16:01

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('dojo', '0218_system_settings_enforce_verified_status_and_more'),
]

operations = [
migrations.AddField(
model_name='system_settings',
name='enforce_verified_status_jira',
field=models.BooleanField(default=True, help_text='When enabled, findings must have a verified status to be pushed to jira.', verbose_name='Enforce Verified Status - Jira'),
),
migrations.AddField(
model_name='system_settings',
name='enforce_verified_status_metrics',
field=models.BooleanField(default=True, help_text='When enabled, findings must have a verified status to be counted in metric calculations, be included in reports, and filters.', verbose_name='Enforce Verified Status - Metrics'),
),
migrations.AddField(
model_name='system_settings',
name='enforce_verified_status_product_grading',
field=models.BooleanField(default=True, help_text="When enabled, findings must have a verified status to be considered as part of a product's grading.", verbose_name='Enforce Verified Status - Product Grading'),
),
migrations.AlterField(
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. This setting will override individually scoped verified toggles.', verbose_name='Enforce Verified Status - Globally'),
),
]
76 changes: 61 additions & 15 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 @@ -1411,6 +1412,15 @@ class ApiProductFilter(DojoFilter):
)


class PercentageRangeFilter(RangeFilter):
def filter(self, qs, value):
if value is not None:
start = value.start / decimal.Decimal("100.0") if value.start else None
stop = value.stop / decimal.Decimal("100.0") if value.stop else None
value = slice(start, stop)
return super().filter(qs, value)


class ApiFindingFilter(DojoFilter):
# BooleanFilter
active = BooleanFilter(field_name="active")
Expand Down Expand Up @@ -1456,13 +1466,30 @@ 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")
endpoints = NumberInFilter(field_name="endpoints", lookup_expr="in")
epss_score = PercentageRangeFilter(
field_name="epss_score",
label="EPSS score range",
help_text=(
"The range of EPSS score percentages to filter on; the min input is a lower bound, "
"the max is an upper bound. Leaving one empty will skip that bound (e.g., leaving "
"the min bound input empty will filter only on the max bound -- filtering on "
'"less than or equal"). Leading 0 required.'
))
epss_percentile = PercentageRangeFilter(
field_name="epss_percentile",
label="EPSS percentile range",
help_text=(
"The range of EPSS percentiles to filter on; the min input is a lower bound, the max "
"is an upper bound. Leaving one empty will skip that bound (e.g., leaving the min bound "
'input empty will filter only on the max bound -- filtering on "less than or equal"). Leading 0 required.'
))
found_by = NumberInFilter(field_name="found_by", lookup_expr="in")
id = NumberInFilter(field_name="id", lookup_expr="in")
last_reviewed_by = NumberInFilter(field_name="last_reviewed_by", lookup_expr="in")
Expand Down Expand Up @@ -1544,6 +1571,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)

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 All @@ -1564,15 +1605,6 @@ def filter_percentage(self, queryset, name, value):
return queryset.filter(**lookup_kwargs)


class PercentageRangeFilter(RangeFilter):
def filter(self, qs, value):
if value is not None:
start = value.start / decimal.Decimal("100.0") if value.start else None
stop = value.stop / decimal.Decimal("100.0") if value.stop else None
value = slice(start, stop)
return super().filter(qs, value)


class FindingFilterHelper(FilterSet):
title = CharFilter(lookup_expr="icontains")
date = DateRangeFilter(field_name="date", label="Date Discovered")
Expand All @@ -1587,9 +1619,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 +1737,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)

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
2 changes: 1 addition & 1 deletion dojo/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -3076,7 +3076,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 and get_system_setting("enforce_verified_status", True)):
if not active or (not verified and (get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_jira", 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"))
Expand Down
3 changes: 1 addition & 2 deletions dojo/jira_link/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ def can_be_pushed_to_jira(obj, form=None):

logger.debug("can_be_pushed_to_jira: %s, %s, %s", active, verified, severity)

isenforced = get_system_setting("enforce_verified_status", True)
isenforced = get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_jira", 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")
Expand Down Expand Up @@ -1116,7 +1116,6 @@ def get_issuetype_fields(
except JIRAError as e:
e.text = f"Jira API call 'createmeta' failed with status: {e.status_code} and message: {e.text}"
raise

project = None
try:
project = meta["projects"][0]
Expand Down
2 changes: 1 addition & 1 deletion dojo/management/commands/jira_async_updates.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class Command(BaseCommand):
def handle(self, *args, **options):

findings = Finding.objects.exclude(jira_issue__isnull=True)
if get_system_setting("enforce_verified_status", True):
if get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_jira", True):
findings = findings.filter(verified=True, active=True)
else:
findings = findings.filter(active=True)
Expand Down
2 changes: 1 addition & 1 deletion dojo/management/commands/push_to_jira_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class Command(BaseCommand):
def handle(self, *args, **options):

findings = Finding.objects.exclude(jira_issue__isnull=True)
if get_system_setting("enforce_verified_status", True):
if get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_jira", True):
findings = findings.filter(verified=True, active=True)
else:
findings = findings.filter(active=True)
Expand Down
2 changes: 1 addition & 1 deletion dojo/metrics/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def finding_queries(
weekly_counts = query_counts_for_period(MetricsPeriod.WEEK, weeks_between)

top_ten = get_authorized_products(Permissions.Product_View)
if get_system_setting("enforce_verified_status", True):
if get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_metrics", True):
top_ten = top_ten.filter(engagement__test__finding__verified=True)

top_ten = top_ten.filter(engagement__test__finding__false_p=False,
Expand Down
8 changes: 4 additions & 4 deletions dojo/metrics/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ def simple_metrics(request):
date__year=now.year,
)

if get_system_setting("enforce_verified_status", True):
if get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_metrics", True):
total = total.filter(verified=True)

total = total.distinct()
Expand Down Expand Up @@ -308,7 +308,7 @@ def product_type_counts(request):
then=Value(1)),
output_field=IntegerField())))["total"]

if get_system_setting("enforce_verified_status", True):
if get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_metrics", True):
overall_in_pt = Finding.objects.filter(date__lt=end_date,
verified=True,
false_p=False,
Expand Down Expand Up @@ -509,7 +509,7 @@ def product_tag_counts(request):
then=Value(1)),
output_field=IntegerField())))["total"]

if get_system_setting("enforce_verified_status", True):
if get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_metrics", True):
overall_in_pt = Finding.objects.filter(date__lt=end_date,
verified=True,
false_p=False,
Expand Down Expand Up @@ -685,7 +685,7 @@ def view_engineer(request, eid):
raise PermissionDenied
now = timezone.now()

if get_system_setting("enforce_verified_status", True):
if get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_metrics", True):
findings = Finding.objects.filter(reporter=user, verified=True)
else:
findings = Finding.objects.filter(reporter=user)
Expand Down
Loading

0 comments on commit 01a86ad

Please sign in to comment.