Skip to content

Commit 15141de

Browse files
authored
Merge pull request #13238 from DefectDojo/release/2.50.3
Release: Merge release into master from: release/2.50.3
2 parents 68821a8 + f69c5a0 commit 15141de

File tree

21 files changed

+158
-195
lines changed

21 files changed

+158
-195
lines changed

components/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "defectdojo",
3-
"version": "2.50.2",
3+
"version": "2.50.3",
44
"license" : "BSD-3-Clause",
55
"private": true,
66
"dependencies": {

docs/content/en/about_defectdojo/about_docs.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ Other guides for working with an Open-Source install:
6767

6868
If you run into trouble with an Open Source install, we highly recommend asking questions on the [OWASP Slack](https://owasp.org/slack/invite). Our community members are active on the **# defectdojo** channel and can help you with issues you’re facing.
6969

70+
Looking for cool DefectDojo laptop stickers? As a thank you for being a part of the DefectDojo community, you can sign up to get some free DefectDojo stickers. For more information, check out [this link](https://defectdojo.com/defectdojo-sticker-request).
71+
7072
### Online Demo
7173

7274
A running example of DefectDojo (Open-Source Edition) is available on [our demo server](https://demo.defectdojo.org), using the credentials `admin` / `1Defectdojo@demo#appsec`. The demo server is refreshed regularly and provisioned with some sample data.

docs/content/en/changelog/changelog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ For Open Source release notes, please see the [Releases page on GitHub](https://
1010

1111
## Sept 2025: v2.50
1212

13+
### Sept 15, 2025: v2.50.2
14+
15+
* **(Pro UI)** Added Any/All status filtering. Filtering by status allows you to apply either AND (inner join) logic, or OR (outer join) logic to the filter.
16+
* **(Pro UI)** Added Contact Support form for On-Premise installs.
17+
1318
### Sept 9, 2025: v2.50.1
1419

1520
* **(Tools)** Removed CSV limit for Qualys HackerGuardian

docs/content/en/connecting_your_tools/parsers/file/generic.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ title: 'Generic Findings Import'
33
toc_hide: true
44
---
55

6-
Import Generic findings in CSV or JSON format.
6+
Generic Findings Import can be used to import any report in CSV or JSON format.
77

8-
Attributes supported for CSV:
8+
### Supported Attributes (CSV)
99

1010
- Date: Date of the finding in mm/dd/yyyy format.
1111
- Title: Title of the finding
@@ -37,6 +37,8 @@ The CSV expects a header row with the names of the attributes.
3737

3838
Date fields are parsed using [dateutil.parse](https://dateutil.readthedocs.io/en/stable/parser.html) supporting a variety of formats such a YYYY-MM-DD or ISO-8601.
3939

40+
### Supported Attributes (JSON)
41+
4042
The list of supported fields in JSON format:
4143

4244
- title: **Required.** String
@@ -93,7 +95,7 @@ The list of supported fields in JSON format:
9395
- ransomware_used: Bool
9496
- fix_available: Bool
9597

96-
Example of JSON format:
98+
### Example JSON
9799

98100
```JSON
99101
{
Lines changed: 7 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -1,136 +1,18 @@
11
---
2-
title: "Generic Findings Import"
2+
title: "Using Generic Findings Import"
33
toc_hide: true
44
weight: 2
55
---
66

7-
You can use Generic Findings Import as a method to ingest JSON or CSV files into DefectDojo which are not already in the supported parsers list.
7+
Open-source and Pro users can use Generic Findings Import as a method to ingest JSON or CSV files into DefectDojo which are not already in the supported Tools list.
88

9-
Files uploaded using Generic Findings Import must conform to the accepted format with respect to CSV column headers / JSON attributes.
9+
Using Generic Findings Import will create a new Test Type in your DefectDojo instance called "`{The Name Of Your Test}` (Generic Findings Import)". For example, this JSON content will result in a Test Type called "Example Report (Generic Findings Import)":
1010

11-
These attributes are supported for CSV:
12-
13-
- Date: Date of the finding in mm/dd/yyyy format.
14-
- Title: Title of the finding
15-
- CweId: Cwe identifier, must be an integer value.
16-
- epss_score: The probability of exploitation in the next 30 days, must be a float value between 0 and 1.0.
17-
- epss_percentile: The proportion of all scored vulnerabilities with the same or a lower EPSS score, must be a float value between 0 and 1.0.
18-
- Url: Url associated with the finding.
19-
- Severity: Severity of the finding. Must be one of Info, Low, Medium, High, or Critical.
20-
- Description: Description of the finding. Can be multiple lines if enclosed in double quotes.
21-
- Mitigation: Possible Mitigations for the finding. Can be multiple lines if enclosed in double quotes.
22-
- Impact: Detailed impact of the finding. Can be multiple lines if enclosed in double quotes.
23-
- References: References associated with the finding. Can be multiple lines if enclosed in double quotes.
24-
- Active: Indicator if the finding is active. Must be empty, TRUE or FALSE
25-
- Verified: Indicator if the finding has been verified. Must be empty, TRUE, or FALSE
26-
- FalsePositive: Indicator if the finding is a false positive. Must be TRUE, or FALSE.
27-
- Duplicate: Indicator if the finding is a duplicate. Must be TRUE, or FALSE
28-
29-
The CSV expects a header row with the names of the attributes.
30-
31-
Example of JSON format:
32-
33-
```JSON
34-
{
35-
"findings": [
36-
{
37-
"title": "test title with endpoints as dict",
38-
"description": "Some very long description with\n\n some UTF-8 chars à qu'il est beau",
39-
"severity": "Medium",
40-
"mitigation": "Some mitigation",
41-
"date": "2021-01-06",
42-
"cve": "CVE-2020-36234",
43-
"cwe": 261,
44-
"cvssv3": "CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:N",
45-
"file_path": "src/first.cpp",
46-
"line": 13,
47-
"endpoints": [
48-
{
49-
"host": "exemple.com"
50-
}
51-
]
52-
},
53-
{
54-
"title": "test title with endpoints as strings",
55-
"description": "Some very long description with\n\n some UTF-8 chars à qu'il est beau2",
56-
"severity": "Critical",
57-
"mitigation": "Some mitigation",
58-
"date": "2021-01-06",
59-
"cve": "CVE-2020-36235",
60-
"cwe": 287,
61-
"cvssv3": "CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:N",
62-
"file_path": "src/two.cpp",
63-
"line": 135,
64-
"endpoints": [
65-
"http://urlfiltering.paloaltonetworks.com/test-command-and-control",
66-
"https://urlfiltering.paloaltonetworks.com:2345/test-pest"
67-
]
68-
},
69-
{
70-
"title": "test title",
71-
"description": "Some very long description with\n\n some UTF-8 chars à qu'il est beau2",
72-
"severity": "Critical",
73-
"mitigation": "Some mitigation",
74-
"date": "2021-01-06",
75-
"cve": "CVE-2020-36236",
76-
"cwe": 287,
77-
"cvssv3": "CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:N",
78-
"file_path": "src/threeeeeeeeee.cpp",
79-
"line": 1353
80-
}
81-
]
82-
}
83-
```
84-
85-
This parser supports an attributes that accept files as Base64 strings. These files are attached to the respective findings.
86-
87-
Example:
88-
89-
```JSON
90-
{
91-
"name": "My wonderful report",
92-
"findings": [
93-
{
94-
"title": "Vuln with image",
95-
"description": "Some very long description",
96-
"severity": "Medium",
97-
"files": [
98-
{
99-
"title": "Screenshot from 2017-04-10 16-54-19.png",
100-
"data": "iVBORw0KGgoAAAANSUhEUgAABWgAAAK0CAIAAAARSkPJAAAAA3N<...>TkSuQmCC"
101-
}
102-
]
103-
}
104-
]
105-
}
106-
```
107-
108-
This parser supports some additional attributes to be able to define custom `TestTypes` as well as influencing some meta fields on the `Test`:
109-
110-
- `name`: The internal name of the tool you are using. This is primarily informational, and used for reading the report manually.
111-
- `type`: The name of the test type to create in DefectDojo with the suffix of `(Generic Findings Import)`. The suffix is an important identifier for future users attempting to identify the test type to supply when importing new reports. This value is very important when fetching the correct test type to import findings into, so be sure to keep the `type` consistent from import to import! As an example, a report submitted with a `type` of `Internal Company Tool` will produce a test type in DefectDojo with the title `Internal Company Tool (Generic Findings Import)`. With this newly created test type, you can define custom `HASHCODE_FIELDS` or `DEDUPLICATION_ALGORITHM` in the settings.
112-
- `version`: The version of the tool you are using. This is primarily informational, and is used for reading the report manually and tracking format changes from version to version.
113-
- `description`: A brief description of the test. This could be an explanation of what the tool is reporting, where the tools is maintained, who the point of contact is for the tool when issues arise, or anything in between.
114-
- `static_tool`: Dictates that tool used is running static analysis methods to discover vulnerabilities.
115-
- `dynamic_tool`: Dictates that tool used is running dynamic analysis methods to discover vulnerabilities.
116-
- `soc`: Dictates that tool is used for reporting alerts from a soc (Pro Edition Only).
117-
118-
Example:
119-
120-
```JSON
12111
{
122-
"name": "My wonderful report",
123-
"type": "My custom Test type",
124-
"version": "1.0.5",
125-
"description": "A unicorn tool that is capable of static analysis, dynamic analysis, and even capturing soc alerts!",
126-
"static_tool": true,
127-
"dynamic_tool": true,
128-
"soc": true,
129-
"findings": [
130-
]
12+
"name": "Example Report",
13+
"findings": []
13114
}
132-
```
13315

134-
### Sample Scan Data
16+
DefectDojo Pro users can also consider using the [Universal Parser](../universal_parser), a tool which allows for highly customizable JSON, XML and CSV imports.
13517

136-
Sample Generic Findings Import scans can be found [here](https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/generic).
18+
For more information on supported parameters for Generic Findings Import, see the [Parser Guide](../file/generic)

dojo/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
# Django starts so that shared_task will use this app.
55
from .celery import app as celery_app # noqa: F401
66

7-
__version__ = "2.50.2"
7+
__version__ = "2.50.3"
88
__url__ = "https://github.com/DefectDojo/django-DefectDojo"
99
__docs__ = "https://documentation.defectdojo.com"

dojo/engagement/views.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1552,6 +1552,10 @@ def engagement_ics(request, eid):
15521552
eng = get_object_or_404(Engagement, id=eid)
15531553
start_date = datetime.combine(eng.target_start, datetime.min.time())
15541554
end_date = datetime.combine(eng.target_end, datetime.max.time())
1555+
if timezone.is_naive(start_date):
1556+
start_date = timezone.make_aware(start_date)
1557+
if timezone.is_naive(end_date):
1558+
end_date = timezone.make_aware(end_date)
15551559
uid = f"dojo_eng_{eng.id}_{eng.product.id}"
15561560
cal = get_cal_event(
15571561
start_date,

dojo/filters.py

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
RangeFilter,
3131
)
3232
from django_filters import rest_framework as filters
33-
from django_filters.filters import ChoiceFilter, _truncate
33+
from django_filters.filters import ChoiceFilter
3434
from drf_spectacular.types import OpenApiTypes
3535
from drf_spectacular.utils import extend_schema_field
3636
from polymorphic.base import ManagerInheritanceWarning
@@ -92,7 +92,7 @@
9292
from dojo.risk_acceptance.queries import get_authorized_risk_acceptances
9393
from dojo.test.queries import get_authorized_tests
9494
from dojo.user.queries import get_authorized_users
95-
from dojo.utils import get_system_setting, is_finding_groups_enabled
95+
from dojo.utils import get_system_setting, is_finding_groups_enabled, truncate_timezone_aware
9696

9797
logger = logging.getLogger(__name__)
9898

@@ -194,8 +194,8 @@ def filter(self, qs, value):
194194
if earliest_finding is not None:
195195
start_date = datetime.combine(
196196
earliest_finding.date, datetime.min.time()).replace(tzinfo=tzinfo())
197-
self.start_date = _truncate(start_date - timedelta(days=1))
198-
self.end_date = _truncate(now() + timedelta(days=1))
197+
self.start_date = truncate_timezone_aware(start_date - timedelta(days=1))
198+
self.end_date = truncate_timezone_aware(now() + timedelta(days=1))
199199
try:
200200
value = int(value)
201201
except (ValueError, TypeError):
@@ -654,16 +654,16 @@ class DateRangeFilter(ChoiceFilter):
654654
f"{name}__day": now().day,
655655
})),
656656
2: (_("Past 7 days"), lambda qs, name: qs.filter(**{
657-
f"{name}__gte": _truncate(now() - timedelta(days=7)),
658-
f"{name}__lt": _truncate(now() + timedelta(days=1)),
657+
f"{name}__gte": truncate_timezone_aware(now() - timedelta(days=7)),
658+
f"{name}__lt": truncate_timezone_aware(now() + timedelta(days=1)),
659659
})),
660660
3: (_("Past 30 days"), lambda qs, name: qs.filter(**{
661-
f"{name}__gte": _truncate(now() - timedelta(days=30)),
662-
f"{name}__lt": _truncate(now() + timedelta(days=1)),
661+
f"{name}__gte": truncate_timezone_aware(now() - timedelta(days=30)),
662+
f"{name}__lt": truncate_timezone_aware(now() + timedelta(days=1)),
663663
})),
664664
4: (_("Past 90 days"), lambda qs, name: qs.filter(**{
665-
f"{name}__gte": _truncate(now() - timedelta(days=90)),
666-
f"{name}__lt": _truncate(now() + timedelta(days=1)),
665+
f"{name}__gte": truncate_timezone_aware(now() - timedelta(days=90)),
666+
f"{name}__lt": truncate_timezone_aware(now() + timedelta(days=1)),
667667
})),
668668
5: (_("Current month"), lambda qs, name: qs.filter(**{
669669
f"{name}__year": now().year,
@@ -673,8 +673,8 @@ class DateRangeFilter(ChoiceFilter):
673673
f"{name}__year": now().year,
674674
})),
675675
7: (_("Past year"), lambda qs, name: qs.filter(**{
676-
f"{name}__gte": _truncate(now() - timedelta(days=365)),
677-
f"{name}__lt": _truncate(now() + timedelta(days=1)),
676+
f"{name}__gte": truncate_timezone_aware(now() - timedelta(days=365)),
677+
f"{name}__lt": truncate_timezone_aware(now() + timedelta(days=1)),
678678
})),
679679
}
680680

@@ -700,43 +700,43 @@ class DateRangeOmniFilter(ChoiceFilter):
700700
f"{name}__day": now().day,
701701
})),
702702
2: (_("Next 7 days"), lambda qs, name: qs.filter(**{
703-
f"{name}__gte": _truncate(now() + timedelta(days=1)),
704-
f"{name}__lt": _truncate(now() + timedelta(days=7)),
703+
f"{name}__gte": truncate_timezone_aware(now() + timedelta(days=1)),
704+
f"{name}__lt": truncate_timezone_aware(now() + timedelta(days=7)),
705705
})),
706706
3: (_("Next 30 days"), lambda qs, name: qs.filter(**{
707-
f"{name}__gte": _truncate(now() + timedelta(days=1)),
708-
f"{name}__lt": _truncate(now() + timedelta(days=30)),
707+
f"{name}__gte": truncate_timezone_aware(now() + timedelta(days=1)),
708+
f"{name}__lt": truncate_timezone_aware(now() + timedelta(days=30)),
709709
})),
710710
4: (_("Next 90 days"), lambda qs, name: qs.filter(**{
711-
f"{name}__gte": _truncate(now() + timedelta(days=1)),
712-
f"{name}__lt": _truncate(now() + timedelta(days=90)),
711+
f"{name}__gte": truncate_timezone_aware(now() + timedelta(days=1)),
712+
f"{name}__lt": truncate_timezone_aware(now() + timedelta(days=90)),
713713
})),
714714
5: (_("Past 7 days"), lambda qs, name: qs.filter(**{
715-
f"{name}__gte": _truncate(now() - timedelta(days=7)),
716-
f"{name}__lt": _truncate(now() + timedelta(days=1)),
715+
f"{name}__gte": truncate_timezone_aware(now() - timedelta(days=7)),
716+
f"{name}__lt": truncate_timezone_aware(now() + timedelta(days=1)),
717717
})),
718718
6: (_("Past 30 days"), lambda qs, name: qs.filter(**{
719-
f"{name}__gte": _truncate(now() - timedelta(days=30)),
720-
f"{name}__lt": _truncate(now() + timedelta(days=1)),
719+
f"{name}__gte": truncate_timezone_aware(now() - timedelta(days=30)),
720+
f"{name}__lt": truncate_timezone_aware(now() + timedelta(days=1)),
721721
})),
722722
7: (_("Past 90 days"), lambda qs, name: qs.filter(**{
723-
f"{name}__gte": _truncate(now() - timedelta(days=90)),
724-
f"{name}__lt": _truncate(now() + timedelta(days=1)),
723+
f"{name}__gte": truncate_timezone_aware(now() - timedelta(days=90)),
724+
f"{name}__lt": truncate_timezone_aware(now() + timedelta(days=1)),
725725
})),
726726
8: (_("Current month"), lambda qs, name: qs.filter(**{
727727
f"{name}__year": now().year,
728728
f"{name}__month": now().month,
729729
})),
730730
9: (_("Past year"), lambda qs, name: qs.filter(**{
731-
f"{name}__gte": _truncate(now() - timedelta(days=365)),
732-
f"{name}__lt": _truncate(now() + timedelta(days=1)),
731+
f"{name}__gte": truncate_timezone_aware(now() - timedelta(days=365)),
732+
f"{name}__lt": truncate_timezone_aware(now() + timedelta(days=1)),
733733
})),
734734
10: (_("Current year"), lambda qs, name: qs.filter(**{
735735
f"{name}__year": now().year,
736736
})),
737737
11: (_("Next year"), lambda qs, name: qs.filter(**{
738-
f"{name}__gte": _truncate(now() + timedelta(days=1)),
739-
f"{name}__lt": _truncate(now() + timedelta(days=365)),
738+
f"{name}__gte": truncate_timezone_aware(now() + timedelta(days=1)),
739+
f"{name}__lt": truncate_timezone_aware(now() + timedelta(days=365)),
740740
})),
741741
}
742742

@@ -818,8 +818,8 @@ def any(self, qs, name):
818818
if earliest_finding is not None:
819819
start_date = datetime.combine(
820820
earliest_finding.date, datetime.min.time()).replace(tzinfo=tzinfo())
821-
self.start_date = _truncate(start_date - timedelta(days=1))
822-
self.end_date = _truncate(now() + timedelta(days=1))
821+
self.start_date = truncate_timezone_aware(start_date - timedelta(days=1))
822+
self.end_date = truncate_timezone_aware(now() + timedelta(days=1))
823823
return qs.all()
824824
return None
825825

@@ -839,8 +839,8 @@ def current_year(self, qs, name):
839839
})
840840

841841
def past_x_days(self, qs, name, days):
842-
self.start_date = _truncate(now() - timedelta(days=days))
843-
self.end_date = _truncate(now() + timedelta(days=1))
842+
self.start_date = truncate_timezone_aware(now() - timedelta(days=days))
843+
self.end_date = truncate_timezone_aware(now() + timedelta(days=1))
844844
return qs.filter(**{
845845
f"{name}__gte": self.start_date,
846846
f"{name}__lt": self.end_date,
@@ -884,8 +884,8 @@ def filter(self, qs, value):
884884
if earliest_finding is not None:
885885
start_date = datetime.combine(
886886
earliest_finding.date, datetime.min.time()).replace(tzinfo=tzinfo())
887-
self.start_date = _truncate(start_date - timedelta(days=1))
888-
self.end_date = _truncate(now() + timedelta(days=1))
887+
self.start_date = truncate_timezone_aware(start_date - timedelta(days=1))
888+
self.end_date = truncate_timezone_aware(now() + timedelta(days=1))
889889
try:
890890
value = int(value)
891891
except (ValueError, TypeError):

dojo/finding/views.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2650,6 +2650,7 @@ def finding_bulk_update_all(request, pid=None):
26502650
find.false_p = form.cleaned_data["false_p"]
26512651
find.out_of_scope = form.cleaned_data["out_of_scope"]
26522652
find.is_mitigated = form.cleaned_data["is_mitigated"]
2653+
find.under_review = form.cleaned_data["under_review"]
26532654
find.last_reviewed = timezone.now()
26542655
find.last_reviewed_by = request.user
26552656

0 commit comments

Comments
 (0)