diff --git a/.github/workflows/renovate.yaml b/.github/workflows/renovate.yaml
index 22cf11cd8df..1673fca3bc6 100644
--- a/.github/workflows/renovate.yaml
+++ b/.github/workflows/renovate.yaml
@@ -21,4 +21,4 @@ jobs:
uses: suzuki-shunsuke/github-action-renovate-config-validator@ca480cb7ec89a9e1cd8c214ad33bda1617184027 # v2.0.0
with:
strict: "true"
- validator_version: 42.92.5 # renovate: datasource=github-releases depName=renovatebot/renovate
+ validator_version: 43.2.4 # renovate: datasource=github-releases depName=renovatebot/renovate
diff --git a/Dockerfile.django-alpine b/Dockerfile.django-alpine
index bf02a563145..4f26ae3c497 100644
--- a/Dockerfile.django-alpine
+++ b/Dockerfile.django-alpine
@@ -5,7 +5,7 @@
# Dockerfile.nginx to use the caching mechanism of Docker.
# Ref: https://devguide.python.org/#branchstatus
-FROM python:3.13.11-alpine3.22@sha256:2fd93799bfc6381d078a8f656a5f45d6092e5d11d16f55889b3d5cbfdc64f045 AS base
+FROM python:3.13.12-alpine3.22@sha256:41351b07080ccfaa27bf38dde20de79ee6a0ac74a58c00c6d7a7d96ac4e69716 AS base
FROM base AS build
WORKDIR /app
RUN \
diff --git a/Dockerfile.django-debian b/Dockerfile.django-debian
index 864005de22d..efbe9b99cf0 100644
--- a/Dockerfile.django-debian
+++ b/Dockerfile.django-debian
@@ -5,7 +5,7 @@
# Dockerfile.nginx to use the caching mechanism of Docker.
# Ref: https://devguide.python.org/#branchstatus
-FROM python:3.13.11-slim-trixie@sha256:51e1a0a317fdb6e170dc791bbeae63fac5272c82f43958ef74a34e170c6f8b18 AS base
+FROM python:3.13.12-slim-trixie@sha256:3de9a8d7aedbb7984dc18f2dff178a7850f16c1ae7c34ba9d7ecc23d0755e35f AS base
FROM base AS build
WORKDIR /app
RUN \
diff --git a/Dockerfile.integration-tests-debian b/Dockerfile.integration-tests-debian
index a5d5eecb805..7f693dcaa83 100644
--- a/Dockerfile.integration-tests-debian
+++ b/Dockerfile.integration-tests-debian
@@ -3,7 +3,7 @@
FROM openapitools/openapi-generator-cli:v7.19.0@sha256:b9e7ad71a9f9406bd810378a939755fad114747a767e29bbf83ef9364d5f9dc0 AS openapitools
# currently only supports x64, no arm yet due to chrome and selenium dependencies
-FROM python:3.13.11-slim-trixie@sha256:51e1a0a317fdb6e170dc791bbeae63fac5272c82f43958ef74a34e170c6f8b18 AS build
+FROM python:3.13.12-slim-trixie@sha256:3de9a8d7aedbb7984dc18f2dff178a7850f16c1ae7c34ba9d7ecc23d0755e35f AS build
WORKDIR /app
RUN \
apt-get -y update && \
diff --git a/Dockerfile.nginx-alpine b/Dockerfile.nginx-alpine
index 8db761d2b4e..8617c4425d1 100644
--- a/Dockerfile.nginx-alpine
+++ b/Dockerfile.nginx-alpine
@@ -5,7 +5,7 @@
# Dockerfile.django-alpine to use the caching mechanism of Docker.
# Ref: https://devguide.python.org/#branchstatus
-FROM python:3.13.11-alpine3.22@sha256:2fd93799bfc6381d078a8f656a5f45d6092e5d11d16f55889b3d5cbfdc64f045 AS base
+FROM python:3.13.12-alpine3.22@sha256:41351b07080ccfaa27bf38dde20de79ee6a0ac74a58c00c6d7a7d96ac4e69716 AS base
FROM base AS build
WORKDIR /app
RUN \
diff --git a/components/package.json b/components/package.json
index bc3de215a48..eafbd9b86cb 100644
--- a/components/package.json
+++ b/components/package.json
@@ -1,6 +1,6 @@
{
"name": "defectdojo",
- "version": "2.55.2",
+ "version": "2.56.0-dev",
"license" : "BSD-3-Clause",
"private": true,
"dependencies": {
@@ -12,7 +12,7 @@
"chosen-bootstrap": "https://github.com/dbtek/chosen-bootstrap",
"chosen-js": "^1.8.7",
"clipboard": "^2.0.11",
- "datatables.net": "^2.3.6",
+ "datatables.net": "^2.3.7",
"datatables.net-buttons-bs": "^3.2.6",
"datatables.net-colreorder": "^2.1.2",
"drmonty-datatables-plugins": "^1.0.0",
diff --git a/components/yarn.lock b/components/yarn.lock
index ecfd05a3377..81525788406 100644
--- a/components/yarn.lock
+++ b/components/yarn.lock
@@ -162,10 +162,10 @@ datatables.net@2.3.2:
dependencies:
jquery ">=1.7"
-datatables.net@^2, datatables.net@^2.3.6:
- version "2.3.6"
- resolved "https://registry.yarnpkg.com/datatables.net/-/datatables.net-2.3.6.tgz#a11be57a2b50d7231cae2980a8ff1df3c18b7b17"
- integrity sha512-xQ/dCxrjfxM0XY70wSIzakkTZ6ghERwlLmAPyCnu8Sk5cyt9YvOVyOsFNOa/BZ/lM63Q3i2YSSvp/o7GXZGsbg==
+datatables.net@^2, datatables.net@^2.3.7:
+ version "2.3.7"
+ resolved "https://registry.yarnpkg.com/datatables.net/-/datatables.net-2.3.7.tgz#3cd34f6f5d1f40a46b5a20a4ba32604bdbcd6738"
+ integrity sha512-AvsjG/Nkp6OxeyBKYZauemuzQCPogE1kOtKwG4sYjvdqGCSLiGaJagQwXv4YxG+ts5vaJr6qKGG9ec3g6vTo3w==
dependencies:
jquery ">=1.7"
diff --git a/docker-compose.yml b/docker-compose.yml
index d8a1b14d4a9..923fd12eb06 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -120,7 +120,7 @@ services:
source: ./docker/extra_settings
target: /app/docker/extra_settings
postgres:
- image: postgres:18.1-alpine@sha256:4eb15de8e7b692c02427a2df278d18eb89422a534e428efb6d43c968250334d4
+ image: postgres:18.1-alpine@sha256:aa6eb304ddb6dd26df23d05db4e5cb05af8951cda3e0dc57731b771e0ef4ab29
environment:
POSTGRES_DB: ${DD_DATABASE_NAME:-defectdojo}
POSTGRES_USER: ${DD_DATABASE_USER:-defectdojo}
@@ -128,7 +128,7 @@ services:
volumes:
- defectdojo_postgres:/var/lib/postgresql/data
valkey:
- image: valkey/valkey:7.2.11-alpine@sha256:9e483e0fe4c98b631b166b41d530c7ff1b8009a44f261bff28e9d1e2e27db58d
+ image: valkey/valkey:7.2.11-alpine@sha256:10328d00120dc14fbc87b2ed61b7677ddbb0d011e705361b4788329a0ec69a93
volumes:
# we keep using the redis volume as renaming is not possible and copying data over
# would require steps during downtime or complex commands in the intializer
diff --git a/docs/content/en/open_source/upgrading/2.56.md b/docs/content/en/open_source/upgrading/2.56.md
new file mode 100644
index 00000000000..ebf55dbc894
--- /dev/null
+++ b/docs/content/en/open_source/upgrading/2.56.md
@@ -0,0 +1,7 @@
+---
+title: 'Upgrading to DefectDojo Version 2.56.x'
+toc_hide: true
+weight: -20260203
+description: No special instructions.
+---
+There are no special instructions for upgrading to 2.56.x. Check the [Release Notes](https://github.com/DefectDojo/django-DefectDojo/releases/tag/2.56.0) for the contents of the release.
diff --git a/dojo/__init__.py b/dojo/__init__.py
index b98285c0f30..700d8de3c7e 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.2"
+__version__ = "2.56.0-dev"
__url__ = "https://github.com/DefectDojo/django-DefectDojo" # noqa: RUF067
__docs__ = "https://documentation.defectdojo.com" # noqa: RUF067
diff --git a/dojo/templates/notifications/mail/product_type_added.tpl b/dojo/templates/notifications/mail/product_type_added.tpl
index a229882a346..1f41b3e5a14 100644
--- a/dojo/templates/notifications/mail/product_type_added.tpl
+++ b/dojo/templates/notifications/mail/product_type_added.tpl
@@ -1,40 +1,57 @@
{% load i18n %}
{% load navigation_tags %}
{% load display_tags %}
+{% url 'view_product_type' product_type.id as product_type_url %}
+
-
- {% autoescape on %}
-
- {% trans "Hello" %},
-
-
- {% blocktranslate trimmed prod_url=url|full_url %}
- The new product type "{{ title }}" has been added. It can be viewed here: {{ title }}
- {% endblocktranslate %}
-
-
-
- {% trans "Kind regards" %},
-
- {% if system_settings.team_name %}
- {{ system_settings.team_name }}
- {% else %}
- Defect Dojo
- {% endif %}
-
-
-
-
- {% url 'notifications' as notification_url %}
- {% trans "You can manage your notification settings here" %}: {{ notification_url|full_url }}
-
- {% if system_settings.disclaimer_notifications and system_settings.disclaimer_notifications.strip %}
-
-
-
{% trans "Disclaimer" %}
-
{{ system_settings.disclaimer_notifications }}
-
- {% endif %}
- {% endautoescape %}
-
+
+ {% autoescape on %}
+
+ {% trans "Hello" %},
+
+
+
+ {% blocktranslate trimmed with title=title prod_url=product_type_url|full_url %}
+ The new product type "{{ title }}" has been added.
+ It can be viewed here: {{ title }}
+ {% endblocktranslate %}
+
+
+
+
+
+ {% trans "Kind regards" %},
+
+
+ {% if system_settings.team_name %}
+ {{ system_settings.team_name }}
+ {% else %}
+ Defect Dojo
+ {% endif %}
+
+
+
+
+
+
+
+ {% url 'notifications' as notification_url %}
+ {% trans "You can manage your notification settings here" %}:
+ {{ notification_url|full_url }}
+
+
+ {% if system_settings.disclaimer_notifications and system_settings.disclaimer_notifications.strip %}
+
+
+
+ {% trans "Disclaimer" %}
+
+
+
+ {{ system_settings.disclaimer_notifications }}
+
+
+ {% endif %}
+ {% endautoescape %}
+
diff --git a/dojo/tools/qualys/parser.py b/dojo/tools/qualys/parser.py
index df9aa6f1ae6..af36c473e48 100644
--- a/dojo/tools/qualys/parser.py
+++ b/dojo/tools/qualys/parser.py
@@ -353,13 +353,18 @@ def parse_finding(host, tree):
if temp.get("CVSS_value") is not None:
finding.cvssv3_score = temp.get("CVSS_value")
finding.verified = True
+ endpoint_port = None
+ if port and str(port).isdigit():
+ endpoint_port = int(port)
# manage endpoint/location
if settings.V3_FEATURE_LOCATIONS:
- location = URL(host=issue_row["fqdn"]) if issue_row["fqdn"] else URL(host=issue_row["ip_address"])
+ host_val = issue_row["fqdn"] or issue_row["ip_address"]
+ location = URL(host=host_val, port=endpoint_port)
finding.unsaved_locations = [location]
else:
# TODO: Delete this after the move to Locations
- location = Endpoint(host=issue_row["fqdn"]) if issue_row["fqdn"] else Endpoint(host=issue_row["ip_address"])
+ host_val = issue_row["fqdn"] or issue_row["ip_address"]
+ location = Endpoint(host=host_val, port=endpoint_port)
finding.unsaved_endpoints = [location]
finding.unsaved_vulnerability_ids = temp.get("cve_list", [])
ret_rows.append(finding)
@@ -369,7 +374,7 @@ def parse_finding(host, tree):
def qualys_parser(qualys_xml_file):
parser = ElementTree.XMLParser()
tree = ElementTree.parse(qualys_xml_file, parser)
- host_list = tree.find("HOST_LIST")
+ host_list = tree.find(".//HOST_LIST")
finding_list = []
if host_list is not None:
for host in host_list:
diff --git a/helm/defectdojo/Chart.yaml b/helm/defectdojo/Chart.yaml
index fe3807b46a0..23bcbb1af8f 100644
--- a/helm/defectdojo/Chart.yaml
+++ b/helm/defectdojo/Chart.yaml
@@ -1,8 +1,8 @@
apiVersion: v2
-appVersion: "2.55.2"
+appVersion: "2.56.0-dev"
description: A Helm chart for Kubernetes to install DefectDojo
name: defectdojo
-version: 1.9.12
+version: 1.9.13-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: Bump DefectDojo to 2.55.2\n"
+ artifacthub.io/prerelease: "true"
+ artifacthub.io/changes: ""
diff --git a/helm/defectdojo/README.md b/helm/defectdojo/README.md
index 480b35e158c..a63757f2687 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
- 
+ 
A Helm chart for Kubernetes to install DefectDojo
@@ -767,9 +767,10 @@ A Helm chart for Kubernetes to install DefectDojo
| tests.unitTests.resources.requests.cpu | string | `"100m"` | |
| tests.unitTests.resources.requests.memory | string | `"128Mi"` | |
| trackConfig | string | `"disabled"` | Track configuration (trackConfig): will automatically respin application pods in case of config changes detection can be: 1. disabled (default) 2. enabled, enables tracking configuration changes based on SHA256 |
-| valkey | object | `{"auth":{"existingSecret":"defectdojo-valkey-specific","existingSecretPasswordKey":"valkey-password","password":""},"enabled":true,"sentinel":{"enabled":false},"service":{"port":6379},"tls":{"enabled":false}}` | For more advance options check the bitnami chart documentation: https://artifacthub.io/packages/helm/cloudpirates-valkey/valkey |
+| valkey | object | `{"auth":{"existingSecret":"defectdojo-valkey-specific","existingSecretPasswordKey":"valkey-password","password":""},"enabled":true,"sentinel":{"enabled":false},"service":{"port":6379},"serviceAccount":{"create":true},"tls":{"enabled":false}}` | For more advance options check the bitnami chart documentation: https://artifacthub.io/packages/helm/cloudpirates-valkey/valkey |
| valkey.enabled | bool | `true` | To use an external instance, switch enabled to `false` and set the address in `redisServer` below |
| valkey.service | object | `{"port":6379}` | To use a different port for Redis (default: 6379) |
+| valkey.serviceAccount.create | bool | `true` | Autocreate dedicated service account (as part of the best practice) |
| valkey.tls.enabled | bool | `false` | If TLS is enabled, the Redis broker will use the redis:// and optionally mount the certificates from an existing secret. |
| valkeyParams | string | `""` | Parameters attached to the valkey connection string, defaults to "ssl_cert_reqs=optional" if `valkey.tls.enabled` |
diff --git a/helm/defectdojo/values.schema.json b/helm/defectdojo/values.schema.json
index 1ad08c9f298..54120f850bf 100644
--- a/helm/defectdojo/values.schema.json
+++ b/helm/defectdojo/values.schema.json
@@ -1500,6 +1500,15 @@
}
}
},
+ "serviceAccount": {
+ "type": "object",
+ "properties": {
+ "create": {
+ "description": "Autocreate dedicated service account (as part of the best practice)",
+ "type": "boolean"
+ }
+ }
+ },
"tls": {
"type": "object",
"properties": {
diff --git a/helm/defectdojo/values.yaml b/helm/defectdojo/values.yaml
index d8c6d99576b..792930707e4 100644
--- a/helm/defectdojo/values.yaml
+++ b/helm/defectdojo/values.yaml
@@ -652,6 +652,9 @@ valkey:
# certFilename: tls.crt
# certKeyFilename: tls.key
# certCAFilename: ca.crt
+ serviceAccount:
+ # -- Autocreate dedicated service account (as part of the best practice)
+ create: true
# -- To add extra variables not predefined by helm config it is possible to define in extraConfigs block, e.g. below:
# NOTE Do not store any kind of sensitive information inside of it
diff --git a/requirements-lint.txt b/requirements-lint.txt
index b97a93db478..b7317ed9de5 100644
--- a/requirements-lint.txt
+++ b/requirements-lint.txt
@@ -1 +1 @@
-ruff==0.14.14
\ No newline at end of file
+ruff==0.15.0
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 52396d021d7..cd830d89307 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -53,7 +53,7 @@ django-split-settings==1.3.2
# do not upgrade to 2.1.1 - https://github.com/DefectDojo/django-DefectDojo/issues/12918
# use fork with django 5.2 fixes, but based on 2.1.0
git+https://github.com/valentijnscholten/django-tagulous.git@2b514f9140acfce608238d1426d864185b3c60a2#egg=django-tagulous
-PyJWT==2.10.1
+PyJWT==2.11.0
cvss==3.6
django-fieldsignals==0.8.0
hyperlink==21.0.0
@@ -69,4 +69,4 @@ fontawesomefree==6.6.0
PyYAML==6.0.3
pyopenssl==25.3.0
parameterized==0.9.0
-setuptools==80.10.2
+setuptools==82.0.0
diff --git a/unittests/scans/qualys/test_qualys.xml b/unittests/scans/qualys/test_qualys.xml
new file mode 100644
index 00000000000..93c1a0da710
--- /dev/null
+++ b/unittests/scans/qualys/test_qualys.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+ SSL Certificate Expired
+ 3
+ Threat text
+ Impact text
+ Solution text
+
+
+
+
+
+
+ 192.168.1.100
+
+
+
+ 80
+ 2026-02-08T10:00:00Z
+ 2026-02-08T10:00:00Z
+ Confirmed
+
+
+
+ 443
+ 2026-02-08T10:00:00Z
+ 2026-02-08T10:00:00Z
+ Confirmed
+
+
+
+
+
+
\ No newline at end of file
diff --git a/unittests/tools/test_qualys_parser.py b/unittests/tools/test_qualys_parser.py
index 060b6b9fcc0..d650eb9bbfd 100644
--- a/unittests/tools/test_qualys_parser.py
+++ b/unittests/tools/test_qualys_parser.py
@@ -16,6 +16,22 @@ def test_parse_file_with_no_vuln_has_no_findings_first_seen(self):
def test_parse_file_with_no_vuln_has_no_findings(self):
self.parse_file_with_no_vuln_has_no_findings()
+ def test_parse_file_with_multiple_ports_for_same_qid(self):
+ with (get_unit_tests_scans_path("qualys") / "test_qualys.xml").open(encoding="utf-8") as testfile:
+ parser = QualysParser()
+ findings = parser.get_findings(testfile, Test())
+
+ self.assertEqual(len(findings), 2, "Should have 2 findings for different ports")
+ ports = [self.get_unsaved_locations(f)[0].port for f in findings]
+ self.assertIn(80, ports)
+ self.assertIn(443, ports)
+
+ self.assertEqual(findings[0].title, findings[1].title)
+ self.assertNotEqual(
+ self.get_unsaved_locations(findings[0])[0].port,
+ self.get_unsaved_locations(findings[1])[0].port,
+ )
+
def parse_file_with_no_vuln_has_no_findings(self):
with (
get_unit_tests_scans_path("qualys") / "empty.xml").open(encoding="utf-8",