Skip to content

Commit

Permalink
Merge branch 'dev' into master-into-dev/2.40.2-2.41.0-dev
Browse files Browse the repository at this point in the history
  • Loading branch information
rossops authored Nov 18, 2024
2 parents 892fc39 + 73f0667 commit 0e00290
Show file tree
Hide file tree
Showing 103 changed files with 441,265 additions and 782 deletions.
Empty file removed components/node_modules/.gitkeep
Empty file.
2 changes: 1 addition & 1 deletion components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"metismenu": "~3.0.7",
"moment": "^2.30.1",
"morris.js": "morrisjs/morris.js",
"pdfmake": "^0.2.14",
"pdfmake": "^0.2.15",
"startbootstrap-sb-admin-2": "1.0.7"
},
"engines": {
Expand Down
532 changes: 33 additions & 499 deletions components/yarn.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ services:
source: ./docker/extra_settings
target: /app/docker/extra_settings
postgres:
image: postgres:17.0-alpine@sha256:14195b0729fce792f47ae3c3704d6fd04305826d57af3b01d5b4d004667df174
image: postgres:17.1-alpine@sha256:0d9624535618a135c5453258fd629f4963390338b11aaffb92292c12df3a6c17
environment:
POSTGRES_DB: ${DD_DATABASE_NAME:-defectdojo}
POSTGRES_USER: ${DD_DATABASE_USER:-defectdojo}
Expand Down
7 changes: 7 additions & 0 deletions docs/content/en/getting_started/upgrading/2.41.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: 'Upgrading to DefectDojo Version 2.41.x'
toc_hide: true
weight: -20241104
description: No special instructions.
---
There are no special instructions for upgrading to 2.41.x. Check the [Release Notes](https://github.com/DefectDojo/django-DefectDojo/releases/tag/2.41.0) for the contents of the release.
8 changes: 8 additions & 0 deletions docs/content/en/integrations/parsers/file/mobsf_scorecard.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
title: "MobSF Scorecard Scanner"
toc_hide: true
---
Export a JSON file using the API, api/v1/report_json.

### Sample Scan Data
Sample MobSF Scorecard Scanner scans can be found [here](https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/mobsf_scorecard).
30 changes: 15 additions & 15 deletions docs/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"devDependencies": {
"postcss": "8.4.47",
"postcss": "8.4.49",
"autoprefixer": "10.4.20",
"postcss-cli": "11.0.0"
}
Expand Down
3 changes: 3 additions & 0 deletions dojo/api_v2/prefetch/prefetcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def _build_serializers():
Returns:
dict[model, serializer]: map of model to their serializer
"""

def _is_model_serializer(obj):
Expand Down Expand Up @@ -61,6 +62,7 @@ def _find_serializer(self, field_type):
Returns:
rest_framework.serializers.ModelSerializer: The serializer if one has been found or None
"""
# If the type is represented in the map then return the serializer
if field_type in self._serializers:
Expand All @@ -80,6 +82,7 @@ def _prefetch(self, entry, fields_to_fetch):
Args:
entry (ModelInstance): Instance of a model as returned by a django queryset
field_to_fetch (list[string]): fields to prefetch
"""
for field_to_fetch in fields_to_fetch:
# Get the field from the instance
Expand Down
3 changes: 3 additions & 0 deletions dojo/api_v2/prefetch/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ def _is_many_to_many_relation(field):
Returns:
bool: true if the field is a many-to-many relationship
"""
return isinstance(field, related.ManyToManyDescriptor)

Expand All @@ -27,6 +28,7 @@ def _is_one_to_one_relation(field):
Returns:
bool: true if the field is a one-to-one relationship
"""
return isinstance(field, related.ForwardManyToOneDescriptor)

Expand All @@ -38,6 +40,7 @@ def _get_prefetchable_fields(serializer):
Args:
serializer (Serializer): [description]
"""

def _is_field_prefetchable(field):
Expand Down
55 changes: 53 additions & 2 deletions dojo/api_v2/serializers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import collections
import json
import logging
import re
Expand Down Expand Up @@ -280,10 +281,10 @@ def _pop_tags(self, validated_data):
return (to_be_tagged, validated_data)


class RequestResponseDict(list):
class RequestResponseDict(collections.UserList):
def __init__(self, *args, **kwargs):
pretty_print = kwargs.pop("pretty_print", True)
list.__init__(self, *args, **kwargs)
collections.UserList.__init__(self, *args, **kwargs)
self.pretty_print = pretty_print

def __add__(self, rhs):
Expand Down Expand Up @@ -416,6 +417,51 @@ class Meta:
fields = "__all__"


class MetadataSerializer(serializers.Serializer):
name = serializers.CharField(max_length=120)
value = serializers.CharField(max_length=300)


class MetaMainSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)

product = serializers.PrimaryKeyRelatedField(
queryset=Product.objects.all(),
required=False,
default=None,
allow_null=True,
)
endpoint = serializers.PrimaryKeyRelatedField(
queryset=Endpoint.objects.all(),
required=False,
default=None,
allow_null=True,
)
finding = serializers.PrimaryKeyRelatedField(
queryset=Finding.objects.all(),
required=False,
default=None,
allow_null=True,
)
metadata = MetadataSerializer(many=True)

def validate(self, data):
product_id = data.get("product", None)
endpoint_id = data.get("endpoint", None)
finding_id = data.get("finding", None)
metadata = data.get("metadata")

for item in metadata:
# this will only verify that one and only one of product, endpoint, or finding is passed...
DojoMeta(product=product_id,
endpoint=endpoint_id,
finding=finding_id,
name=item.get("name"),
value=item.get("value")).clean()

return data


class ProductMetaSerializer(serializers.ModelSerializer):
class Meta:
model = DojoMeta
Expand Down Expand Up @@ -2697,6 +2743,11 @@ class ReportGenerateSerializer(serializers.Serializer):
)


class EngagementUpdateJiraEpicSerializer(serializers.Serializer):
epic_name = serializers.CharField(required=False, max_length=200)
epic_priority = serializers.CharField(required=False, allow_null=True)


class TagSerializer(serializers.Serializer):
tags = TagListSerializerField(required=True)

Expand Down
108 changes: 108 additions & 0 deletions dojo/api_v2/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,36 @@ def download_file(self, request, file_id, pk=None):
# send file
return generate_file_response(file_object)

@extend_schema(
request=serializers.EngagementUpdateJiraEpicSerializer,
responses={status.HTTP_200_OK: serializers.EngagementUpdateJiraEpicSerializer},
)
@action(
detail=True, methods=["post"], permission_classes=[IsAuthenticated],
)
def update_jira_epic(self, request, pk=None):
engagement = self.get_object()
try:

if engagement.has_jira_issue:
jira_helper.update_epic(engagement, **request.data)
response = Response(
{"info": "Jira Epic update query sent"},
status=status.HTTP_200_OK,
)
else:
jira_helper.add_epic(engagement, **request.data)
response = Response(
{"info": "Jira Epic create query sent"},
status=status.HTTP_200_OK,
)
return response
except ValidationError:
return Response(
{"error": "Bad Request!"},
status=status.HTTP_400_BAD_REQUEST,
)


# @extend_schema_view(**schema_with_prefetch())
# Nested models with prefetch make the response schema too long for Swagger UI
Expand Down Expand Up @@ -1620,6 +1650,61 @@ class DojoMetaViewSet(
def get_queryset(self):
return get_authorized_dojo_meta(Permissions.Product_View)

@extend_schema(
methods=["post", "patch"],
request=serializers.MetaMainSerializer,
responses={status.HTTP_200_OK: serializers.MetaMainSerializer},
filters=False,
)
@action(
detail=False, methods=["post", "patch"], pagination_class=None,
)
def batch(self, request, pk=None):
serialized_data = serializers.MetaMainSerializer(data=request.data)
if serialized_data.is_valid(raise_exception=True):
if request.method == "POST":
self.process_post(request.data)
if request.method == "PATCH":
self.process_patch(request.data)

return Response(status=status.HTTP_201_CREATED, data=serialized_data.data)

def process_post(self: object, data: dict):
product = Product.objects.filter(id=data.get("product")).first()
finding = Finding.objects.filter(id=data.get("finding")).first()
endpoint = Endpoint.objects.filter(id=data.get("endpoint")).first()
metalist = data.get("metadata")
for metadata in metalist:
try:
DojoMeta.objects.create(
product=product,
finding=finding,
endpoint=endpoint,
name=metadata.get("name"),
value=metadata.get("value"),
)
except (IntegrityError) as ex: # this should not happen as the data was validated in the batch call
raise ValidationError(str(ex))

def process_patch(self: object, data: dict):
product = Product.objects.filter(id=data.get("product")).first()
finding = Finding.objects.filter(id=data.get("finding")).first()
endpoint = Endpoint.objects.filter(id=data.get("endpoint")).first()
metalist = data.get("metadata")
for metadata in metalist:
dojometa = DojoMeta.objects.filter(product=product, finding=finding, endpoint=endpoint, name=metadata.get("name"))
if dojometa:
try:
dojometa.update(
name=metadata.get("name"),
value=metadata.get("value"),
)
except (IntegrityError) as ex:
raise ValidationError(str(ex))
else:
msg = f"Metadata {metadata.get('name')} not found for object."
raise ValidationError(msg)


@extend_schema_view(**schema_with_prefetch())
class ProductViewSet(
Expand Down Expand Up @@ -3057,6 +3142,29 @@ class QuestionnaireEngagementSurveyViewSet(
def get_queryset(self):
return Engagement_Survey.objects.all().order_by("id")

@extend_schema(
request=OpenApiTypes.NONE,
parameters=[
OpenApiParameter(
"engagement_id", OpenApiTypes.INT, OpenApiParameter.PATH,
),
],
responses={status.HTTP_200_OK: serializers.QuestionnaireAnsweredSurveySerializer},
)
@action(
detail=True, methods=["post"], url_path=r"link_engagement/(?P<engagement_id>\d+)",
)
def link_engagement(self, request, pk, engagement_id):
# Get the answered survey
engagement_survey = self.get_object()
# Safely get the engagement
engagement = get_object_or_404(Engagement.objects, pk=engagement_id)
# Link the engagement
answered_survey, _ = Answered_Survey.objects.get_or_create(engagement=engagement, survey=engagement_survey)
# Send a favorable response
serialized_answered_survey = serializers.QuestionnaireAnsweredSurveySerializer(answered_survey)
return Response(serialized_answered_survey.data)


@extend_schema_view(**schema_with_prefetch())
class QuestionnaireAnsweredSurveyViewSet(
Expand Down
Loading

0 comments on commit 0e00290

Please sign in to comment.