From 7e7efd801656939e37b89db20c039d27c75954f5 Mon Sep 17 00:00:00 2001 From: DefectDojo release bot Date: Tue, 18 Feb 2025 15:41:34 +0000 Subject: [PATCH 1/5] Update versions in application files --- components/package.json | 2 +- helm/defectdojo/Chart.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/components/package.json b/components/package.json index 20b0bc815d0..559dcab7c3f 100644 --- a/components/package.json +++ b/components/package.json @@ -1,6 +1,6 @@ { "name": "defectdojo", - "version": "2.43.3", + "version": "2.44.0-dev", "license" : "BSD-3-Clause", "private": true, "dependencies": { diff --git a/helm/defectdojo/Chart.yaml b/helm/defectdojo/Chart.yaml index c66916b0f99..323038a6b30 100644 --- a/helm/defectdojo/Chart.yaml +++ b/helm/defectdojo/Chart.yaml @@ -1,8 +1,8 @@ apiVersion: v2 -appVersion: "2.43.3" +appVersion: "2.44.0-dev" description: A Helm chart for Kubernetes to install DefectDojo name: defectdojo -version: 1.6.174 +version: 1.6.175-dev icon: https://www.defectdojo.org/img/favicon.ico maintainers: - name: madchap From 3b32f3e7c2f7747fb0d9ab3a009d95e642b7c13a Mon Sep 17 00:00:00 2001 From: manuelsommer <47991713+manuel-sommer@users.noreply.github.com> Date: Thu, 20 Feb 2025 00:41:52 +0100 Subject: [PATCH 2/5] Return Feedback about wrong File Format in ZAP (#11772) * Return Feedback about wrong File Format in ZAP * ruff --- dojo/tools/zap/parser.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dojo/tools/zap/parser.py b/dojo/tools/zap/parser.py index 19fbf16241e..88d09ccb66e 100644 --- a/dojo/tools/zap/parser.py +++ b/dojo/tools/zap/parser.py @@ -27,6 +27,9 @@ def get_description_for_scan_types(self, scan_type): return "ZAP XML report format." def get_findings(self, file, test): + if not file.name.endswith(".xml"): + msg = "Internal error: Wrong file format, please use xml." + raise ValueError(msg) tree = ET.parse(file) items = [] for node in tree.findall("site"): From 4ae1eb5d6dc5c1527db362b90e16d3b2724c498a Mon Sep 17 00:00:00 2001 From: Cody Maffucci <46459665+Maffooch@users.noreply.github.com> Date: Thu, 20 Feb 2025 13:25:43 -0600 Subject: [PATCH 3/5] Surveys: Correct Question 404 (#11862) * Surveys: Correct Question 404 When editing a survey question, a 404 is presented for a valid object. At some point, the content type for Questions changed to `Defect Dojo` (the verbose name of the app) rather than `dojo` (the common name) There is only one place where the name of the content type is accessed, so adding some backward compatible checks corrected the issue [sc-10195] * Update views.py --- dojo/survey/views.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dojo/survey/views.py b/dojo/survey/views.py index d83803f2efd..fde06a113f7 100644 --- a/dojo/survey/views.py +++ b/dojo/survey/views.py @@ -539,17 +539,17 @@ def edit_question(request, qid): extra_tags="alert-info") type = str(ContentType.objects.get_for_model(question)) - if type == "dojo | text question": + if type in {"dojo | text question", "Defect Dojo | text question"}: form = EditTextQuestionForm(instance=question) - elif type == "dojo | choice question": + elif type in {"dojo | choice question", "Defect Dojo | choice question"}: form = EditChoiceQuestionForm(instance=question) else: raise Http404 if request.method == "POST": - if type == "dojo | text question": + if type in {"dojo | text question", "Defect Dojo | text question"}: form = EditTextQuestionForm(request.POST, instance=question) - elif type == "dojo | choice question": + elif type in {"dojo | choice question", "Defect Dojo | choice question"}: form = EditChoiceQuestionForm(request.POST, instance=question) else: raise Http404 From 0fb088f7a1e53c9114758f24d8a0abdbed50381a Mon Sep 17 00:00:00 2001 From: Cody Maffucci <46459665+Maffooch@users.noreply.github.com> Date: Thu, 20 Feb 2025 16:43:44 -0600 Subject: [PATCH 4/5] API Tags: Add filter for `AND` expressions (#11743) * API Tags: Add filter for `AND` expressions * Fix some ruff stuff * Small corrections * Update dojo/filters.py --- dojo/filters.py | 135 +++++++++++++++++++++++++++--------- unittests/dojo_test_case.py | 4 +- unittests/test_tags.py | 6 ++ 3 files changed, 111 insertions(+), 34 deletions(-) diff --git a/dojo/filters.py b/dojo/filters.py index daa73375945..5c6f4e68dba 100644 --- a/dojo/filters.py +++ b/dojo/filters.py @@ -12,7 +12,7 @@ from django.apps import apps from django.conf import settings from django.contrib.contenttypes.models import ContentType -from django.db.models import JSONField, Q +from django.db.models import Count, JSONField, Q from django.forms import HiddenInput from django.utils import timezone from django.utils.translation import gettext_lazy as _ @@ -135,6 +135,20 @@ def __init__(self, *args, **kwargs): super(CharFilter, self).__init__(*args, **kwargs) +class CharFieldFilterANDExpression(CharFieldInFilter): + def filter(self, queryset, value): + # Catch the case where a value if not supplied + if not value: + return queryset + # Do the filtering + objects = set(value.split(",")) + return ( + queryset.filter(**{f"{self.field_name}__in": objects}) + .annotate(object_count=Count(self.field_name)) + .filter(object_count=len(objects)) + ) + + class FindingStatusFilter(ChoiceFilter): def any(self, qs, name): return qs @@ -1204,11 +1218,20 @@ class ProductEngagementFilterWithoutObjectLookups(ProductEngagementFilterHelper, class ApiEngagementFilter(DojoFilter): product__prod_type = NumberInFilter(field_name="product__prod_type", lookup_expr="in") tag = CharFilter(field_name="tags__name", lookup_expr="icontains", help_text="Tag name contains") - tags = CharFieldInFilter(field_name="tags__name", lookup_expr="in", - help_text="Comma separated list of exact tags") - product__tags = CharFieldInFilter(field_name="product__tags__name", - lookup_expr="in", - help_text="Comma separated list of exact tags present on product") + tags = CharFieldInFilter( + field_name="tags__name", + lookup_expr="in", + help_text="Comma separated list of exact tags (uses OR for multiple values)") + tags__and = CharFieldFilterANDExpression( + field_name="tags__name", + help_text="Comma separated list of exact tags to match with an AND expression") + product__tags = CharFieldInFilter( + field_name="product__tags__name", + lookup_expr="in", + help_text="Comma separated list of exact tags present on product (uses OR for multiple values)") + product__tags__and = CharFieldFilterANDExpression( + field_name="product__tags__name", + help_text="Comma separated list of exact tags to match with an AND expression present on product") not_tag = CharFilter(field_name="tags__name", lookup_expr="icontains", help_text="Not Tag name contains", exclude="True") not_tags = CharFieldInFilter(field_name="tags__name", lookup_expr="in", @@ -1365,9 +1388,13 @@ class ApiProductFilter(DojoFilter): regulations = NumberInFilter(field_name="regulations", lookup_expr="in") tag = CharFilter(field_name="tags__name", lookup_expr="icontains", label="Tag name contains") - tags = CharFieldInFilter(field_name="tags__name", lookup_expr="in", - help_text="Comma separated list of exact tags") - + tags = CharFieldInFilter( + field_name="tags__name", + lookup_expr="in", + help_text="Comma separated list of exact tags (uses OR for multiple values)") + tags__and = CharFieldFilterANDExpression( + field_name="tags__name", + help_text="Comma separated list of exact tags to match with an AND expression") not_tag = CharFilter(field_name="tags__name", lookup_expr="icontains", help_text="Not Tag name contains", exclude="True") not_tags = CharFieldInFilter(field_name="tags__name", lookup_expr="in", help_text="Comma separated list of exact tags not present on product", exclude="True") @@ -1511,16 +1538,34 @@ class ApiFindingFilter(DojoFilter): risk_acceptance = extend_schema_field(OpenApiTypes.NUMBER)(ReportRiskAcceptanceFilter()) tag = CharFilter(field_name="tags__name", lookup_expr="icontains", help_text="Tag name contains") - tags = CharFieldInFilter(field_name="tags__name", lookup_expr="in", - help_text="Comma separated list of exact tags") - test__tags = CharFieldInFilter(field_name="test__tags__name", lookup_expr="in", help_text="Comma separated list of exact tags present on test") - test__engagement__tags = CharFieldInFilter(field_name="test__engagement__tags__name", lookup_expr="in", - help_text="Comma separated list of exact tags present on engagement") + tags = CharFieldInFilter( + field_name="tags__name", + lookup_expr="in", + help_text="Comma separated list of exact tags (uses OR for multiple values)") + tags__and = CharFieldFilterANDExpression( + field_name="tags__name", + help_text="Comma separated list of exact tags to match with an AND expression") + test__tags = CharFieldInFilter( + field_name="test__tags__name", + lookup_expr="in", + help_text="Comma separated list of exact tags present on test (uses OR for multiple values)") + test__tags__and = CharFieldFilterANDExpression( + field_name="test__tags__name", + help_text="Comma separated list of exact tags to match with an AND expression present on test") + test__engagement__tags = CharFieldInFilter( + field_name="test__engagement__tags__name", + lookup_expr="in", + help_text="Comma separated list of exact tags present on engagement (uses OR for multiple values)") + test__engagement__tags__and = CharFieldFilterANDExpression( + field_name="test__engagement__tags__name", + help_text="Comma separated list of exact tags to match with an AND expression present on engagement") test__engagement__product__tags = CharFieldInFilter( field_name="test__engagement__product__tags__name", lookup_expr="in", - help_text="Comma separated list of exact tags present on product") - + help_text="Comma separated list of exact tags present on product (uses OR for multiple values)") + test__engagement__product__tags__and = CharFieldFilterANDExpression( + field_name="test__engagement__product__tags__name", + help_text="Comma separated list of exact tags to match with an AND expression present on product") not_tag = CharFilter(field_name="tags__name", lookup_expr="icontains", help_text="Not Tag name contains", exclude="True") not_tags = CharFieldInFilter(field_name="tags__name", lookup_expr="in", help_text="Comma separated list of exact tags not present on model", exclude="True") @@ -2118,9 +2163,13 @@ def __init__(self, *args, **kwargs): class ApiTemplateFindingFilter(DojoFilter): tag = CharFilter(field_name="tags__name", lookup_expr="icontains", help_text="Tag name contains") - tags = CharFieldInFilter(field_name="tags__name", lookup_expr="in", - help_text="Comma separated list of exact tags") - + tags = CharFieldInFilter( + field_name="tags__name", + lookup_expr="in", + help_text="Comma separated list of exact tags (uses OR for multiple values)") + tags__and = CharFieldFilterANDExpression( + field_name="tags__name", + help_text="Comma separated list of exact tags to match with an AND expression") not_tag = CharFilter(field_name="tags__name", lookup_expr="icontains", help_text="Not Tag name contains", exclude="True") not_tags = CharFieldInFilter(field_name="tags__name", lookup_expr="in", help_text="Comma separated list of exact tags not present on model", exclude="True") @@ -2695,9 +2744,13 @@ class Meta: class ApiEndpointFilter(DojoFilter): tag = CharFilter(field_name="tags__name", lookup_expr="icontains", help_text="Tag name contains") - tags = CharFieldInFilter(field_name="tags__name", lookup_expr="in", - help_text="Comma separated list of exact tags") - + tags = CharFieldInFilter( + field_name="tags__name", + lookup_expr="in", + help_text="Comma separated list of exact tags (uses OR for multiple values)") + tags__and = CharFieldFilterANDExpression( + field_name="tags__name", + help_text="Comma separated list of exact tags to match with an AND expression") not_tag = CharFilter(field_name="tags__name", lookup_expr="icontains", help_text="Not Tag name contains", exclude="True") not_tags = CharFieldInFilter(field_name="tags__name", lookup_expr="in", help_text="Comma separated list of exact tags not present on model", exclude="True") @@ -2851,13 +2904,27 @@ def __init__(self, *args, **kwargs): class ApiTestFilter(DojoFilter): tag = CharFilter(field_name="tags__name", lookup_expr="icontains", help_text="Tag name contains") - tags = CharFieldInFilter(field_name="tags__name", lookup_expr="in", - help_text="Comma separated list of exact tags") - engagement__tags = CharFieldInFilter(field_name="engagement__tags__name", lookup_expr="in", - help_text="Comma separated list of exact tags present on engagement") - engagement__product__tags = CharFieldInFilter(field_name="engagement__product__tags__name", - lookup_expr="in", - help_text="Comma separated list of exact tags present on product") + tags = CharFieldInFilter( + field_name="tags__name", + lookup_expr="in", + help_text="Comma separated list of exact tags (uses OR for multiple values)") + tags__and = CharFieldFilterANDExpression( + field_name="tags__name", + help_text="Comma separated list of exact tags to match with an AND expression") + engagement__tags = CharFieldInFilter( + field_name="engagement__tags__name", + lookup_expr="in", + help_text="Comma separated list of exact tags present on engagement (uses OR for multiple values)") + engagement__tags__and = CharFieldFilterANDExpression( + field_name="engagement__tags__name", + help_text="Comma separated list of exact tags to match with an AND expression present on engagement") + engagement__product__tags = CharFieldInFilter( + field_name="engagement__product__tags__name", + lookup_expr="in", + help_text="Comma separated list of exact tags present on product (uses OR for multiple values)") + engagement__product__tags__and = CharFieldFilterANDExpression( + field_name="engagement__product__tags__name", + help_text="Comma separated list of exact tags to match with an AND expression present on product") not_tag = CharFilter(field_name="tags__name", lookup_expr="icontains", help_text="Not Tag name contains", exclude="True") not_tags = CharFieldInFilter(field_name="tags__name", lookup_expr="in", @@ -2905,9 +2972,13 @@ class Meta: class ApiAppAnalysisFilter(DojoFilter): tag = CharFilter(field_name="tags__name", lookup_expr="icontains", help_text="Tag name contains") - tags = CharFieldInFilter(field_name="tags__name", lookup_expr="in", - help_text="Comma separated list of exact tags") - + tags = CharFieldInFilter( + field_name="tags__name", + lookup_expr="in", + help_text="Comma separated list of exact tags (uses OR for multiple values)") + tags__and = CharFieldFilterANDExpression( + field_name="tags__name", + help_text="Comma separated list of exact tags to match with an AND expression") not_tag = CharFilter(field_name="tags__name", lookup_expr="icontains", help_text="Not Tag name contains", exclude="True") not_tags = CharFieldInFilter(field_name="tags__name", lookup_expr="in", help_text="Comma separated list of exact tags not present on model", exclude="True") diff --git a/unittests/dojo_test_case.py b/unittests/dojo_test_case.py index 0de526b358b..4fa447a2c1b 100644 --- a/unittests/dojo_test_case.py +++ b/unittests/dojo_test_case.py @@ -716,8 +716,8 @@ def get_finding_tags_api(self, finding_id): response = self.do_finding_tags_api(self.client.get, finding_id) return response.data - def get_finding_api_filter_tags(self, tags): - response = self.client.get(reverse("finding-list") + f"?tags={tags}", format="json") + def get_finding_api_filter_tags(self, tags, parameter="tags"): + response = self.client.get(reverse("finding-list") + f"?{parameter}={tags}", format="json") self.assertEqual(200, response.status_code, response.content[:1000]) return response.data diff --git a/unittests/test_tags.py b/unittests/test_tags.py index 9c8a71d0d7a..137ae07c0b9 100644 --- a/unittests/test_tags.py +++ b/unittests/test_tags.py @@ -58,6 +58,12 @@ def test_finding_filter_tags(self): response = self.get_finding_api_filter_tags("tag4") self.assertEqual(response["count"], 0) + # Test the tags__and filter for a case with no matches + response = self.get_finding_api_filter_tags("tag2,tag3", parameter="tags__and") + self.assertEqual(response["count"], 0) + # Test the tags__and filter for a case with one exact match + response = self.get_finding_api_filter_tags("tag1,tag2", parameter="tags__and") + self.assertEqual(response["count"], 1) def test_finding_post_tags(self): # create finding From c0be2eb940f26cb6c42dca2faee37734be5e8df3 Mon Sep 17 00:00:00 2001 From: DefectDojo release bot Date: Mon, 24 Feb 2025 14:42:11 +0000 Subject: [PATCH 5/5] Update versions in application files --- components/package.json | 2 +- dojo/__init__.py | 2 +- helm/defectdojo/Chart.yaml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/package.json b/components/package.json index 559dcab7c3f..4efaee87c24 100644 --- a/components/package.json +++ b/components/package.json @@ -1,6 +1,6 @@ { "name": "defectdojo", - "version": "2.44.0-dev", + "version": "2.43.4", "license" : "BSD-3-Clause", "private": true, "dependencies": { diff --git a/dojo/__init__.py b/dojo/__init__.py index 3686132297a..2636e4aeb9e 100644 --- a/dojo/__init__.py +++ b/dojo/__init__.py @@ -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.43.3" +__version__ = "2.43.4" __url__ = "https://github.com/DefectDojo/django-DefectDojo" __docs__ = "https://documentation.defectdojo.com" diff --git a/helm/defectdojo/Chart.yaml b/helm/defectdojo/Chart.yaml index 323038a6b30..cfb45b99d5e 100644 --- a/helm/defectdojo/Chart.yaml +++ b/helm/defectdojo/Chart.yaml @@ -1,8 +1,8 @@ apiVersion: v2 -appVersion: "2.44.0-dev" +appVersion: "2.43.4" description: A Helm chart for Kubernetes to install DefectDojo name: defectdojo -version: 1.6.175-dev +version: 1.6.175 icon: https://www.defectdojo.org/img/favicon.ico maintainers: - name: madchap