Skip to content

Commit 5966c13

Browse files
authored
Merge pull request DefectDojo#7806 from DefectDojo/release/2.20.1
Release: Merge release into master from: release/2.20.1
2 parents 0cffbe2 + 06e722b commit 5966c13

File tree

17 files changed

+422
-337
lines changed

17 files changed

+422
-337
lines changed

components/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
{
22
"name": "defectdojo",
3-
"version": "2.20.0",
3+
"version": "2.20.1",
44
"license" : "BSD-3-Clause",
55
"private": true,
66
"dependencies": {
77
"JUMFlot": "jumjum123/JUMFlot#*",
8-
"bootstrap": "^3.4.0",
8+
"bootstrap": "^3.4.1",
99
"bootstrap-select": "^1.13.18",
1010
"bootstrap-social": "^4.0.0",
1111
"bootstrap-wysiwyg": "^2.0.0",
12-
"bootswatch": "3.4.1",
12+
"bootswatch": "5.2.3",
1313
"chosen-bootstrap": "https://github.com/dbtek/chosen-bootstrap",
1414
"chosen-js": "^1.8.7",
1515
"clipboard": "^2.0.11",
16-
"components-jqueryui": "^1.0.0",
1716
"datatables.net": "^1.13.3",
1817
"datatables.net-buttons-bs": "^2.3.5",
1918
"datatables.net-buttons-dt": "^2.3.5",
@@ -29,6 +28,7 @@
2928
"google-code-prettify": "^1.0.0",
3029
"jquery": "^3.6.3",
3130
"jquery-highlight": "3.5.0",
31+
"jquery-ui": "1.13.2",
3232
"jquery.cookie": "1.4.1",
3333
"jquery.flot.tooltip": "^0.9.0",
3434
"jquery.hotkeys": "jeresig/jquery.hotkeys#master",

components/yarn.lock

Lines changed: 277 additions & 308 deletions
Large diffs are not rendered by default.

dc-unittest.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,4 @@ then
7373
fi
7474

7575
echo "Running docker compose unit tests with profile $PROFILE and test case $TEST_CASE ..."
76-
docker-compose --profile $PROFILE --env-file ./docker/environments/$PROFILE.env exec uwsgi bash -c "python manage.py test $TEST_CASE -v2"
76+
docker-compose --profile $PROFILE --env-file ./docker/environments/$PROFILE.env exec uwsgi bash -c "python manage.py test $TEST_CASE -v2 --keepdb"

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
66

7-
__version__ = '2.20.0'
7+
__version__ = '2.20.1'
88
__url__ = 'https://github.com/DefectDojo/django-DefectDojo'
99
__docs__ = 'https://documentation.defectdojo.com'

dojo/api_v2/serializers.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
Network_Locations, UserContactInfo, Product_API_Scan_Configuration, DEFAULT_NOTIFICATION, \
2525
Vulnerability_Id, Vulnerability_Id_Template, get_current_date, \
2626
Question, TextQuestion, ChoiceQuestion, Answer, TextAnswer, ChoiceAnswer, \
27-
Engagement_Survey, Answered_Survey, General_Survey
27+
Engagement_Survey, Answered_Survey, General_Survey, Check_List
2828

2929
from dojo.tools.factory import requires_file, get_choices_sorted, requires_tool_type
3030
from dojo.utils import is_scan_file_too_large
@@ -34,6 +34,7 @@
3434
from django.contrib.auth.password_validation import validate_password
3535
from django.contrib.auth.models import Permission
3636
from django.utils import timezone
37+
from django.urls import reverse
3738
from django.db.utils import IntegrityError
3839
import six
3940
from django.utils.translation import gettext_lazy as _
@@ -637,6 +638,14 @@ class Meta:
637638
fields = ['file']
638639

639640

641+
class RiskAcceptanceProofSerializer(serializers.ModelSerializer):
642+
path = serializers.FileField(required=True)
643+
644+
class Meta:
645+
model = Risk_Acceptance
646+
fields = ['path']
647+
648+
640649
class ProductMemberSerializer(serializers.ModelSerializer):
641650

642651
class Meta:
@@ -796,6 +805,12 @@ def to_representation(self, data):
796805
return new_data
797806

798807

808+
class EngagementCheckListSerializer(serializers.ModelSerializer):
809+
class Meta:
810+
model = Check_List
811+
fields = '__all__'
812+
813+
799814
class AppAnalysisSerializer(TaggitSerializer, serializers.ModelSerializer):
800815
tags = TagListSerializerField(required=False)
801816

@@ -1130,6 +1145,7 @@ class Meta:
11301145
class RiskAcceptanceSerializer(serializers.ModelSerializer):
11311146
recommendation = serializers.SerializerMethodField()
11321147
decision = serializers.SerializerMethodField()
1148+
path = serializers.SerializerMethodField()
11331149

11341150
@extend_schema_field(serializers.CharField())
11351151
@swagger_serializer_method(serializers.CharField())
@@ -1141,6 +1157,23 @@ def get_recommendation(self, obj):
11411157
def get_decision(self, obj):
11421158
return Risk_Acceptance.TREATMENT_TRANSLATIONS.get(obj.decision)
11431159

1160+
@extend_schema_field(serializers.CharField())
1161+
@swagger_serializer_method(serializers.CharField())
1162+
def get_path(self, obj):
1163+
risk_acceptance_id = obj.id
1164+
engagement_id = Engagement.objects.filter(risk_acceptance__id__in=[obj.id]).first().id
1165+
path = reverse('download_risk_acceptance', args=(engagement_id, risk_acceptance_id))
1166+
request = self.context.get("request")
1167+
if request:
1168+
path = request.build_absolute_uri(path)
1169+
return path
1170+
1171+
@extend_schema_field(serializers.IntegerField())
1172+
@swagger_serializer_method(serializers.IntegerField())
1173+
def get_engagement(self, obj):
1174+
engagement = Engagement.objects.filter(risk_acceptance__id__in=[obj.id]).first()
1175+
return EngagementSerializer(read_only=True).to_representation(engagement)
1176+
11441177
class Meta:
11451178
model = Risk_Acceptance
11461179
fields = '__all__'

dojo/api_v2/views.py

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
BurpRawRequestResponse, FileUpload, Product_Type_Member, Product_Member, Dojo_Group, \
3333
Product_Group, Product_Type_Group, Role, Global_Role, Dojo_Group_Member, Engagement_Presets, Network_Locations, \
3434
UserContactInfo, Product_API_Scan_Configuration, Cred_Mapping, Cred_User, Question, Answer, \
35-
Engagement_Survey, Answered_Survey, General_Survey
35+
Engagement_Survey, Answered_Survey, General_Survey, Check_List
3636
from dojo.endpoint.views import get_endpoint_ids
3737
from dojo.reports.views import report_url_resolver, prefetch_related_findings_for_report
3838
from dojo.finding.views import set_finding_as_original_internal, reset_finding_duplicate_status_internal, \
@@ -258,7 +258,9 @@ class EngagementViewSet(prefetch.PrefetchListMixin,
258258
queryset = Engagement.objects.none()
259259
filter_backends = (DjangoFilterBackend,)
260260
filterset_class = ApiEngagementFilter
261-
swagger_schema = prefetch.get_prefetch_schema(["engagements_list", "engagements_read"], serializers.EngagementSerializer).to_schema()
261+
swagger_schema = prefetch.get_prefetch_schema(["engagements_list", "engagements_read"], serializers.EngagementSerializer).composeWith(
262+
prefetch.get_prefetch_schema(["engagements_complete_checklist_read"], serializers.EngagementCheckListSerializer)
263+
).to_schema()
262264
permission_classes = (IsAuthenticated, permissions.UserHasEngagementPermission)
263265

264266
@property
@@ -429,6 +431,41 @@ def files(self, request, pk=None):
429431
})
430432
return Response(serialized_files.data, status=status.HTTP_200_OK)
431433

434+
@extend_schema(
435+
methods=['POST'],
436+
request=serializers.EngagementCheckListSerializer,
437+
responses={status.HTTP_201_CREATED: serializers.EngagementCheckListSerializer}
438+
)
439+
@swagger_auto_schema(
440+
method='post',
441+
request_body=serializers.EngagementCheckListSerializer,
442+
responses={status.HTTP_201_CREATED: serializers.EngagementCheckListSerializer}
443+
)
444+
@action(detail=True, methods=["get", "post"])
445+
def complete_checklist(self, request, pk=None):
446+
from dojo.api_v2.prefetch.prefetcher import _Prefetcher
447+
engagement = self.get_object()
448+
check_lists = Check_List.objects.filter(engagement=engagement)
449+
if request.method == 'POST':
450+
if check_lists.count() > 0:
451+
return Response({"message": "A completed checklist for this engagement already exists."}, status=status.HTTP_400_BAD_REQUEST)
452+
check_list = serializers.EngagementCheckListSerializer(data=request.data)
453+
if not check_list.is_valid():
454+
return Response(check_list.errors, status=status.HTTP_400_BAD_REQUEST)
455+
check_list = Check_List(**check_list.data)
456+
check_list.engagement = engagement
457+
check_list.save()
458+
serialized_check_list = serializers.EngagementCheckListSerializer(check_list)
459+
return Response(serialized_check_list.data, status=status.HTTP_201_CREATED)
460+
prefetch_params = request.GET.get("prefetch", "").split(",")
461+
prefetcher = _Prefetcher()
462+
entry = check_lists.first()
463+
# Get the queried object representation
464+
result = serializers.EngagementCheckListSerializer(entry).data
465+
prefetcher._prefetch(entry, prefetch_params)
466+
result["prefetch"] = prefetcher.prefetched_data
467+
return Response(result, status=status.HTTP_200_OK)
468+
432469
@extend_schema(
433470
methods=['GET'],
434471
responses={
@@ -482,6 +519,35 @@ def get_queryset(self):
482519
'owner',
483520
'accepted_findings').distinct()
484521

522+
@extend_schema(
523+
methods=['GET'],
524+
responses={
525+
status.HTTP_200_OK: serializers.RiskAcceptanceProofSerializer,
526+
}
527+
)
528+
@swagger_auto_schema(
529+
method='get',
530+
responses={
531+
status.HTTP_200_OK: serializers.RiskAcceptanceProofSerializer,
532+
}
533+
)
534+
@action(detail=True, methods=["get"])
535+
def download_proof(self, request, pk=None):
536+
risk_acceptance = self.get_object()
537+
# Get the file object
538+
file_object = risk_acceptance.path
539+
if file_object is None:
540+
return Response({"error": "Proof has not provided to this risk acceptance..."}, status=status.HTTP_404_NOT_FOUND)
541+
# Get the path of the file in media root
542+
file_path = f'{settings.MEDIA_ROOT}/{file_object.name}'
543+
file_handle = open(file_path, "rb")
544+
# send file
545+
response = FileResponse(file_handle, content_type=f'{mimetypes.guess_type(file_path)}', status=status.HTTP_200_OK)
546+
response['Content-Length'] = file_object.size
547+
response['Content-Disposition'] = f'attachment; filename="{risk_acceptance.filename()}"'
548+
549+
return response
550+
485551

486552
# These are technologies in the UI and the API!
487553
# Authorization: object-based
@@ -2770,6 +2836,25 @@ def get_queryset(self):
27702836
return get_authorized_engagement_presets(Permissions.Product_View)
27712837

27722838

2839+
class EngagementCheckListViewset(prefetch.PrefetchListMixin,
2840+
prefetch.PrefetchRetrieveMixin,
2841+
mixins.ListModelMixin,
2842+
mixins.RetrieveModelMixin,
2843+
mixins.UpdateModelMixin,
2844+
mixins.DestroyModelMixin,
2845+
mixins.CreateModelMixin,
2846+
viewsets.GenericViewSet,
2847+
dojo_mixins.DeletePreviewModelMixin):
2848+
serializer_class = serializers.EngagementCheckListSerializer
2849+
queryset = Check_List.objects.none()
2850+
filter_backends = (DjangoFilterBackend,)
2851+
swagger_schema = prefetch.get_prefetch_schema(["engagement_checklists_list", "engagement_checklists_read"], serializers.EngagementCheckListSerializer).to_schema()
2852+
permission_classes = (IsAuthenticated, permissions.UserHasEngagementPermission)
2853+
2854+
def get_queryset(self):
2855+
return get_authorized_engagement_checklists(Permissions.Product_View)
2856+
2857+
27732858
class NetworkLocationsViewset(mixins.ListModelMixin,
27742859
mixins.RetrieveModelMixin,
27752860
mixins.UpdateModelMixin,
@@ -2811,7 +2896,7 @@ class QuestionnaireQuestionViewSet(mixins.ListModelMixin,
28112896
mixins.RetrieveModelMixin,
28122897
viewsets.GenericViewSet,
28132898
dojo_mixins.QuestionSubClassFieldsMixin):
2814-
serializer_class = serializers.QuestionSerializer
2899+
serializer_class = serializers.QuestionnaireQuestionSerializer
28152900
queryset = Question.objects.all()
28162901
filter_backends = (DjangoFilterBackend,)
28172902
permission_classes = (permissions.UserHasEngagementPermission, DjangoModelPermissions)

dojo/forms.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3037,8 +3037,7 @@ def __init__(self, *args, **kwargs):
30373037
# we have ChoiceAnswer instance
30383038
if choice_answer:
30393039
choice_answer = choice_answer[0]
3040-
initial_choices = choice_answer.answer.all().values_list('id',
3041-
flat=True)
3040+
initial_choices = list(choice_answer.answer.all().values_list('id', flat=True))
30423041
if self.question.multichoice is False:
30433042
initial_choices = initial_choices[0]
30443043

dojo/reports/views.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -564,9 +564,8 @@ def generate_report(request, obj, host_view=False):
564564
'title': report_title,
565565
'host': report_url_resolver(request),
566566
'user_id': request.user.id}
567-
elif type(obj).__name__ == "QuerySet" or type(obj).__name__ == "CastTaggedQuerySet":
568-
findings = ReportFindingFilter(request.GET,
569-
queryset=prefetch_related_findings_for_report(obj).distinct())
567+
elif type(obj).__name__ in ["QuerySet", "CastTaggedQuerySet", "TagulousCastTaggedQuerySet"]:
568+
findings = ReportFindingFilter(request.GET, queryset=prefetch_related_findings_for_report(obj).distinct())
570569
report_name = 'Finding'
571570
report_type = 'Finding'
572571
template = 'dojo/finding_pdf_report.html'

dojo/settings/settings.dist.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@
251251
DD_DELETE_PREVIEW=(bool, True),
252252
# List of acceptable file types that can be uploaded to a given object via arbitrary file upload
253253
DD_FILE_UPLOAD_TYPES=(list, ['.txt', '.pdf', '.json', '.xml', '.csv', '.yml', '.png', '.jpeg',
254-
'.html', '.sarif', '.xslx', '.doc', '.html', '.js', '.nessus', '.zip']),
254+
'.sarif', '.xslx', '.doc', '.html', '.js', '.nessus', '.zip']),
255255
# Max file size for scan added via API in MB
256256
DD_SCAN_FILE_MAX_SIZE=(int, 100),
257257
# When disabled, existing user tokens will not be removed but it will not be
@@ -1450,9 +1450,9 @@ def saml2_attrib_map_format(dict):
14501450
if env('DD_JIRA_EXTRA_ISSUE_TYPES') != '':
14511451
if env('DD_JIRA_EXTRA_ISSUE_TYPES').count(',') > 0:
14521452
for extra_type in env('DD_JIRA_EXTRA_ISSUE_TYPES').split(','):
1453-
JIRA_ISSUE_TYPE_CHOICES_CONFIG += (extra_type, extra_type)
1453+
JIRA_ISSUE_TYPE_CHOICES_CONFIG += (extra_type, extra_type),
14541454
else:
1455-
JIRA_ISSUE_TYPE_CHOICES_CONFIG += (env('DD_JIRA_EXTRA_ISSUE_TYPES'), env('DD_JIRA_EXTRA_ISSUE_TYPES'))
1455+
JIRA_ISSUE_TYPE_CHOICES_CONFIG += (env('DD_JIRA_EXTRA_ISSUE_TYPES'), env('DD_JIRA_EXTRA_ISSUE_TYPES')),
14561456

14571457
JIRA_SSL_VERIFY = env('DD_JIRA_SSL_VERIFY')
14581458

dojo/templates/base.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
<!-- jQuery -->
2828
<script src="{% static "jquery/dist/jquery.js" %}"></script>
2929
<!-- jQuery UI -->
30-
<script src="{% static "components-jqueryui/jquery-ui.min.js" %}"></script>
30+
<script src="{% static "jquery-ui/dist/jquery-ui.min.js" %}"></script>
3131
<!-- Bootstrap Core JavaScript -->
3232
<script src="{% static "bootstrap/dist/js/bootstrap.min.js" %}"></script>
3333
<!-- Bootstrap Select -->
@@ -80,7 +80,7 @@
8080

8181
{% block add_css %}
8282
{% endblock %}
83-
<link rel="stylesheet" href="{% static "components-jqueryui/themes/flick/jquery-ui.min.css" %}">
83+
<link rel="stylesheet" href="{% static "jquery-ui/dist/themes/flick/jquery-ui.min.css" %}">
8484
<link rel="shortcut icon" href="{% static "dojo/img/favicon.ico" %}"/>
8585
<link rel="stylesheet" href="{% static "chosen-bootstrap/chosen.bootstrap.min.css" %}">
8686
<link rel="stylesheet" href="{% static "fullcalendar/dist/fullcalendar.min.css" %}">

0 commit comments

Comments
 (0)