Skip to content

Commit cbe63d6

Browse files
committed
Merge remote-tracking branch 'origin/dev' into cascade-delete-fix
# Conflicts: # dojo/finding/helper.py
2 parents fef1f82 + 0dee7bc commit cbe63d6

181 files changed

Lines changed: 5467 additions & 3461 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.dryrunsecurity.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ sensitiveCodepaths:
4141
- 'dojo/middleware.py'
4242
- 'dojo/models.py'
4343
- 'dojo/okta.py'
44-
- 'dojo/pipeline.py'
45-
- 'dojo/remote_user.py'
44+
- 'dojo/sso/pipeline.py'
45+
- 'dojo/sso/remote_user.py'
4646
- 'dojo/tasks.py'
4747
- 'dojo/urls.py'
4848
- 'dojo/utils.py'

Dockerfile.django-debian

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# Dockerfile.nginx to use the caching mechanism of Docker.
66

77
# Ref: https://devguide.python.org/#branchstatus
8-
FROM python:3.13.13-slim-trixie@sha256:9213d136547f0602c3337ff48291e937f9cc43060b3e123402cf2aaff1a08b75 AS base
8+
FROM python:3.13.13-slim-trixie@sha256:d2462a6bed37b4fc6cabecf5a2132ae70df772fe03c7393c4d98a0c2fb48aa2e AS base
99
FROM base AS build
1010
WORKDIR /app
1111
RUN \

Dockerfile.integration-tests-debian

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11

22
# code: language=Dockerfile
33

4-
FROM openapitools/openapi-generator-cli:v7.21.0@sha256:ce308310f3c1f8761e65338b8ab87b651bf4862c6acb80de510f381fffc4510b AS openapitools
4+
FROM openapitools/openapi-generator-cli:v7.22.0@sha256:1f459499a7c794aa0ea769c3c9b0eb54806c5ad2f68510a0ebb9338d0a626ced AS openapitools
55
# currently only supports x64, no arm yet due to chrome and selenium dependencies
6-
FROM python:3.13.13-slim-trixie@sha256:9213d136547f0602c3337ff48291e937f9cc43060b3e123402cf2aaff1a08b75 AS build
6+
FROM python:3.13.13-slim-trixie@sha256:d2462a6bed37b4fc6cabecf5a2132ae70df772fe03c7393c4d98a0c2fb48aa2e AS build
77
WORKDIR /app
88
RUN \
99
apt-get -y update && \

components/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"chosen-bootstrap": "https://github.com/dbtek/chosen-bootstrap",
1313
"chosen-js": "^1.8.7",
1414
"clipboard": "^2.0.11",
15-
"datatables.net": "^2.3.7",
15+
"datatables.net": "^2.3.8",
1616
"datatables.net-buttons-bs": "^3.2.6",
1717
"datatables.net-colreorder": "^2.1.2",
1818
"drmonty-datatables-plugins": "^1.0.0",

components/yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,10 +167,10 @@ datatables.net@2.3.2:
167167
dependencies:
168168
jquery ">=1.7"
169169

170-
datatables.net@^2, datatables.net@^2.3.7:
171-
version "2.3.7"
172-
resolved "https://registry.yarnpkg.com/datatables.net/-/datatables.net-2.3.7.tgz#3cd34f6f5d1f40a46b5a20a4ba32604bdbcd6738"
173-
integrity sha512-AvsjG/Nkp6OxeyBKYZauemuzQCPogE1kOtKwG4sYjvdqGCSLiGaJagQwXv4YxG+ts5vaJr6qKGG9ec3g6vTo3w==
170+
datatables.net@^2, datatables.net@^2.3.8:
171+
version "2.3.8"
172+
resolved "https://registry.yarnpkg.com/datatables.net/-/datatables.net-2.3.8.tgz#55a8dbe3bd2196951c498ab79bf44602a2bf3229"
173+
integrity sha512-uhViowhlDlheAuo5a8TrkQqADsjrtGeOyvrigvr4t0+K3MyAWqClORXWAYIcN9VLX6iIX0C8O9gwJNd01hITRg==
174174
dependencies:
175175
jquery ">=1.7"
176176

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
title: 'Upgrading to DefectDojo Version 2.58.x'
3+
toc_hide: true
4+
weight: -20260504
5+
description: Notification .tpl templates relocated under dojo/notifications/
6+
---
7+
8+
## Notification `.tpl` templates relocated
9+
10+
The notification domain has been consolidated under a new `dojo/notifications/` package, and the 62 channel `.tpl` templates that drive alert, mail, MS Teams, Slack, and webhook notifications have moved on disk. The Django template lookup name (e.g. `notifications/mail/scan_added.tpl`) is unchanged, so most customizations keep working without any edits — but operators who override `.tpl` files by mounting them into the source tree need to update their paths.
11+
12+
### What moved
13+
14+
The channel templates under `alert/`, `mail/`, `msteams/`, `slack/`, `webhooks/`, and `webhooks_summary/` have been relocated:
15+
16+
| Old on-disk location | New on-disk location |
17+
| --- | --- |
18+
| `dojo/templates/notifications/{channel}/{event}.tpl` | `dojo/notifications/templates/notifications/{channel}/{event}.tpl` |
19+
20+
For example, `dojo/templates/notifications/mail/scan_added.tpl` now lives at `dojo/notifications/templates/notifications/mail/scan_added.tpl`. A new `TEMPLATES["DIRS"]` entry pointing at `dojo/notifications/templates/` is registered automatically, so the lookup path used by `render_to_string()` (e.g. `notifications/slack/sla_breach.tpl`) resolves exactly as before.
21+
22+
### Required actions
23+
24+
- **Customizing `.tpl` files via your own templates directory (recommended pattern):** No action required. Overrides resolved by lookup name continue to take precedence.
25+
- **Customizing `.tpl` files via a Docker volume mount or in-tree patch at the old `dojo/templates/notifications/...` path:** Update the mount/patch target to the new `dojo/notifications/templates/notifications/...` path, or move your override into a project-level templates directory keyed by the lookup name.
26+
- **No customizations:** No action required.
27+
28+
For more information, check the [Release Notes](https://github.com/DefectDojo/django-DefectDojo/releases/tag/2.58.0).
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
---
2+
title: "Qualys VMDR"
3+
toc_hide: true
4+
---
5+
6+
The [Qualys VMDR](https://www.qualys.com/apps/vulnerability-management-detection-response/) parser for DefectDojo supports imports from CSV format. This parser handles both QID-centric and CVE-centric export variants from Qualys VMDR (Vulnerability Management, Detection, and Response).
7+
8+
## Supported File Types
9+
10+
The Qualys VMDR parser accepts CSV file format in two variants:
11+
12+
**QID Format:** Primary vulnerability identifier is the Qualys QID
13+
**CVE Format:** Includes CVE identifiers and CVSS scores from NVD
14+
15+
To generate these files from Qualys VMDR:
16+
17+
1. Log into your Qualys VMDR console
18+
2. Navigate to Vulnerabilities > Vulnerability Management
19+
3. Select the assets or vulnerabilities to export
20+
4. Click "Download" and select CSV format
21+
5. Choose either QID-centric or CVE-centric export option
22+
6. Upload the downloaded CSV file to DefectDojo
23+
24+
## Default Deduplication
25+
26+
The parser uses `DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL_OR_HASH_CODE`, which tries `unique_id_from_tool` (populated with the Qualys QID) first and falls back to hashcode deduplication.
27+
28+
**Hashcode fields:** `title`, `component_name`, `vuln_id_from_tool`
29+
30+
For more information, see [About Deduplication](https://docs.defectdojo.com/en/working_with_findings/finding_deduplication/about_deduplication/).
31+
32+
### Sample Scan Data
33+
34+
Sample Qualys VMDR scans can be found in the [sample scan data folder](https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/qualys_vmdr).
35+
36+
## Link To Tool
37+
38+
- [Qualys VMDR](https://www.qualys.com/apps/vulnerability-management-detection-response/)
39+
- [Qualys Documentation](https://www.qualys.com/documentation/)
40+
41+
## QID Format (Primary Export)
42+
43+
### QID Format Field Mapping
44+
45+
<details>
46+
<summary>Click to expand Field Mapping Table</summary>
47+
48+
| Source Field | DefectDojo Field | Notes |
49+
| ------------ | ---------------- | ----- |
50+
| Title | title | Truncated to 500 characters |
51+
| Severity | severity | Mapped: 1=Info, 2=Low, 3=Medium, 4=High, 5=Critical |
52+
| Severity | severity_justification | Preserved as "Qualys Severity: X" |
53+
| QID | unique_id_from_tool | Native Qualys vulnerability identifier |
54+
| QID | vuln_id_from_tool | Also used as vulnerability ID |
55+
| First Detected | date | Parsed to date object |
56+
| Status | active | True if "ACTIVE", False otherwise |
57+
| Solution | mitigation | Remediation guidance |
58+
| Threat | impact | Threat description |
59+
| Asset Name | component_name | Asset/server name |
60+
| Category | service | Vulnerability category |
61+
| Asset IPV4 | unsaved_endpoints | Multiple endpoints if comma-separated |
62+
| Asset IPV6 | unsaved_endpoints | Fallback if no IPv4 |
63+
| Asset Tags | unsaved_tags | Split on comma |
64+
| Results | description | Included in structured description |
65+
66+
</details>
67+
68+
### Additional Finding Settings (QID Format)
69+
70+
| Finding Field | Default Value | Notes |
71+
|---------------|---------------|-------|
72+
| static_finding | True | Vulnerability scan data |
73+
| dynamic_finding | False | Not dynamic testing |
74+
75+
## CVE Format (Extended Export)
76+
77+
### CVE Format Field Mapping
78+
79+
<details>
80+
<summary>Click to expand Field Mapping Table</summary>
81+
82+
| Source Field | DefectDojo Field | Notes |
83+
| ------------ | ---------------- | ----- |
84+
| CVE | vuln_id_from_tool | CVE identifier (e.g., CVE-2021-44228) |
85+
| CVE | unsaved_vulnerability_ids | Also added for CVE tracking |
86+
| CVE-Description | description | Prepended to structured description |
87+
| CVSSv3.1 Base (nvd) | cvssv3_score | Numeric CVSS score |
88+
| Title | title | Truncated to 500 characters |
89+
| Severity | severity | Mapped: 1=Info, 2=Low, 3=Medium, 4=High, 5=Critical |
90+
| Severity | severity_justification | Preserved as "Qualys Severity: X" |
91+
| QID | unique_id_from_tool | Native Qualys vulnerability identifier |
92+
| First Detected | date | Parsed to date object |
93+
| Status | active | True if "ACTIVE", False otherwise |
94+
| Solution | mitigation | Remediation guidance |
95+
| Threat | impact | Threat description |
96+
| Asset Name | component_name | Asset/server name |
97+
| Category | service | Vulnerability category |
98+
| Asset IPV4 | unsaved_endpoints | Multiple endpoints if comma-separated |
99+
| Asset IPV6 | unsaved_endpoints | Fallback if no IPv4 |
100+
| Asset Tags | unsaved_tags | Split on comma |
101+
| Results | description | Included in structured description |
102+
103+
</details>
104+
105+
### Additional Finding Settings (CVE Format)
106+
107+
| Finding Field | Default Value | Notes |
108+
|---------------|---------------|-------|
109+
| static_finding | True | Vulnerability scan data |
110+
| dynamic_finding | False | Not dynamic testing |
111+
112+
## Special Processing Notes
113+
114+
### Severity Conversion
115+
116+
Qualys severity levels (1-5 numeric scale) are converted to DefectDojo severity levels:
117+
- `1` → Info
118+
- `2` → Low
119+
- `3` → Medium
120+
- `4` → High
121+
- `5` → Critical
122+
123+
The original Qualys severity is preserved in the severity_justification field as "Qualys Severity: X".
124+
125+
### Endpoint Handling
126+
127+
The parser creates Endpoint objects from IP addresses:
128+
- Multiple IPv4 addresses (comma-separated) create multiple endpoints
129+
- Falls back to IPv6 if no IPv4 address is present
130+
131+
### CSV Format Handling
132+
133+
Qualys VMDR exports use a non-standard CSV format where each row is wrapped in outer quotes and fields are delimited by `,""` instead of standard `","`. The parser automatically detects and handles both standard and non-standard CSV formats.
134+
135+
**Multi-line records:** Qualys fields such as Results and Threat may contain embedded newlines. The parser correctly assembles multi-line records that span multiple lines in the CSV file, including records containing malformed quote patterns in fields like Results.
136+
137+
**Metadata lines:** Some Qualys exports include 3 metadata lines (report title, date range, column count) before the header row. The parser auto-detects whether metadata is present and skips it accordingly.
138+
139+
### Data Cleaning
140+
141+
- **HTML tags** in fields like Threat (mapped to impact) are stripped automatically
142+
- **Qualys null markers** (`'-`) are filtered and treated as empty values
143+
- **Stray quotes** left by the non-standard CSV format are cleaned from field values
144+
145+
### Format Detection
146+
147+
The parser automatically detects whether the import file is QID format or CVE format by examining the first column of the header row:
148+
- If first column is "QID" → QID format parser is used
149+
- If first column is "CVE" → CVE format parser is used

docs/package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dojo/api_v2/serializers.py

Lines changed: 3 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from rest_framework import serializers
2222
from rest_framework.exceptions import NotFound
2323
from rest_framework.exceptions import ValidationError as RestFrameworkValidationError
24-
from rest_framework.fields import DictField, MultipleChoiceField
24+
from rest_framework.fields import DictField
2525

2626
import dojo.finding.helper as finding_helper
2727
import dojo.risk_acceptance.helper as ra_helper
@@ -43,9 +43,7 @@
4343
from dojo.jira import services as jira_services
4444
from dojo.location.models import Location, LocationFindingReference
4545
from dojo.models import (
46-
DEFAULT_NOTIFICATION,
4746
IMPORT_ACTIONS,
48-
NOTIFICATION_CHOICES,
4947
SEVERITIES,
5048
SEVERITY_CHOICES,
5149
STATS_FIELDS,
@@ -82,8 +80,6 @@
8280
Note_Type,
8381
NoteHistory,
8482
Notes,
85-
Notification_Webhooks,
86-
Notifications,
8783
Product,
8884
Product_API_Scan_Configuration,
8985
Product_Group,
@@ -3069,110 +3065,7 @@ class FindingNoteSerializer(serializers.Serializer):
30693065
note_id = serializers.IntegerField()
30703066

30713067

3072-
class NotificationsSerializer(serializers.ModelSerializer):
3073-
product = serializers.PrimaryKeyRelatedField(
3074-
queryset=Product.objects.all(),
3075-
required=False,
3076-
default=None,
3077-
allow_null=True,
3078-
)
3079-
user = serializers.PrimaryKeyRelatedField(
3080-
queryset=Dojo_User.objects.all(),
3081-
required=False,
3082-
default=None,
3083-
allow_null=True,
3084-
)
3085-
product_type_added = MultipleChoiceField(
3086-
choices=NOTIFICATION_CHOICES, default=DEFAULT_NOTIFICATION,
3087-
)
3088-
product_added = MultipleChoiceField(
3089-
choices=NOTIFICATION_CHOICES, default=DEFAULT_NOTIFICATION,
3090-
)
3091-
engagement_added = MultipleChoiceField(
3092-
choices=NOTIFICATION_CHOICES, default=DEFAULT_NOTIFICATION,
3093-
)
3094-
test_added = MultipleChoiceField(
3095-
choices=NOTIFICATION_CHOICES, default=DEFAULT_NOTIFICATION,
3096-
)
3097-
scan_added = MultipleChoiceField(
3098-
choices=NOTIFICATION_CHOICES, default=DEFAULT_NOTIFICATION,
3099-
)
3100-
jira_update = MultipleChoiceField(
3101-
choices=NOTIFICATION_CHOICES, default=DEFAULT_NOTIFICATION,
3102-
)
3103-
upcoming_engagement = MultipleChoiceField(
3104-
choices=NOTIFICATION_CHOICES, default=DEFAULT_NOTIFICATION,
3105-
)
3106-
stale_engagement = MultipleChoiceField(
3107-
choices=NOTIFICATION_CHOICES, default=DEFAULT_NOTIFICATION,
3108-
)
3109-
auto_close_engagement = MultipleChoiceField(
3110-
choices=NOTIFICATION_CHOICES, default=DEFAULT_NOTIFICATION,
3111-
)
3112-
close_engagement = MultipleChoiceField(
3113-
choices=NOTIFICATION_CHOICES, default=DEFAULT_NOTIFICATION,
3114-
)
3115-
user_mentioned = MultipleChoiceField(
3116-
choices=NOTIFICATION_CHOICES, default=DEFAULT_NOTIFICATION,
3117-
)
3118-
code_review = MultipleChoiceField(
3119-
choices=NOTIFICATION_CHOICES, default=DEFAULT_NOTIFICATION,
3120-
)
3121-
review_requested = MultipleChoiceField(
3122-
choices=NOTIFICATION_CHOICES, default=DEFAULT_NOTIFICATION,
3123-
)
3124-
other = MultipleChoiceField(
3125-
choices=NOTIFICATION_CHOICES, default=DEFAULT_NOTIFICATION,
3126-
)
3127-
sla_breach = MultipleChoiceField(
3128-
choices=NOTIFICATION_CHOICES, default=DEFAULT_NOTIFICATION,
3129-
)
3130-
sla_breach_combined = MultipleChoiceField(
3131-
choices=NOTIFICATION_CHOICES, default=DEFAULT_NOTIFICATION,
3132-
)
3133-
risk_acceptance_expiration = MultipleChoiceField(
3134-
choices=NOTIFICATION_CHOICES, default=DEFAULT_NOTIFICATION,
3135-
)
3136-
template = serializers.BooleanField(default=False)
3137-
3138-
class Meta:
3139-
model = Notifications
3140-
fields = "__all__"
3141-
3142-
def validate(self, data):
3143-
user = None
3144-
product = None
3145-
template = False
3146-
3147-
if self.instance is not None:
3148-
user = self.instance.user
3149-
product = self.instance.product
3150-
3151-
if "user" in data:
3152-
user = data.get("user")
3153-
if "product" in data:
3154-
product = data.get("product")
3155-
if "template" in data:
3156-
template = data.get("template")
3157-
3158-
if (
3159-
template
3160-
and Notifications.objects.filter(template=True).count() > 0
3161-
):
3162-
msg = "Notification template already exists"
3163-
raise ValidationError(msg)
3164-
if (
3165-
self.instance is None
3166-
or user != self.instance.user
3167-
or product != self.instance.product
3168-
):
3169-
notifications = Notifications.objects.filter(
3170-
user=user, product=product, template=template,
3171-
).count()
3172-
if notifications > 0:
3173-
msg = "Notification for user and product already exists"
3174-
raise ValidationError(msg)
3175-
return data
3068+
from dojo.notifications.api.serializer import NotificationsSerializer # noqa: E402, F401 -- backward compat
31763069

31773070

31783071
class EngagementPresetsSerializer(serializers.ModelSerializer):
@@ -3349,7 +3242,4 @@ def create(self, validated_data):
33493242
raise
33503243

33513244

3352-
class NotificationWebhooksSerializer(serializers.ModelSerializer):
3353-
class Meta:
3354-
model = Notification_Webhooks
3355-
fields = "__all__"
3245+
from dojo.notifications.api.serializer import NotificationWebhooksSerializer # noqa: E402, F401 -- backward compat

0 commit comments

Comments
 (0)