Skip to content

Commit 73dddf6

Browse files
authored
Merge pull request DefectDojo#10647 from DefectDojo/release/2.36.6
Release: Merge release into master from: release/2.36.6
2 parents c58a297 + f58e43b commit 73dddf6

Some content is hidden

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

54 files changed

+4886
-660
lines changed

.github/workflows/refresh_helm_lock_file.yaml

-40
This file was deleted.

components/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "defectdojo",
3-
"version": "2.36.5",
3+
"version": "2.36.6",
44
"license" : "BSD-3-Clause",
55
"private": true,
66
"dependencies": {

dojo/__init__.py

+1-1
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.36.5'
7+
__version__ = '2.36.6'
88
__url__ = 'https://github.com/DefectDojo/django-DefectDojo'
99
__docs__ = 'https://documentation.defectdojo.com'

dojo/api_v2/views.py

+4-42
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@
164164
from dojo.user.utils import get_configuration_permissions_codenames
165165
from dojo.utils import (
166166
async_delete,
167+
generate_file_response,
167168
get_setting,
168169
get_system_setting,
169170
)
@@ -646,21 +647,8 @@ def download_file(self, request, file_id, pk=None):
646647
{"error": "File ID not associated with Engagement"},
647648
status=status.HTTP_404_NOT_FOUND,
648649
)
649-
# Get the path of the file in media root
650-
file_path = f"{settings.MEDIA_ROOT}/{file_object.file.url.lstrip(settings.MEDIA_URL)}"
651-
file_handle = open(file_path, "rb")
652650
# send file
653-
response = FileResponse(
654-
file_handle,
655-
content_type=f"{mimetypes.guess_type(file_path)}",
656-
status=status.HTTP_200_OK,
657-
)
658-
response["Content-Length"] = file_object.file.size
659-
response[
660-
"Content-Disposition"
661-
] = f'attachment; filename="{file_object.file.name}"'
662-
663-
return response
651+
return generate_file_response(file_object)
664652

665653

666654
class RiskAcceptanceViewSet(
@@ -1156,21 +1144,8 @@ def download_file(self, request, file_id, pk=None):
11561144
{"error": "File ID not associated with Finding"},
11571145
status=status.HTTP_404_NOT_FOUND,
11581146
)
1159-
# Get the path of the file in media root
1160-
file_path = f"{settings.MEDIA_ROOT}/{file_object.file.url.lstrip(settings.MEDIA_URL)}"
1161-
file_handle = open(file_path, "rb")
11621147
# send file
1163-
response = FileResponse(
1164-
file_handle,
1165-
content_type=f"{mimetypes.guess_type(file_path)}",
1166-
status=status.HTTP_200_OK,
1167-
)
1168-
response["Content-Length"] = file_object.file.size
1169-
response[
1170-
"Content-Disposition"
1171-
] = f'attachment; filename="{file_object.file.name}"'
1172-
1173-
return response
1148+
return generate_file_response(file_object)
11741149

11751150
@extend_schema(
11761151
request=serializers.FindingNoteSerializer,
@@ -2320,21 +2295,8 @@ def download_file(self, request, file_id, pk=None):
23202295
{"error": "File ID not associated with Test"},
23212296
status=status.HTTP_404_NOT_FOUND,
23222297
)
2323-
# Get the path of the file in media root
2324-
file_path = f"{settings.MEDIA_ROOT}/{file_object.file.url.lstrip(settings.MEDIA_URL)}"
2325-
file_handle = open(file_path, "rb")
23262298
# send file
2327-
response = FileResponse(
2328-
file_handle,
2329-
content_type=f"{mimetypes.guess_type(file_path)}",
2330-
status=status.HTTP_200_OK,
2331-
)
2332-
response["Content-Length"] = file_object.file.size
2333-
response[
2334-
"Content-Disposition"
2335-
] = f'attachment; filename="{file_object.file.name}"'
2336-
2337-
return response
2299+
return generate_file_response(file_object)
23382300

23392301

23402302
# Authorization: authenticated, configuration

dojo/apps.py

+5
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,19 @@ def ready(self):
7272
# Load any signals here that will be ready for runtime
7373
# Importing the signals file is good enough if using the reciever decorator
7474
import dojo.announcement.signals # noqa: F401
75+
import dojo.benchmark.signals # noqa: F401
76+
import dojo.cred.signals # noqa: F401
7577
import dojo.endpoint.signals # noqa: F401
7678
import dojo.engagement.signals # noqa: F401
7779
import dojo.finding_group.signals # noqa: F401
80+
import dojo.notes.signals # noqa: F401
7881
import dojo.product.signals # noqa: F401
7982
import dojo.product_type.signals # noqa: F401
83+
import dojo.risk_acceptance.signals # noqa: F401
8084
import dojo.sla_config.helpers # noqa: F401
8185
import dojo.tags_signals # noqa: F401
8286
import dojo.test.signals # noqa: F401
87+
import dojo.tool_product.signals # noqa: F401
8388

8489

8590
def get_model_fields_with_extra(model, extra_fields=()):

dojo/benchmark/signals.py

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import logging
2+
3+
from django.db.models.signals import pre_delete
4+
from django.dispatch import receiver
5+
6+
from dojo.models import Benchmark_Product
7+
from dojo.notes.helper import delete_related_notes
8+
9+
logger = logging.getLogger(__name__)
10+
11+
12+
@receiver(pre_delete, sender=Benchmark_Product)
13+
def benchmark_product_pre_delete(sender, instance, **kwargs):
14+
delete_related_notes(instance)

dojo/benchmark/views.py

+2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ def add_benchmark(queryset, product):
4343
pass
4444

4545

46+
@user_is_authorized(Product, Permissions.Benchmark_Edit, "pid")
4647
def update_benchmark(request, pid, _type):
4748
if request.method == "POST":
4849
bench_id = request.POST.get("bench_id")
@@ -90,6 +91,7 @@ def update_benchmark(request, pid, _type):
9091
)
9192

9293

94+
@user_is_authorized(Product, Permissions.Benchmark_Edit, "pid")
9395
def update_benchmark_summary(request, pid, _type, summary):
9496
if request.method == "POST":
9597
field = request.POST.get("field")

dojo/components/views.py

+1
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,6 @@ def components(request):
7070
"filter": comp_filter,
7171
"result": result,
7272
"component_words": sorted(set(component_words)),
73+
"enable_table_filtering": get_system_setting("enable_ui_table_based_searching"),
7374
},
7475
)

dojo/cred/signals.py

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import logging
2+
3+
from django.db.models.signals import pre_delete
4+
from django.dispatch import receiver
5+
6+
from dojo.models import Cred_User
7+
from dojo.notes.helper import delete_related_notes
8+
9+
logger = logging.getLogger(__name__)
10+
11+
12+
@receiver(pre_delete, sender=Cred_User)
13+
def cred_user_pre_delete(sender, instance, **kwargs):
14+
delete_related_notes(instance)

dojo/cred/views.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ def view_cred_details(request, ttid):
112112
'cred': cred,
113113
'form': form,
114114
'notes': notes,
115-
'cred_products': cred_products
115+
'cred_products': cred_products,
116+
'person': request.user.username,
116117
})
117118

118119

@@ -650,7 +651,7 @@ def delete_cred_controller(request, destination_url, id, ttid):
650651
if id:
651652
product = None
652653
if destination_url == "all_cred_product":
653-
product = get_object_or_404(Product, id)
654+
product = get_object_or_404(Product, id=id)
654655
elif destination_url == "view_engagement":
655656
engagement = get_object_or_404(Engagement, id=id)
656657
product = engagement.product
@@ -669,7 +670,7 @@ def delete_cred_controller(request, destination_url, id, ttid):
669670

670671
@user_is_authorized(Cred_User, Permissions.Credential_Delete, 'ttid')
671672
def delete_cred(request, ttid):
672-
return delete_cred_controller(request, "cred", 0, ttid)
673+
return delete_cred_controller(request, "cred", 0, ttid=ttid)
673674

674675

675676
@user_is_authorized(Product, Permissions.Product_Edit, 'pid')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 4.2.13 on 2024-07-23 19:53
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('dojo', '0212_sla_configuration_enforce_critical_and_more'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='system_settings',
15+
name='enable_ui_table_based_searching',
16+
field=models.BooleanField(default=True, help_text='With this setting enabled, table headings will contain sort buttons for the current page of data in addition to sorting buttons that consider data from all pages.', verbose_name='Enable UI Table Based Filtering/Sorting'),
17+
),
18+
]

dojo/engagement/signals.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
from auditlog.models import LogEntry
22
from django.conf import settings
33
from django.contrib.contenttypes.models import ContentType
4-
from django.db.models.signals import post_delete, post_save, pre_save
4+
from django.db.models.signals import post_delete, post_save, pre_delete, pre_save
55
from django.dispatch import receiver
66
from django.urls import reverse
77
from django.utils.translation import gettext as _
88

99
from dojo.models import Engagement
10+
from dojo.notes.helper import delete_related_notes
1011
from dojo.notifications.helper import create_notification
1112

1213

@@ -55,3 +56,8 @@ def engagement_post_delete(sender, instance, using, origin, **kwargs):
5556
url=reverse('view_product', args=(instance.product.id, )),
5657
recipients=[instance.lead],
5758
icon="exclamation-triangle")
59+
60+
61+
@receiver(pre_delete, sender=Engagement)
62+
def engagement_pre_delete(sender, instance, **kwargs):
63+
delete_related_notes(instance)

dojo/engagement/views.py

+10-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import csv
22
import logging
3+
import mimetypes
34
import operator
45
import re
56
from datetime import datetime
@@ -257,6 +258,7 @@ def engagements_all(request):
257258
'filter_form': filtered.form,
258259
'name_words': sorted(set(name_words)),
259260
'eng_words': sorted(set(eng_words)),
261+
"enable_table_filtering": get_system_setting("enable_ui_table_based_searching"),
260262
})
261263

262264

@@ -438,6 +440,8 @@ def get_filtered_tests(
438440

439441
def get(self, request, eid, *args, **kwargs):
440442
eng = get_object_or_404(Engagement, id=eid)
443+
# Make sure the user is authorized
444+
user_has_permission_or_403(request.user, eng, Permissions.Engagement_View)
441445
tests = eng.test_set.all().order_by('test_type__name', '-updated')
442446
default_page_num = 10
443447
tests_filter = self.get_filtered_tests(request, tests, eng)
@@ -506,6 +510,8 @@ def get(self, request, eid, *args, **kwargs):
506510

507511
def post(self, request, eid, *args, **kwargs):
508512
eng = get_object_or_404(Engagement, id=eid)
513+
# Make sure the user is authorized
514+
user_has_permission_or_403(request.user, eng, Permissions.Engagement_View)
509515
tests = eng.test_set.all().order_by('test_type__name', '-updated')
510516

511517
default_page_num = 10
@@ -1435,6 +1441,7 @@ def view_edit_risk_acceptance(request, eid, raid, edit_mode=False):
14351441
'request': request,
14361442
'add_findings': add_fpage,
14371443
'return_url': get_return_url(request),
1444+
"enable_table_filtering": get_system_setting("enable_ui_table_based_searching"),
14381445
})
14391446

14401447

@@ -1479,12 +1486,11 @@ def delete_risk_acceptance(request, eid, raid):
14791486

14801487
@user_is_authorized(Engagement, Permissions.Engagement_View, 'eid')
14811488
def download_risk_acceptance(request, eid, raid):
1482-
import mimetypes
1483-
14841489
mimetypes.init()
1485-
14861490
risk_acceptance = get_object_or_404(Risk_Acceptance, pk=raid)
1487-
1491+
# Ensure the risk acceptance is under the supplied engagement
1492+
if not Engagement.objects.filter(risk_acceptance=risk_acceptance, id=eid).exists():
1493+
raise PermissionDenied
14881494
response = StreamingHttpResponse(
14891495
FileIterWrapper(
14901496
open(settings.MEDIA_ROOT + "/" + risk_acceptance.path.name, mode='rb')))

dojo/finding/helper.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
Vulnerability_Id,
2424
Vulnerability_Id_Template,
2525
)
26+
from dojo.notes.helper import delete_related_notes
2627
from dojo.utils import get_current_user, mass_model_updater, to_str_typed
2728

2829
logger = logging.getLogger(__name__)
@@ -402,8 +403,8 @@ def finding_pre_delete(sender, instance, **kwargs):
402403
logger.debug('finding pre_delete: %d', instance.id)
403404
# this shouldn't be necessary as Django should remove any Many-To-Many entries automatically, might be a bug in Django?
404405
# https://code.djangoproject.com/ticket/154
405-
406406
instance.found_by.clear()
407+
delete_related_notes(instance)
407408

408409

409410
def finding_delete(instance, **kwargs):

dojo/finding/views.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,7 @@ def get_initial_context(self, request: HttpRequest):
376376
"jira_project": None,
377377
"github_config": None,
378378
"bulk_edit_form": FindingBulkUpdateForm(request.GET),
379+
"enable_table_filtering": get_system_setting("enable_ui_table_based_searching"),
379380
"title_words": get_words_for_field(Finding, "title"),
380381
"component_words": get_words_for_field(Finding, "component_name"),
381382
}
@@ -742,6 +743,7 @@ def get_initial_context(self, request: HttpRequest, finding: Finding, user: Dojo
742743
"files": finding.files.all(),
743744
"note_type_activation": note_type_activation,
744745
"available_note_types": available_note_types,
746+
"enable_table_filtering": get_system_setting("enable_ui_table_based_searching"),
745747
"product_tab": Product_Tab(
746748
finding.test.engagement.product, title="View Finding", tab="findings"
747749
)
@@ -1736,7 +1738,7 @@ def request_finding_review(request, fid):
17361738
return render(
17371739
request,
17381740
"dojo/review_finding.html",
1739-
{"finding": finding, "product_tab": product_tab, "user": user, "form": form},
1741+
{"finding": finding, "product_tab": product_tab, "user": user, "form": form, "enable_table_filtering": get_system_setting("enable_ui_table_based_searching"), },
17401742
)
17411743

17421744

dojo/importers/base_importer.py

+5-8
Original file line numberDiff line numberDiff line change
@@ -212,15 +212,12 @@ def parse_findings(
212212
"""
213213
Determine how to parse the findings based on the presence of the
214214
`get_tests` function on the parser object
215+
216+
This function will vary by importer, so it is marked as
217+
abstract with a prohibitive exception raised if the
218+
method is attempted to to be used by the BaseImporter class
215219
"""
216-
# Attempt any preprocessing before generating findings
217-
if len(self.parsed_findings) == 0 or self.test is None:
218-
scan = self.process_scan_file(scan)
219-
if hasattr(parser, 'get_tests'):
220-
self.parsed_findings = self.parse_findings_dynamic_test_type(scan, parser)
221-
else:
222-
self.parsed_findings = self.parse_findings_static_test_type(scan, parser)
223-
return self.parsed_findings
220+
self.check_child_implementation_exception()
224221

225222
def sync_process_findings(
226223
self,

0 commit comments

Comments
 (0)