From e13b991d94b7c6baa3ab8034881a79fdb64c29e2 Mon Sep 17 00:00:00 2001 From: DefectDojo release bot Date: Tue, 3 Feb 2026 00:14:07 +0000 Subject: [PATCH 1/7] Update versions in application files --- components/package.json | 2 +- helm/defectdojo/Chart.yaml | 8 ++++---- helm/defectdojo/README.md | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/package.json b/components/package.json index c25b207b86..8d2f0a0b2a 100644 --- a/components/package.json +++ b/components/package.json @@ -1,6 +1,6 @@ { "name": "defectdojo", - "version": "2.55.0", + "version": "2.56.0-dev", "license" : "BSD-3-Clause", "private": true, "dependencies": { diff --git a/helm/defectdojo/Chart.yaml b/helm/defectdojo/Chart.yaml index 0b58221251..896aaa008b 100644 --- a/helm/defectdojo/Chart.yaml +++ b/helm/defectdojo/Chart.yaml @@ -1,8 +1,8 @@ apiVersion: v2 -appVersion: "2.55.0" +appVersion: "2.56.0-dev" description: A Helm chart for Kubernetes to install DefectDojo name: defectdojo -version: 1.9.10 +version: 1.9.11-dev icon: https://defectdojo.com/hubfs/DefectDojo_favicon.png maintainers: - name: madchap @@ -33,5 +33,5 @@ dependencies: # - kind: security # description: Critical bug annotations: - artifacthub.io/prerelease: "false" - artifacthub.io/changes: "- kind: changed\n description: Update valkey Docker tag from 0.13.0 to v0.15.0 (_/defect_/Chart.yaml)\n- kind: changed\n description: chore(deps)_ update valkey _ tag from 0.15.0 to v0.15.1 (_/defect_/chart.yaml)\n- kind: changed\n description: chore(deps)_ update gcr.io/cloudsql__/gce_proxy _ tag from 1.37.11 to v1.37.12 (_/defect_/values.yaml)\n- kind: changed\n description: Update valkey Docker tag from 0.15.1 to v0.15.2 (_/defect_/Chart.yaml)\n- kind: changed\n description: Update valkey Docker tag from 0.15.2 to v0.15.3 (_/defect_/Chart.yaml)\n- kind: changed\n description: Bump DefectDojo to 2.55.0\n" + artifacthub.io/prerelease: "true" + artifacthub.io/changes: "" diff --git a/helm/defectdojo/README.md b/helm/defectdojo/README.md index 33bc2bbc84..b8059537b6 100644 --- a/helm/defectdojo/README.md +++ b/helm/defectdojo/README.md @@ -511,7 +511,7 @@ The HELM schema will be generated for you. # General information about chart values -![Version: 1.9.10](https://img.shields.io/badge/Version-1.9.10-informational?style=flat-square) ![AppVersion: 2.55.0](https://img.shields.io/badge/AppVersion-2.55.0-informational?style=flat-square) +![Version: 1.9.11-dev](https://img.shields.io/badge/Version-1.9.11--dev-informational?style=flat-square) ![AppVersion: 2.56.0-dev](https://img.shields.io/badge/AppVersion-2.56.0--dev-informational?style=flat-square) A Helm chart for Kubernetes to install DefectDojo From 014d73700d1fdb361467d8173927c7d4bce0fa53 Mon Sep 17 00:00:00 2001 From: Paul Osinski <42211303+paulOsinski@users.noreply.github.com> Date: Wed, 4 Feb 2026 14:13:50 -0500 Subject: [PATCH 2/7] [docs] indexing improvements (#14229) * update robots.txt for indexing * add audience content to algolia indexing * add cache refresh for release notes version --- docs/config/_default/hugo.toml | 5 ++++- docs/layouts/_partials/head/custom-head.html | 5 +++-- docs/layouts/_partials/seo/robots.html | 3 +++ docs/layouts/robots.txt | 3 +++ 4 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 docs/layouts/_partials/seo/robots.html create mode 100644 docs/layouts/robots.txt diff --git a/docs/config/_default/hugo.toml b/docs/config/_default/hugo.toml index 13367504ca..0b62354212 100644 --- a/docs/config/_default/hugo.toml +++ b/docs/config/_default/hugo.toml @@ -46,9 +46,12 @@ copyRight = "Copyright (c) 2020-2024 Thulite" priority = 0.5 [caches] + [caches.getresource] + dir = ":cacheDir/:project" + maxAge = "1h" [caches.getjson] dir = ":cacheDir/:project" - maxAge = -1 # "30m" + maxAge = "1h" [taxonomies] contributor = "contributors" diff --git a/docs/layouts/_partials/head/custom-head.html b/docs/layouts/_partials/head/custom-head.html index cadc425ac3..5f14c4648e 100644 --- a/docs/layouts/_partials/head/custom-head.html +++ b/docs/layouts/_partials/head/custom-head.html @@ -1,6 +1,7 @@ -{{ if site.Params.add_ons.docSearch -}} +{{ if site.Params.add_ons.docSearch -}} {{ $options := (dict "targetPath" "/css/main.min.css" "outputStyle" "compressed") }} {{ $style := resources.Get "scss/app.scss" | css.Sass $options }} - + {{ end -}} + \ No newline at end of file diff --git a/docs/layouts/_partials/seo/robots.html b/docs/layouts/_partials/seo/robots.html new file mode 100644 index 0000000000..128d19bf8f --- /dev/null +++ b/docs/layouts/_partials/seo/robots.html @@ -0,0 +1,3 @@ +{{- with .Params.seo.robots }} + +{{- end }} \ No newline at end of file diff --git a/docs/layouts/robots.txt b/docs/layouts/robots.txt new file mode 100644 index 0000000000..3cba9e1766 --- /dev/null +++ b/docs/layouts/robots.txt @@ -0,0 +1,3 @@ +User-agent: * +Disallow: +Sitemap: {{ "/sitemap.xml" | absURL }} \ No newline at end of file From 15b3c4cddbfd45ed443522efd5015f66835402d8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Feb 2026 15:13:39 -0700 Subject: [PATCH 3/7] chore(deps): bump django from 5.2.9 to 5.2.11 (#14236) Bumps [django](https://github.com/django/django) from 5.2.9 to 5.2.11. - [Commits](https://github.com/django/django/compare/5.2.9...5.2.11) --- updated-dependencies: - dependency-name: django dependency-version: 5.2.11 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d152e8a32e..52396d021d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,7 +19,7 @@ django-slack==5.19.0 django-watson==1.6.3 django-permissions-policy==4.28.0 django-prometheus==2.4.1 -Django==5.2.9 +Django==5.2.11 django-single-session==0.2.0 djangorestframework==3.16.1 html2text==2025.4.15 From 9864582710d5d82c8548469809ea5a0820460360 Mon Sep 17 00:00:00 2001 From: Paul Osinski <42211303+paulOsinski@users.noreply.github.com> Date: Wed, 4 Feb 2026 17:22:03 -0500 Subject: [PATCH 4/7] Update views.py (#14243) --- dojo/jira_link/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dojo/jira_link/views.py b/dojo/jira_link/views.py index 31841c9bf4..f130d5b32a 100644 --- a/dojo/jira_link/views.py +++ b/dojo/jira_link/views.py @@ -345,11 +345,11 @@ def post(self, request): # Get the open and close keys msg = "Unable to find Open/Close ID's (invalid issue key specified?). They will need to be found manually" try: + open_key = close_key = None issue_id = jform.cleaned_data.get("issue_key") key_url = jira_server.strip("/") + "/rest/api/latest/issue/" + issue_id + "/transitions?expand=transitions.fields" response = jira._session.get(key_url).json() logger.debug("Retrieved JIRA issue successfully") - open_key = close_key = None for node in response["transitions"]: if node["to"]["statusCategory"]["name"] == "To Do": open_key = open_key or int(node["id"]) From 9e651dc243af7cadd90e6cea2a4d97b19ca2eb66 Mon Sep 17 00:00:00 2001 From: Cody Maffucci <46459665+Maffooch@users.noreply.github.com> Date: Wed, 4 Feb 2026 15:22:57 -0700 Subject: [PATCH 5/7] Set last reviewed date and reviewer when note is added (#14209) * Set last reviewed date and reviewer for finding Update finding's last reviewed date and reviewer to maintain parity with UI behaviors * Apply suggestion from @Maffooch * Set last reviewed date and author for finding Update finding with last reviewed date and author. * Apply suggestions from code review * Apply suggestion from @Maffooch --------- Co-authored-by: valentijnscholten --- dojo/api_v2/views.py | 3 +++ dojo/jira_link/views.py | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/dojo/api_v2/views.py b/dojo/api_v2/views.py index c1a3b12db6..3998683c31 100644 --- a/dojo/api_v2/views.py +++ b/dojo/api_v2/views.py @@ -1124,6 +1124,9 @@ def notes(self, request, pk=None): note_type=note_type, ) note.save() + finding.last_reviewed = note.date + finding.last_reviewed_by = author + finding.save(update_fields=["last_reviewed", "last_reviewed_by", "updated"]) finding.notes.add(note) # Determine if we need to send any notifications for user mentioned process_tag_notifications( diff --git a/dojo/jira_link/views.py b/dojo/jira_link/views.py index f130d5b32a..061ad83d83 100644 --- a/dojo/jira_link/views.py +++ b/dojo/jira_link/views.py @@ -285,9 +285,11 @@ def check_for_and_create_comment(parsed_json): finding.notes.add(new_note) finding.jira_issue.jira_change = timezone.now() finding.jira_issue.save() - # Only update the timestamp, not other fields like 'active' to avoid + finding.last_reviewed = new_note.date + finding.last_reviewed_by = author + # Only update the timestamp fields, not other fields like 'active' to avoid # race conditions with concurrent webhook events (e.g. issue_updated) - finding.save(update_fields=["updated"]) + finding.save(update_fields=["last_reviewed", "last_reviewed_by", "updated"]) return None From 121b78950fe375abb6636a78b70bfc345043b5f5 Mon Sep 17 00:00:00 2001 From: valentijnscholten Date: Wed, 4 Feb 2026 23:53:18 +0100 Subject: [PATCH 6/7] Fix finding counts showing as 1 due to subquery ordering bug (#14242) Hardened build_count_subquery to explicitly clear ordering and order by group_field before slicing. This prevents Django from adding implicit ORDER BY which causes GROUP BY to collapse counts to 1. Also updated prefetch_for_product_type to use the hardened helper instead of a local Subquery with the same vulnerability. Added unit tests to verify the fixes work correctly. Co-authored-by: Paul Osinski <42211303+paulOsinski@users.noreply.github.com> --- dojo/product_type/views.py | 13 +++++-------- dojo/query_utils.py | 6 +++++- unittests/test_product_type_counts.py | 19 +++++++++++++++++++ unittests/test_query_utils.py | 21 +++++++++++++++++++++ 4 files changed, 50 insertions(+), 9 deletions(-) create mode 100644 unittests/test_product_type_counts.py create mode 100644 unittests/test_query_utils.py diff --git a/dojo/product_type/views.py b/dojo/product_type/views.py index 28553db8cf..51c3985ec4 100644 --- a/dojo/product_type/views.py +++ b/dojo/product_type/views.py @@ -4,7 +4,7 @@ from django.contrib import messages from django.contrib.admin.utils import NestedObjects from django.db import DEFAULT_DB_ALIAS -from django.db.models import Count, IntegerField, OuterRef, Subquery, Value +from django.db.models import OuterRef, Value from django.db.models.functions import Coalesce from django.db.models.query import QuerySet from django.http import HttpResponseRedirect @@ -82,13 +82,10 @@ def prefetch_for_product_type(prod_types): logger.debug("unable to prefetch because query was already executed") return prod_types - prod_subquery = Subquery( - Product.objects.filter(prod_type_id=OuterRef("pk")) - .values("prod_type_id") - .annotate(c=Count("*")) - .values("c")[:1], - output_field=IntegerField(), - ) + prod_subquery = build_count_subquery( + Product.objects.filter(prod_type_id=OuterRef("pk")), + group_field="prod_type_id", + ) base_findings = Finding.objects.filter(test__engagement__product__prod_type_id=OuterRef("pk")) count_subquery = partial(build_count_subquery, group_field="test__engagement__product__prod_type_id") diff --git a/dojo/query_utils.py b/dojo/query_utils.py index b14c4bc03f..19e062871b 100644 --- a/dojo/query_utils.py +++ b/dojo/query_utils.py @@ -4,7 +4,11 @@ def build_count_subquery(model_qs: QuerySet, group_field: str) -> Subquery: """Return a Subquery that yields one aggregated count per `group_field`.""" + # Important: slicing (`[:1]`) on an unordered queryset makes Django add an implicit `ORDER BY `. + # With aggregation, Django then includes that pk in the GROUP BY, which collapses counts to 1. + # Ordering by `group_field` avoids that and keeps the GROUP BY stable. + model_qs = model_qs.order_by() return Subquery( - model_qs.values(group_field).annotate(c=Count("*")).values("c")[:1], # one row per group_field + model_qs.values(group_field).annotate(c=Count("pk")).order_by(group_field).values("c")[:1], # one row per group_field output_field=IntegerField(), ) diff --git a/unittests/test_product_type_counts.py b/unittests/test_product_type_counts.py new file mode 100644 index 0000000000..5bac04f3d1 --- /dev/null +++ b/unittests/test_product_type_counts.py @@ -0,0 +1,19 @@ +from dojo.models import Product, Product_Type +from dojo.product_type.views import prefetch_for_product_type +from unittests.dojo_test_case import DojoTestCase, versioned_fixtures + + +@versioned_fixtures +class TestProductTypeCounts(DojoTestCase): + fixtures = ["dojo_testdata.json"] + + def test_prefetch_for_product_type_prod_count_matches_direct_count(self): + product_type = Product_Type.objects.create(name="PT count test") + Product.objects.create(name="PT product 1", description="test", prod_type=product_type) + Product.objects.create(name="PT product 2", description="test", prod_type=product_type) + + annotated = prefetch_for_product_type(Product_Type.objects.filter(id=product_type.id)) + annotated_count = annotated.values_list("prod_count", flat=True).get() + + direct_count = Product.objects.filter(prod_type_id=product_type.id).count() + self.assertEqual(annotated_count, direct_count) diff --git a/unittests/test_query_utils.py b/unittests/test_query_utils.py new file mode 100644 index 0000000000..e953efd1df --- /dev/null +++ b/unittests/test_query_utils.py @@ -0,0 +1,21 @@ +from django.db.models import Count + +from dojo.engagement.views import prefetch_for_view_tests +from dojo.models import Finding, Test +from unittests.dojo_test_case import DojoTestCase, versioned_fixtures + + +@versioned_fixtures +class TestQueryUtils(DojoTestCase): + fixtures = ["dojo_testdata.json"] + + def test_prefetch_for_view_tests_finding_counts_match_direct_count(self): + test = Test.objects.annotate(finding_count=Count("finding")).filter(finding_count__gt=1).first() + # If fixtures ever change, ensure we still have a representative test case. + self.assertIsNotNone(test) + + annotated = prefetch_for_view_tests(Test.objects.filter(id=test.id)) + annotated_count = annotated.values_list("count_findings_test_all", flat=True).get() + + direct_count = Finding.objects.filter(test_id=test.id).count() + self.assertEqual(annotated_count, direct_count) From 9778f34b6c5d95eb370ef646d7c8022009980bd9 Mon Sep 17 00:00:00 2001 From: DefectDojo release bot Date: Wed, 4 Feb 2026 23:35:39 +0000 Subject: [PATCH 7/7] Update versions in application files --- components/package.json | 2 +- dojo/__init__.py | 2 +- helm/defectdojo/Chart.yaml | 8 ++++---- helm/defectdojo/README.md | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/components/package.json b/components/package.json index 8d2f0a0b2a..44db1f8348 100644 --- a/components/package.json +++ b/components/package.json @@ -1,6 +1,6 @@ { "name": "defectdojo", - "version": "2.56.0-dev", + "version": "2.55.1", "license" : "BSD-3-Clause", "private": true, "dependencies": { diff --git a/dojo/__init__.py b/dojo/__init__.py index fe0ab480ae..9f7285d1e6 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.55.0" +__version__ = "2.55.1" __url__ = "https://github.com/DefectDojo/django-DefectDojo" # noqa: RUF067 __docs__ = "https://documentation.defectdojo.com" # noqa: RUF067 diff --git a/helm/defectdojo/Chart.yaml b/helm/defectdojo/Chart.yaml index 896aaa008b..1b77c00b17 100644 --- a/helm/defectdojo/Chart.yaml +++ b/helm/defectdojo/Chart.yaml @@ -1,8 +1,8 @@ apiVersion: v2 -appVersion: "2.56.0-dev" +appVersion: "2.55.1" description: A Helm chart for Kubernetes to install DefectDojo name: defectdojo -version: 1.9.11-dev +version: 1.9.11 icon: https://defectdojo.com/hubfs/DefectDojo_favicon.png maintainers: - name: madchap @@ -33,5 +33,5 @@ dependencies: # - kind: security # description: Critical bug annotations: - artifacthub.io/prerelease: "true" - artifacthub.io/changes: "" + artifacthub.io/prerelease: "false" + artifacthub.io/changes: "- kind: changed\n description: Bump DefectDojo to 2.55.1\n" diff --git a/helm/defectdojo/README.md b/helm/defectdojo/README.md index b8059537b6..1e471662ca 100644 --- a/helm/defectdojo/README.md +++ b/helm/defectdojo/README.md @@ -511,7 +511,7 @@ The HELM schema will be generated for you. # General information about chart values -![Version: 1.9.11-dev](https://img.shields.io/badge/Version-1.9.11--dev-informational?style=flat-square) ![AppVersion: 2.56.0-dev](https://img.shields.io/badge/AppVersion-2.56.0--dev-informational?style=flat-square) +![Version: 1.9.11](https://img.shields.io/badge/Version-1.9.11-informational?style=flat-square) ![AppVersion: 2.55.1](https://img.shields.io/badge/AppVersion-2.55.1-informational?style=flat-square) A Helm chart for Kubernetes to install DefectDojo