From d2d2ced09029d373ec8737231bfb4d32af921e03 Mon Sep 17 00:00:00 2001 From: DefectDojo release bot Date: Mon, 1 Jul 2024 15:39:46 +0000 Subject: [PATCH 01/11] Update versions in application files --- components/package.json | 2 +- dojo/__init__.py | 2 +- helm/defectdojo/Chart.yaml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/package.json b/components/package.json index ae18f7927c5..ab3201e6a41 100644 --- a/components/package.json +++ b/components/package.json @@ -1,6 +1,6 @@ { "name": "defectdojo", - "version": "2.36.0", + "version": "2.37.0-dev", "license" : "BSD-3-Clause", "private": true, "dependencies": { diff --git a/dojo/__init__.py b/dojo/__init__.py index b6ae12017f9..707177ee3ee 100644 --- a/dojo/__init__.py +++ b/dojo/__init__.py @@ -4,6 +4,6 @@ # Django starts so that shared_task will use this app. from .celery import app as celery_app # noqa: F401 -__version__ = '2.36.0' +__version__ = '2.37.0-dev' __url__ = 'https://github.com/DefectDojo/django-DefectDojo' __docs__ = 'https://documentation.defectdojo.com' diff --git a/helm/defectdojo/Chart.yaml b/helm/defectdojo/Chart.yaml index 3ede21350c9..ab21a0409cb 100644 --- a/helm/defectdojo/Chart.yaml +++ b/helm/defectdojo/Chart.yaml @@ -1,8 +1,8 @@ apiVersion: v2 -appVersion: "2.36.0" +appVersion: "2.37.0-dev" description: A Helm chart for Kubernetes to install DefectDojo name: defectdojo -version: 1.6.137 +version: 1.6.138-dev icon: https://www.defectdojo.org/img/favicon.ico maintainers: - name: madchap From 8d631e32e0aa83e5ceea4fbcea3e2aa929ddcb41 Mon Sep 17 00:00:00 2001 From: kiblik <5609770+kiblik@users.noreply.github.com> Date: Wed, 3 Jul 2024 05:46:09 +0200 Subject: [PATCH 02/11] fix(helm-celery): Drop unused variable logLevel (#10468) --- helm/defectdojo/values.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/helm/defectdojo/values.yaml b/helm/defectdojo/values.yaml index 1faba1520b3..ff7537ef14b 100644 --- a/helm/defectdojo/values.yaml +++ b/helm/defectdojo/values.yaml @@ -131,7 +131,6 @@ celery: worker: annotations: {} affinity: {} - logLevel: INFO nodeSelector: {} replicas: 1 resources: From c7cc407b5c2f33a48ddc01d241b3ba8c6b4b8533 Mon Sep 17 00:00:00 2001 From: CC <1499184+ccronca@users.noreply.github.com> Date: Wed, 3 Jul 2024 17:24:31 +0200 Subject: [PATCH 03/11] Mark Finding properties related_fields, jira_creation and jira_change as nullable (#10371) Co-authored-by: Camilo Cota --- dojo/api_v2/serializers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dojo/api_v2/serializers.py b/dojo/api_v2/serializers.py index 4de5d536d07..1bbac7cd6cd 100644 --- a/dojo/api_v2/serializers.py +++ b/dojo/api_v2/serializers.py @@ -1645,10 +1645,10 @@ class FindingSerializer(TaggitSerializer, serializers.ModelSerializer): age = serializers.IntegerField(read_only=True) sla_days_remaining = serializers.IntegerField(read_only=True) finding_meta = FindingMetaSerializer(read_only=True, many=True) - related_fields = serializers.SerializerMethodField() + related_fields = serializers.SerializerMethodField(allow_null=True) # for backwards compatibility - jira_creation = serializers.SerializerMethodField(read_only=True) - jira_change = serializers.SerializerMethodField(read_only=True) + jira_creation = serializers.SerializerMethodField(read_only=True, allow_null=True) + jira_change = serializers.SerializerMethodField(read_only=True, allow_null=True) display_status = serializers.SerializerMethodField() finding_groups = FindingGroupSerializer( source="finding_group_set", many=True, read_only=True From 9446f08a7938ed3381ef84865b8f6e6ddf42d413 Mon Sep 17 00:00:00 2001 From: kiblik <5609770+kiblik@users.noreply.github.com> Date: Mon, 8 Jul 2024 03:56:44 +0200 Subject: [PATCH 04/11] fix(helm-psql): Drop pinning of old version of postgresql (#10507) --- helm/defectdojo/values.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/helm/defectdojo/values.yaml b/helm/defectdojo/values.yaml index ff7537ef14b..f0d9bc130bd 100644 --- a/helm/defectdojo/values.yaml +++ b/helm/defectdojo/values.yaml @@ -367,8 +367,6 @@ mysql: postgresql: enabled: true - image: - tag: 11.22.0-debian-11-r4 auth: username: defectdojo password: "" From 14f2b28dcba9207fbd274715b1b30c5641e880ce Mon Sep 17 00:00:00 2001 From: kiblik <5609770+kiblik@users.noreply.github.com> Date: Mon, 8 Jul 2024 03:57:24 +0200 Subject: [PATCH 05/11] fix(doc): Disable markup.highlight.guessSyntax + enable mermaid (#10509) * enable mermaid, disable guessSyntax * Set languages --- docs/config.dev.toml | 6 +++++- docs/config.master.toml | 6 +++++- docs/content/en/integrations/ldap-authentication.md | 4 ++-- docs/content/en/integrations/parsers/file/fortify.md | 2 +- docs/content/en/integrations/parsers/file/veracode.md | 4 ++-- docs/content/en/integrations/social-authentication.md | 4 ++-- docs/content/en/usage/productgrading.md | 2 +- 7 files changed, 18 insertions(+), 10 deletions(-) diff --git a/docs/config.dev.toml b/docs/config.dev.toml index 23b9cf30c52..65fff4564ba 100644 --- a/docs/config.dev.toml +++ b/docs/config.dev.toml @@ -85,7 +85,8 @@ weight = 1 # See a complete list of available styles at https://xyproto.github.io/splash/docs/all.html style = "dracula" # Uncomment if you want your chosen highlight style used for code blocks without a specified language - guessSyntax = "true" + # Do not uncomment otherwise it breaks mermaid + # guessSyntax = "true" # Everything below this are Site Params @@ -198,3 +199,6 @@ enable = false url = "https://owasp.slack.com/archives/C014H3ZV9U6" icon = "fab fa-slack" desc = "Chat with other project developers" + +[params.mermaid] +enable = true diff --git a/docs/config.master.toml b/docs/config.master.toml index 5771eb8367b..29c4e0a6adc 100644 --- a/docs/config.master.toml +++ b/docs/config.master.toml @@ -85,7 +85,8 @@ weight = 1 # See a complete list of available styles at https://xyproto.github.io/splash/docs/all.html style = "dracula" # Uncomment if you want your chosen highlight style used for code blocks without a specified language - guessSyntax = "true" + # Do not uncomment otherwise it breaks mermaid + # guessSyntax = "true" # Everything below this are Site Params @@ -198,3 +199,6 @@ enable = false url = "https://owasp.slack.com/archives/C014H3ZV9U6" icon = "fab fa-slack" desc = "Chat with other project developers" + +[params.mermaid] +enable = true diff --git a/docs/content/en/integrations/ldap-authentication.md b/docs/content/en/integrations/ldap-authentication.md index 2fcf895e12b..17697043736 100644 --- a/docs/content/en/integrations/ldap-authentication.md +++ b/docs/content/en/integrations/ldap-authentication.md @@ -41,7 +41,7 @@ Please check for the latest version of these requirements at the time of impleme Otherwise add the following to requirements.txt: -``` +```python python-ldap==3.4.2 django-auth-ldap==4.1.0 ``` @@ -119,7 +119,7 @@ Read the docs for Django Authentication with LDAP here: https://django-auth-ldap In order to pass the variables to the settings.dist.py file via docker, it's a good idea to add these to the docker-compose file. You can do this by adding the following variables to the environment section for the uwsgi image: -``` +```yaml DD_LDAP_SERVER_URI: "${DD_LDAP_SERVER_URI:-ldap://ldap.example.com}" DD_LDAP_BIND_DN: "${DD_LDAP_BIND_DN:-}" DD_LDAP_BIND_PASSWORD: "${DD_LDAP_BIND_PASSWORD:-}" diff --git a/docs/content/en/integrations/parsers/file/fortify.md b/docs/content/en/integrations/parsers/file/fortify.md index 5c113c36cb8..2897e39d7c2 100644 --- a/docs/content/en/integrations/parsers/file/fortify.md +++ b/docs/content/en/integrations/parsers/file/fortify.md @@ -20,6 +20,6 @@ per category. To get all issues, copy the [DefaultReportDefinitionAllIssues.xml] Once this is complete, you can run the following command on your .fpr file to generate the required XML: -``` +```bash ./path/to/ReportGenerator -format xml -f /path/to/output.xml -source /path/to/downloaded/artifact.fpr -template DefaultReportDefinitionAllIssues.xml ``` \ No newline at end of file diff --git a/docs/content/en/integrations/parsers/file/veracode.md b/docs/content/en/integrations/parsers/file/veracode.md index 77237860413..431a7f54cad 100644 --- a/docs/content/en/integrations/parsers/file/veracode.md +++ b/docs/content/en/integrations/parsers/file/veracode.md @@ -14,7 +14,7 @@ Veracode reports can be ingested in either XML or JSON Format - Requires slight modification of the response returned from the API - Exmample of a request being: `url | jq "{findings}"` - Desired Format: - ``` + ```json { "findings": [ { @@ -28,7 +28,7 @@ Veracode reports can be ingested in either XML or JSON Format - This response can be saved directly to a file and uploaded - Not as ideal for crafting a refined report consisting of multiple requests - Desired Format: - ``` + ```json { "_embedded": { "findings": [ diff --git a/docs/content/en/integrations/social-authentication.md b/docs/content/en/integrations/social-authentication.md index a7cafe38067..ebf2a6b0c84 100644 --- a/docs/content/en/integrations/social-authentication.md +++ b/docs/content/en/integrations/social-authentication.md @@ -312,7 +312,7 @@ Edit the settings (see [Configuration]({{< ref "/getting_started/configuration" or, alternatively, for helm configuration, add this to the `extraConfig` section: -``` +```yaml DD_SESSION_COOKIE_SECURE: 'True' DD_CSRF_COOKIE_SECURE: 'True' DD_SECURE_SSL_REDIRECT: 'True' @@ -453,7 +453,7 @@ Some Identity Providers are able to send list of groups to which should user bel You can bypass the login form if you are only using SSO/Social authentication for login in by enabling these two environment variables: -``` +```yaml DD_SOCIAL_LOGIN_AUTO_REDIRECT: "true" DD_SOCIAL_AUTH_SHOW_LOGIN_FORM: "false" ``` diff --git a/docs/content/en/usage/productgrading.md b/docs/content/en/usage/productgrading.md index 88cb88267fa..3ead06d24f4 100644 --- a/docs/content/en/usage/productgrading.md +++ b/docs/content/en/usage/productgrading.md @@ -27,7 +27,7 @@ Note that the following abbreviations were used: - med: amount of medium findings within the product - low: amount of low findings within the product -``` +```python health=100 if crit > 0: health = 40 From e9a784fb9d98e88937db609f7334656348d9cefd Mon Sep 17 00:00:00 2001 From: kiblik <5609770+kiblik@users.noreply.github.com> Date: Mon, 8 Jul 2024 14:32:59 +0200 Subject: [PATCH 06/11] fix(docker-compose): Remove 'version' from docker-compose.override.unit_tests (#10519) --- docker-compose.override.unit_tests.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/docker-compose.override.unit_tests.yml b/docker-compose.override.unit_tests.yml index a3d15b93501..a492dbdaad7 100644 --- a/docker-compose.override.unit_tests.yml +++ b/docker-compose.override.unit_tests.yml @@ -1,5 +1,4 @@ --- -version: '3.8' services: nginx: image: busybox:1.36.1-musl From 3e096e12d83af0b2f20349df506c83e774d6c305 Mon Sep 17 00:00:00 2001 From: Charles Neill <1749665+cneill@users.noreply.github.com> Date: Mon, 8 Jul 2024 07:33:31 -0500 Subject: [PATCH 07/11] Direct Renovate to ignore MySQL and RabbitMQ packages (#10512) --- .github/renovate.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/renovate.json b/.github/renovate.json index 7c9c6623cd6..7acaf0f30bc 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -6,6 +6,10 @@ "dependencyDashboardApproval": false, "baseBranches": ["dev"], "rebaseWhen": "conflicted", + "ignoreDeps": [ + "mysql", + "rabbitmq" + ], "ignorePaths": ["requirements.txt", "components/package.json", "components/package-lock.json", "dojo/components/yarn.lock", "dojo/components/package.json", "Dockerfile**"], "packageRules": [{ "packagePatterns": ["*"], From cf864b1087baee40d93bb7f177cc3dace7b55983 Mon Sep 17 00:00:00 2001 From: Cody Maffucci <46459665+Maffooch@users.noreply.github.com> Date: Mon, 8 Jul 2024 07:34:07 -0500 Subject: [PATCH 08/11] Rest Framework Tests: Improve speed and repeatability (#10503) --- docker-compose.override.unit_tests_cicd.yml | 1 + docker/entrypoint-unit-tests-devDocker.sh | 3 +- docker/entrypoint-unit-tests.sh | 4 +- dojo/api_v2/views.py | 163 ++++++++++++------ dojo/cred/queries.py | 2 +- dojo/endpoint/queries.py | 4 +- dojo/engagement/queries.py | 6 +- dojo/finding/queries.py | 8 +- dojo/group/queries.py | 4 +- dojo/jira_link/queries.py | 4 +- dojo/product/queries.py | 40 ++--- dojo/product_type/queries.py | 10 +- dojo/risk_acceptance/queries.py | 6 +- dojo/test/queries.py | 10 +- dojo/tool_product/queries.py | 6 +- dojo/urls.py | 110 ++++++------ unittests/dojo_test_case.py | 18 +- unittests/test_apiv2_notifications.py | 23 ++- unittests/test_apiv2_scan_import_options.py | 2 +- unittests/test_import_reimport.py | 2 +- unittests/test_importers_importer.py | 2 +- unittests/test_jira_config_engagement.py | 29 ++-- unittests/test_rest_framework.py | 16 +- .../test_deepfence_threatmapper_parser.py | 56 +++--- 24 files changed, 308 insertions(+), 221 deletions(-) diff --git a/docker-compose.override.unit_tests_cicd.yml b/docker-compose.override.unit_tests_cicd.yml index cc677ac41ef..10dd2e0b6b0 100644 --- a/docker-compose.override.unit_tests_cicd.yml +++ b/docker-compose.override.unit_tests_cicd.yml @@ -15,6 +15,7 @@ services: environment: PYTHONWARNINGS: error # We are strict about Warnings during testing DD_DEBUG: 'True' + DD_LOG_LEVEL: 'ERROR' DD_TEST_DATABASE_NAME: ${DD_TEST_DATABASE_NAME} DD_DATABASE_NAME: ${DD_TEST_DATABASE_NAME} DD_DATABASE_ENGINE: ${DD_DATABASE_ENGINE} diff --git a/docker/entrypoint-unit-tests-devDocker.sh b/docker/entrypoint-unit-tests-devDocker.sh index 6872d8668fc..2e6523930e1 100755 --- a/docker/entrypoint-unit-tests-devDocker.sh +++ b/docker/entrypoint-unit-tests-devDocker.sh @@ -53,7 +53,8 @@ EOF echo "Unit Tests" echo "------------------------------------------------------------" -python3 manage.py test unittests -v 3 --keepdb --no-input --shuffle +python3 manage.py test unittests -v 3 --keepdb --no-input --failfast --shuffle --parallel --exclude-tag="non-parallel" +python3 manage.py test unittests -v 3 --keepdb --no-input --failfast --shuffle --tag="non-parallel" # you can select a single file to "test" unit tests # python3 manage.py test unittests.tools.test_npm_audit_scan_parser.TestNpmAuditParser --keepdb -v 3 diff --git a/docker/entrypoint-unit-tests.sh b/docker/entrypoint-unit-tests.sh index a356283c377..5be85c8df8a 100755 --- a/docker/entrypoint-unit-tests.sh +++ b/docker/entrypoint-unit-tests.sh @@ -79,4 +79,6 @@ python3 manage.py migrate echo "Unit Tests" echo "------------------------------------------------------------" -python3 manage.py test unittests -v 3 --keepdb --no-input --shuffle +python3 manage.py test unittests -v 3 --keepdb --no-input --failfast --shuffle --parallel --exclude-tag="non-parallel" +python3 manage.py test unittests -v 3 --keepdb --no-input --failfast --shuffle --tag="non-parallel" + diff --git a/dojo/api_v2/views.py b/dojo/api_v2/views.py index d0fe775b070..b106e664e9f 100644 --- a/dojo/api_v2/views.py +++ b/dojo/api_v2/views.py @@ -200,11 +200,14 @@ class PrefetchDojoModelViewSet( # Authorization: authenticated users class RoleViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = serializers.RoleSerializer - queryset = Role.objects.all() + queryset = Role.objects.none() filter_backends = (DjangoFilterBackend,) filterset_fields = ["id", "name"] permission_classes = (IsAuthenticated,) + def filter_queryset(self, queryset): + return Role.objects.all().order_by('id') + # Authorization: object-based @extend_schema_view( @@ -306,6 +309,9 @@ class GlobalRoleViewSet( filterset_fields = ["id", "user", "group", "role"] permission_classes = (permissions.IsSuperUser, DjangoModelPermissions) + def filter_queryset(self, queryset): + return Global_Role.objects.all().order_by("id") + # Authorization: object-based class EndPointViewSet( @@ -746,9 +752,11 @@ class CredentialsViewSet( serializer_class = serializers.CredentialSerializer queryset = Cred_User.objects.all() filter_backends = (DjangoFilterBackend,) - permission_classes = (permissions.IsSuperUser, DjangoModelPermissions) + def get_queryset(self): + return Cred_User.objects.all().order_by("id") + # Authorization: configuration class CredentialsMappingViewSet( @@ -773,11 +781,14 @@ class FindingTemplatesViewSet( DojoModelViewSet, ): serializer_class = serializers.FindingTemplateSerializer - queryset = Finding_Template.objects.all() + queryset = Finding_Template.objects.none() filter_backends = (DjangoFilterBackend,) filterset_class = ApiTemplateFindingFilter permission_classes = (permissions.UserHasConfigurationPermissionStaff,) + def get_queryset(self): + return Finding_Template.objects.all().order_by("id") + # Authorization: object-based @extend_schema_view( @@ -1490,11 +1501,14 @@ class JiraInstanceViewSet( DojoModelViewSet, ): serializer_class = serializers.JIRAInstanceSerializer - queryset = JIRA_Instance.objects.all() + queryset = JIRA_Instance.objects.none() filter_backends = (DjangoFilterBackend,) filterset_fields = ["id", "url"] permission_classes = (permissions.UserHasConfigurationPermissionSuperuser,) + def filter_queryset(self, queryset): + return JIRA_Instance.objects.all().order_by("id") + # Authorization: object-based class JiraIssuesViewSet( @@ -1554,18 +1568,21 @@ class SonarqubeIssueViewSet( DojoModelViewSet, ): serializer_class = serializers.SonarqubeIssueSerializer - queryset = Sonarqube_Issue.objects.all() + queryset = Sonarqube_Issue.objects.none() filter_backends = (DjangoFilterBackend,) filterset_fields = ["id", "key", "status", "type"] permission_classes = (permissions.IsSuperUser, DjangoModelPermissions) + def filter_queryset(self, queryset): + return Sonarqube_Issue.objects.all().order_by("id") + # Authorization: superuser class SonarqubeIssueTransitionViewSet( DojoModelViewSet, ): serializer_class = serializers.SonarqubeIssueTransitionSerializer - queryset = Sonarqube_Issue_Transition.objects.all() + queryset = Sonarqube_Issue_Transition.objects.none() filter_backends = (DjangoFilterBackend,) filterset_fields = [ "id", @@ -1576,6 +1593,9 @@ class SonarqubeIssueTransitionViewSet( ] permission_classes = (permissions.IsSuperUser, DjangoModelPermissions) + def filter_queryset(self, queryset): + return Sonarqube_Issue_Transition.objects.all().order_by("id") + # Authorization: object-based class ProductAPIScanConfigurationViewSet( @@ -1646,9 +1666,6 @@ class DojoMetaViewSet( IsAuthenticated, permissions.UserHasDojoMetaPermission, ) - # swagger_schema = prefetch.get_prefetch_schema( - # ["metadata_list", "metadata_read"], serializers.MetaSerializer - # ).to_schema() def get_queryset(self): return get_authorized_dojo_meta(Permissions.Product_View) @@ -1688,10 +1705,8 @@ class ProductViewSet( dojo_mixins.DeletePreviewModelMixin, ): serializer_class = serializers.ProductSerializer - # TODO: prefetch queryset = Product.objects.none() filter_backends = (DjangoFilterBackend,) - filterset_class = ApiProductFilter permission_classes = ( IsAuthenticated, @@ -2111,10 +2126,13 @@ class DevelopmentEnvironmentViewSet( DojoModelViewSet, ): serializer_class = serializers.DevelopmentEnvironmentSerializer - queryset = Development_Environment.objects.all() + queryset = Development_Environment.objects.none() filter_backends = (DjangoFilterBackend,) permission_classes = (IsAuthenticated, DjangoModelPermissions) + def filter_queryset(self, queryset): + return Development_Environment.objects.all().order_by("id") + # Authorization: object-based class TestsViewSet( @@ -2326,13 +2344,16 @@ class TestTypesViewSet( viewsets.ReadOnlyModelViewSet, ): serializer_class = serializers.TestTypeSerializer - queryset = Test_Type.objects.all() + queryset = Test_Type.objects.none() filter_backends = (DjangoFilterBackend,) filterset_fields = [ "name", ] permission_classes = (IsAuthenticated, DjangoModelPermissions) + def filter_queryset(self, queryset): + return Test_Type.objects.all().order_by("id") + @extend_schema_view( list=extend_schema( @@ -2420,7 +2441,7 @@ class ToolConfigurationsViewSet( PrefetchDojoModelViewSet, ): serializer_class = serializers.ToolConfigurationSerializer - queryset = Tool_Configuration.objects.all() + queryset = Tool_Configuration.objects.none() filter_backends = (DjangoFilterBackend,) filterset_fields = [ "id", @@ -2431,6 +2452,9 @@ class ToolConfigurationsViewSet( ] permission_classes = (permissions.UserHasConfigurationPermissionSuperuser,) + def filter_queryset(self, queryset): + return Tool_Configuration.objects.all().order_by("id") + # Authorization: object-based class ToolProductSettingsViewSet( @@ -2461,29 +2485,35 @@ class ToolTypesViewSet( DojoModelViewSet, ): serializer_class = serializers.ToolTypeSerializer - queryset = Tool_Type.objects.all() + queryset = Tool_Type.objects.none() filter_backends = (DjangoFilterBackend,) filterset_fields = ["id", "name", "description"] permission_classes = (permissions.UserHasConfigurationPermissionSuperuser,) + def filter_queryset(self, queryset): + return Tool_Type.objects.all().order_by("id") + # Authorization: authenticated, configuration class RegulationsViewSet( DojoModelViewSet, ): serializer_class = serializers.RegulationSerializer - queryset = Regulation.objects.all() + queryset = Regulation.objects.none() filter_backends = (DjangoFilterBackend,) filterset_fields = ["id", "name", "description"] permission_classes = (IsAuthenticated, DjangoModelPermissions) + def filter_queryset(self, queryset): + return Regulation.objects.all().order_by("id") + # Authorization: configuration class UsersViewSet( DojoModelViewSet, ): serializer_class = serializers.UserSerializer - queryset = User.objects.all() + queryset = User.objects.none() filter_backends = (DjangoFilterBackend,) filterset_fields = [ "id", @@ -2496,6 +2526,9 @@ class UsersViewSet( ] permission_classes = (permissions.UserHasConfigurationPermissionSuperuser,) + def filter_queryset(self, queryset): + return User.objects.all().order_by("id") + def destroy(self, request, *args, **kwargs): instance = self.get_object() if request.user == instance: @@ -2536,11 +2569,14 @@ class UserContactInfoViewSet( PrefetchDojoModelViewSet, ): serializer_class = serializers.UserContactInfoSerializer - queryset = UserContactInfo.objects.all() + queryset = UserContactInfo.objects.none() filter_backends = (DjangoFilterBackend,) filterset_fields = "__all__" permission_classes = (permissions.IsSuperUser, DjangoModelPermissions) + def filter_queryset(self, queryset): + return UserContactInfo.objects.all().order_by("id") + # Authorization: authenticated users class UserProfileView(GenericAPIView): @@ -2655,7 +2691,7 @@ class EndpointMetaImporterView( serializer_class = serializers.EndpointMetaImporterSerializer parser_classes = [MultiPartParser] - queryset = Product.objects.all() + queryset = Product.objects.none() permission_classes = ( IsAuthenticated, permissions.UserHasMetaImportPermission, @@ -2673,11 +2709,14 @@ class LanguageTypeViewSet( DojoModelViewSet, ): serializer_class = serializers.LanguageTypeSerializer - queryset = Language_Type.objects.all() + queryset = Language_Type.objects.none() filter_backends = (DjangoFilterBackend,) filterset_fields = ["id", "language", "color"] permission_classes = (permissions.UserHasConfigurationPermissionStaff,) + def filter_queryset(self, queryset): + return Language_Type.objects.all().order_by("id") + # Authorization: object-based @extend_schema_view( @@ -2807,7 +2846,7 @@ class NoteTypeViewSet( DojoModelViewSet, ): serializer_class = serializers.NoteTypeSerializer - queryset = Note_Type.objects.all() + queryset = Note_Type.objects.none() filter_backends = (DjangoFilterBackend,) filterset_fields = [ "id", @@ -2819,6 +2858,9 @@ class NoteTypeViewSet( ] permission_classes = (permissions.UserHasConfigurationPermissionSuperuser,) + def filter_queryset(self, queryset): + return Note_Type.objects.all().order_by("id") + # Authorization: superuser class NotesViewSet( @@ -2826,7 +2868,7 @@ class NotesViewSet( viewsets.ReadOnlyModelViewSet, ): serializer_class = serializers.NoteSerializer - queryset = Notes.objects.all() + queryset = Notes.objects.none() filter_backends = (DjangoFilterBackend,) filterset_fields = [ "id", @@ -2840,6 +2882,9 @@ class NotesViewSet( ] permission_classes = (permissions.IsSuperUser, DjangoModelPermissions) + def filter_queryset(self, queryset): + return Notes.objects.all().order_by("id") + def report_generate(request, obj, options): user = Dojo_User.objects.get(id=request.user.id) @@ -3136,7 +3181,10 @@ class SystemSettingsViewSet( permission_classes = (permissions.IsSuperUser, DjangoModelPermissions) serializer_class = serializers.SystemSettingsSerializer - queryset = System_Settings.objects.all() + queryset = System_Settings.objects.none() + + def filter_queryset(self, queryset): + return System_Settings.objects.all().order_by("id") # Authorization: superuser @@ -3168,11 +3216,14 @@ class NotificationsViewSet( PrefetchDojoModelViewSet, ): serializer_class = serializers.NotificationsSerializer - queryset = Notifications.objects.all() + queryset = Notifications.objects.none() filter_backends = (DjangoFilterBackend,) filterset_fields = ["id", "user", "product", "template"] permission_classes = (permissions.IsSuperUser, DjangoModelPermissions) + def filter_queryset(self, queryset): + return Notifications.objects.all().order_by("id") + class EngagementPresetsViewset( PrefetchDojoModelViewSet, @@ -3190,102 +3241,108 @@ def get_queryset(self): return get_authorized_engagement_presets(Permissions.Product_View) -class EngagementCheckListViewset( - PrefetchDojoModelViewSet, -): - serializer_class = serializers.EngagementCheckListSerializer - queryset = Check_List.objects.none() - filter_backends = (DjangoFilterBackend,) - permission_classes = ( - IsAuthenticated, - permissions.UserHasEngagementPermission, - ) - - def get_queryset(self): - return get_authorized_engagement_checklists(Permissions.Product_View) - - class NetworkLocationsViewset( DojoModelViewSet, ): serializer_class = serializers.NetworkLocationsSerializer - queryset = Network_Locations.objects.all() + queryset = Network_Locations.objects.none() filter_backends = (DjangoFilterBackend,) filterset_fields = ["id", "location"] permission_classes = (IsAuthenticated, DjangoModelPermissions) + def filter_queryset(self, queryset): + return Network_Locations.objects.all().order_by("id") + # Authorization: superuser class ConfigurationPermissionViewSet( viewsets.ReadOnlyModelViewSet, ): serializer_class = serializers.ConfigurationPermissionSerializer - queryset = Permission.objects.filter( - codename__in=get_configuration_permissions_codenames() - ) + queryset = Permission.objects.none() filter_backends = (DjangoFilterBackend,) filterset_fields = ["id", "name", "codename"] permission_classes = (permissions.IsSuperUser, DjangoModelPermissions) + def filter_queryset(self, queryset): + return Permission.objects.filter( + codename__in=get_configuration_permissions_codenames() + ).order_by("id") + class SLAConfigurationViewset( DojoModelViewSet, ): serializer_class = serializers.SLAConfigurationSerializer - queryset = SLA_Configuration.objects.all() + queryset = SLA_Configuration.objects.none() filter_backends = (DjangoFilterBackend,) permission_classes = (IsAuthenticated, DjangoModelPermissions) + def filter_queryset(self, queryset): + return SLA_Configuration.objects.all().order_by("id") + class QuestionnaireQuestionViewSet( viewsets.ReadOnlyModelViewSet, dojo_mixins.QuestionSubClassFieldsMixin, ): serializer_class = serializers.QuestionnaireQuestionSerializer - queryset = Question.objects.all() + queryset = Question.objects.none() filter_backends = (DjangoFilterBackend,) permission_classes = ( permissions.UserHasEngagementPermission, DjangoModelPermissions, ) + def filter_queryset(self, queryset): + return Question.objects.all().order_by("id") + class QuestionnaireAnswerViewSet( viewsets.ReadOnlyModelViewSet, dojo_mixins.AnswerSubClassFieldsMixin, ): serializer_class = serializers.QuestionnaireAnswerSerializer - queryset = Answer.objects.all() + queryset = Answer.objects.none() filter_backends = (DjangoFilterBackend,) permission_classes = ( permissions.UserHasEngagementPermission, DjangoModelPermissions, ) + def filter_queryset(self, queryset): + return Answer.objects.all().order_by("id") + class QuestionnaireGeneralSurveyViewSet( viewsets.ReadOnlyModelViewSet, ): serializer_class = serializers.QuestionnaireGeneralSurveySerializer - queryset = General_Survey.objects.all() + queryset = General_Survey.objects.none() filter_backends = (DjangoFilterBackend,) permission_classes = ( permissions.UserHasEngagementPermission, DjangoModelPermissions, ) + def filter_queryset(self, queryset): + return General_Survey.objects.all().order_by("id") + class QuestionnaireEngagementSurveyViewSet( viewsets.ReadOnlyModelViewSet ): serializer_class = serializers.QuestionnaireEngagementSurveySerializer - queryset = Engagement_Survey.objects.all() + queryset = Engagement_Survey.objects.none() filter_backends = (DjangoFilterBackend,) permission_classes = ( permissions.UserHasEngagementPermission, DjangoModelPermissions, ) + def filter_queryset(self, queryset): + return Engagement_Survey.objects.all().order_by("id") + class QuestionnaireAnsweredSurveyViewSet( prefetch.PrefetchListMixin, @@ -3293,20 +3350,26 @@ class QuestionnaireAnsweredSurveyViewSet( viewsets.ReadOnlyModelViewSet, ): serializer_class = serializers.QuestionnaireAnsweredSurveySerializer - queryset = Answered_Survey.objects.all() + queryset = Answered_Survey.objects.none() filter_backends = (DjangoFilterBackend,) permission_classes = ( permissions.UserHasEngagementPermission, DjangoModelPermissions, ) + def filter_queryset(self, queryset): + return Answered_Survey.objects.all().order_by("id") + # Authorization: configuration class AnnouncementViewSet( DojoModelViewSet ): serializer_class = serializers.AnnouncementSerializer - queryset = Announcement.objects.all() + queryset = Announcement.objects.none() filter_backends = (DjangoFilterBackend,) filterset_fields = "__all__" permission_classes = (permissions.UserHasConfigurationPermissionStaff,) + + def filter_queryset(self, queryset): + return Announcement.objects.all().order_by("id") diff --git a/dojo/cred/queries.py b/dojo/cred/queries.py index d86c432fc69..f0868d7db1a 100644 --- a/dojo/cred/queries.py +++ b/dojo/cred/queries.py @@ -12,7 +12,7 @@ def get_authorized_cred_mappings(permission, queryset=None): return Cred_Mapping.objects.none() if queryset is None: - cred_mappings = Cred_Mapping.objects.all() + cred_mappings = Cred_Mapping.objects.all().order_by("id") else: cred_mappings = queryset diff --git a/dojo/endpoint/queries.py b/dojo/endpoint/queries.py index e9facac14fe..c1a3267ba2d 100644 --- a/dojo/endpoint/queries.py +++ b/dojo/endpoint/queries.py @@ -21,7 +21,7 @@ def get_authorized_endpoints(permission, queryset=None, user=None): return Endpoint.objects.none() if queryset is None: - endpoints = Endpoint.objects.all() + endpoints = Endpoint.objects.all().order_by("id") else: endpoints = queryset @@ -69,7 +69,7 @@ def get_authorized_endpoint_status(permission, queryset=None, user=None): return Endpoint_Status.objects.none() if queryset is None: - endpoint_status = Endpoint_Status.objects.all() + endpoint_status = Endpoint_Status.objects.all().order_by("id") else: endpoint_status = queryset diff --git a/dojo/engagement/queries.py b/dojo/engagement/queries.py index d5a7f445931..ea116b57450 100644 --- a/dojo/engagement/queries.py +++ b/dojo/engagement/queries.py @@ -12,10 +12,10 @@ def get_authorized_engagements(permission): return Engagement.objects.none() if user.is_superuser: - return Engagement.objects.all() + return Engagement.objects.all().order_by("id") if user_has_global_permission(user, permission): - return Engagement.objects.all() + return Engagement.objects.all().order_by("id") roles = get_roles_for_permission(permission) authorized_product_type_roles = Product_Type_Member.objects.filter( @@ -38,7 +38,7 @@ def get_authorized_engagements(permission): product__prod_type__member=Exists(authorized_product_type_roles), product__member=Exists(authorized_product_roles), product__prod_type__authorized_group=Exists(authorized_product_type_groups), - product__authorized_group=Exists(authorized_product_groups)) + product__authorized_group=Exists(authorized_product_groups)).order_by("id") engagements = engagements.filter( Q(product__prod_type__member=True) | Q(product__member=True) | Q(product__prod_type__authorized_group=True) | Q(product__authorized_group=True)) diff --git a/dojo/finding/queries.py b/dojo/finding/queries.py index e10cfca3ddd..9011c8511d3 100644 --- a/dojo/finding/queries.py +++ b/dojo/finding/queries.py @@ -46,7 +46,7 @@ def get_authorized_findings(permission, queryset=None, user=None): if user is None: return Finding.objects.none() if queryset is None: - findings = Finding.objects.all() + findings = Finding.objects.all().order_by("id") else: findings = queryset @@ -84,10 +84,10 @@ def get_authorized_stub_findings(permission): return Stub_Finding.objects.none() if user.is_superuser: - return Stub_Finding.objects.all() + return Stub_Finding.objects.all().order_by("id") if user_has_global_permission(user, permission): - return Stub_Finding.objects.all() + return Stub_Finding.objects.all().order_by("id") ( authorized_product_type_roles, @@ -100,7 +100,7 @@ def get_authorized_stub_findings(permission): test__engagement__product__prod_type__member=Exists(authorized_product_type_roles), test__engagement__product__member=Exists(authorized_product_roles), test__engagement__product__prod_type__authorized_group=Exists(authorized_product_type_groups), - test__engagement__product__authorized_group=Exists(authorized_product_groups)) + test__engagement__product__authorized_group=Exists(authorized_product_groups)).order_by("id") findings = findings.filter( Q(test__engagement__product__prod_type__member=True) | Q(test__engagement__product__member=True) diff --git a/dojo/group/queries.py b/dojo/group/queries.py index db4d8d633df..82658226636 100644 --- a/dojo/group/queries.py +++ b/dojo/group/queries.py @@ -30,10 +30,10 @@ def get_authorized_group_members(permission): return Dojo_Group_Member.objects.none() if user.is_superuser: - return Dojo_Group_Member.objects.all().select_related('role') + return Dojo_Group_Member.objects.all().order_by("id").select_related('role') groups = get_authorized_groups(permission) - return Dojo_Group_Member.objects.filter(group__in=groups).select_related('role') + return Dojo_Group_Member.objects.filter(group__in=groups).order_by("id").select_related('role') def get_authorized_group_members_for_user(user): diff --git a/dojo/jira_link/queries.py b/dojo/jira_link/queries.py index 4b9d9c09b7d..c00ba02da74 100644 --- a/dojo/jira_link/queries.py +++ b/dojo/jira_link/queries.py @@ -13,7 +13,7 @@ def get_authorized_jira_projects(permission, user=None): if user is None: return JIRA_Project.objects.none() - jira_projects = JIRA_Project.objects.all() + jira_projects = JIRA_Project.objects.all().order_by("id") if user.is_superuser: return jira_projects @@ -82,7 +82,7 @@ def get_authorized_jira_issues(permission): if user is None: return JIRA_Issue.objects.none() - jira_issues = JIRA_Issue.objects.all() + jira_issues = JIRA_Issue.objects.all().order_by("id") if user.is_superuser: return jira_issues diff --git a/dojo/product/queries.py b/dojo/product/queries.py index 96f1b626cb3..413835d69bc 100644 --- a/dojo/product/queries.py +++ b/dojo/product/queries.py @@ -92,13 +92,13 @@ def get_authorized_product_members(permission): return Product_Member.objects.none() if user.is_superuser: - return Product_Member.objects.all().select_related('role') + return Product_Member.objects.all().order_by("id").select_related('role') if user_has_global_permission(user, permission): - return Product_Member.objects.all().select_related('role') + return Product_Member.objects.all().order_by("id").select_related('role') products = get_authorized_products(permission) - return Product_Member.objects.filter(product__in=products).select_related('role') + return Product_Member.objects.filter(product__in=products).order_by("id").select_related('role') def get_authorized_product_members_for_user(user, permission): @@ -124,10 +124,10 @@ def get_authorized_product_groups(permission): return Product_Group.objects.none() if user.is_superuser: - return Product_Group.objects.all().select_related('role') + return Product_Group.objects.all().order_by("id").select_related('role') products = get_authorized_products(permission) - return Product_Group.objects.filter(product__in=products).select_related('role') + return Product_Group.objects.filter(product__in=products).order_by("id").select_related('role') def get_authorized_app_analysis(permission): @@ -137,10 +137,10 @@ def get_authorized_app_analysis(permission): return App_Analysis.objects.none() if user.is_superuser: - return App_Analysis.objects.all().order_by('name') + return App_Analysis.objects.all().order_by('id') if user_has_global_permission(user, permission): - return App_Analysis.objects.all().order_by('name') + return App_Analysis.objects.all().order_by('id') roles = get_roles_for_permission(permission) authorized_product_type_roles = Product_Type_Member.objects.filter( @@ -163,7 +163,7 @@ def get_authorized_app_analysis(permission): product__prod_type__member=Exists(authorized_product_type_roles), product__member=Exists(authorized_product_roles), product__prod_type__authorized_group=Exists(authorized_product_type_groups), - product__authorized_group=Exists(authorized_product_groups)).order_by('name') + product__authorized_group=Exists(authorized_product_groups)).order_by('id') app_analysis = app_analysis.filter( Q(product__prod_type__member=True) | Q(product__member=True) | Q(product__prod_type__authorized_group=True) | Q(product__authorized_group=True)) @@ -178,10 +178,10 @@ def get_authorized_dojo_meta(permission): return DojoMeta.objects.none() if user.is_superuser: - return DojoMeta.objects.all().order_by('name') + return DojoMeta.objects.all().order_by("id") if user_has_global_permission(user, permission): - return DojoMeta.objects.all().order_by('name') + return DojoMeta.objects.all().order_by("id") roles = get_roles_for_permission(permission) product_authorized_product_type_roles = Product_Type_Member.objects.filter( @@ -245,7 +245,7 @@ def get_authorized_dojo_meta(permission): finding__test__engagement__product__member=Exists(finding_authorized_product_roles), finding__test__engagement__product__prod_type__authorized_group=Exists(finding_authorized_product_type_groups), finding__test__engagement__product__authorized_group=Exists(finding_authorized_product_groups) - ).order_by('name') + ).order_by("id") dojo_meta = dojo_meta.filter( Q(product__prod_type__member=True) | Q(product__member=True) @@ -270,10 +270,10 @@ def get_authorized_languages(permission): return Languages.objects.none() if user.is_superuser: - return Languages.objects.all().order_by('language') + return Languages.objects.all().order_by("id") if user_has_global_permission(user, permission): - return Languages.objects.all().order_by('language') + return Languages.objects.all().order_by("id") roles = get_roles_for_permission(permission) authorized_product_type_roles = Product_Type_Member.objects.filter( @@ -296,7 +296,7 @@ def get_authorized_languages(permission): product__prod_type__member=Exists(authorized_product_type_roles), product__member=Exists(authorized_product_roles), product__prod_type__authorized_group=Exists(authorized_product_type_groups), - product__authorized_group=Exists(authorized_product_groups)).order_by('language') + product__authorized_group=Exists(authorized_product_groups)).order_by("id") languages = languages.filter( Q(product__prod_type__member=True) | Q(product__member=True) | Q(product__prod_type__authorized_group=True) | Q(product__authorized_group=True)) @@ -311,10 +311,10 @@ def get_authorized_engagement_presets(permission): return Engagement_Presets.objects.none() if user.is_superuser: - return Engagement_Presets.objects.all().order_by('title') + return Engagement_Presets.objects.all().order_by("id") if user_has_global_permission(user, permission): - return Engagement_Presets.objects.all().order_by('title') + return Engagement_Presets.objects.all().order_by("id") roles = get_roles_for_permission(permission) authorized_product_type_roles = Product_Type_Member.objects.filter( @@ -337,7 +337,7 @@ def get_authorized_engagement_presets(permission): product__prod_type__member=Exists(authorized_product_type_roles), product__member=Exists(authorized_product_roles), product__prod_type__authorized_group=Exists(authorized_product_type_groups), - product__authorized_group=Exists(authorized_product_groups)).order_by('title') + product__authorized_group=Exists(authorized_product_groups)).order_by("id") engagement_presets = engagement_presets.filter( Q(product__prod_type__member=True) | Q(product__member=True) | Q(product__prod_type__authorized_group=True) | Q(product__authorized_group=True)) @@ -352,10 +352,10 @@ def get_authorized_product_api_scan_configurations(permission): return Product_API_Scan_Configuration.objects.none() if user.is_superuser: - return Product_API_Scan_Configuration.objects.all() + return Product_API_Scan_Configuration.objects.all().order_by("id") if user_has_global_permission(user, permission): - return Product_API_Scan_Configuration.objects.all() + return Product_API_Scan_Configuration.objects.all().order_by("id") roles = get_roles_for_permission(permission) authorized_product_type_roles = Product_Type_Member.objects.filter( @@ -378,7 +378,7 @@ def get_authorized_product_api_scan_configurations(permission): product__prod_type__member=Exists(authorized_product_type_roles), product__member=Exists(authorized_product_roles), product__prod_type__authorized_group=Exists(authorized_product_type_groups), - product__authorized_group=Exists(authorized_product_groups)) + product__authorized_group=Exists(authorized_product_groups)).order_by("id") product_api_scan_configurations = product_api_scan_configurations.filter( Q(product__prod_type__member=True) | Q(product__member=True) | Q(product__prod_type__authorized_group=True) | Q(product__authorized_group=True)) diff --git a/dojo/product_type/queries.py b/dojo/product_type/queries.py index 4b658798e29..787a5d6a120 100644 --- a/dojo/product_type/queries.py +++ b/dojo/product_type/queries.py @@ -66,13 +66,13 @@ def get_authorized_product_type_members(permission): return Product_Type_Member.objects.none() if user.is_superuser: - return Product_Type_Member.objects.all().select_related('role') + return Product_Type_Member.objects.all().order_by("id").select_related('role') if user_has_global_permission(user, permission): - return Product_Type_Member.objects.all().select_related('role') + return Product_Type_Member.objects.all().order_by("id").select_related('role') product_types = get_authorized_product_types(permission) - return Product_Type_Member.objects.filter(product_type__in=product_types).select_related('role') + return Product_Type_Member.objects.filter(product_type__in=product_types).order_by("id").select_related('role') def get_authorized_product_type_members_for_user(user, permission): @@ -98,7 +98,7 @@ def get_authorized_product_type_groups(permission): return Product_Type_Group.objects.none() if user.is_superuser: - return Product_Type_Group.objects.all().select_related('role') + return Product_Type_Group.objects.all().order_by("id").select_related('role') product_types = get_authorized_product_types(permission) - return Product_Type_Group.objects.filter(product_type__in=product_types).select_related('role') + return Product_Type_Group.objects.filter(product_type__in=product_types).order_by("id").select_related('role') diff --git a/dojo/risk_acceptance/queries.py b/dojo/risk_acceptance/queries.py index 2d45fb64457..92ab786b92a 100644 --- a/dojo/risk_acceptance/queries.py +++ b/dojo/risk_acceptance/queries.py @@ -12,10 +12,10 @@ def get_authorized_risk_acceptances(permission): return Risk_Acceptance.objects.none() if user.is_superuser: - return Risk_Acceptance.objects.all() + return Risk_Acceptance.objects.all().order_by("id") if user_has_global_permission(user, permission): - return Risk_Acceptance.objects.all() + return Risk_Acceptance.objects.all().order_by("id") roles = get_roles_for_permission(permission) authorized_product_type_roles = Product_Type_Member.objects.filter( @@ -38,7 +38,7 @@ def get_authorized_risk_acceptances(permission): product__prod_type__member=Exists(authorized_product_type_roles), product__member=Exists(authorized_product_roles), product__prod_type__authorized_group=Exists(authorized_product_type_groups), - product__authorized_group=Exists(authorized_product_groups)) + product__authorized_group=Exists(authorized_product_groups)).order_by("id") risk_acceptances = risk_acceptances.filter( Q(product__prod_type__member=True) | Q(product__member=True) | Q(product__prod_type__authorized_group=True) | Q(product__authorized_group=True)) diff --git a/dojo/test/queries.py b/dojo/test/queries.py index 29400451a0d..daabaf3c494 100644 --- a/dojo/test/queries.py +++ b/dojo/test/queries.py @@ -11,7 +11,7 @@ def get_authorized_tests(permission, product=None): if user is None: return Test.objects.none() - tests = Test.objects.all() + tests = Test.objects.all().order_by("id") if product: tests = tests.filter(engagement__product=product) @@ -19,7 +19,7 @@ def get_authorized_tests(permission, product=None): return tests if user_has_global_permission(user, permission): - return Test.objects.all() + return Test.objects.all().order_by("id") roles = get_roles_for_permission(permission) authorized_product_type_roles = Product_Type_Member.objects.filter( @@ -62,10 +62,10 @@ def get_authorized_test_imports(permission): return Test_Import.objects.none() if user.is_superuser: - return Test_Import.objects.all() + return Test_Import.objects.all().order_by("id") if user_has_global_permission(user, permission): - return Test_Import.objects.all() + return Test_Import.objects.all().order_by("id") roles = get_roles_for_permission(permission) authorized_product_type_roles = Product_Type_Member.objects.filter( @@ -88,7 +88,7 @@ def get_authorized_test_imports(permission): test__engagement__product__prod_type__member=Exists(authorized_product_type_roles), test__engagement__product__member=Exists(authorized_product_roles), test__engagement__product__prod_type__authorized_group=Exists(authorized_product_type_groups), - test__engagement__product__authorized_group=Exists(authorized_product_groups)) + test__engagement__product__authorized_group=Exists(authorized_product_groups)).order_by("id") test_imports = test_imports.filter( Q(test__engagement__product__prod_type__member=True) | Q(test__engagement__product__member=True) diff --git a/dojo/tool_product/queries.py b/dojo/tool_product/queries.py index b098ef050a9..d4c5e0b4e79 100644 --- a/dojo/tool_product/queries.py +++ b/dojo/tool_product/queries.py @@ -12,10 +12,10 @@ def get_authorized_tool_product_settings(permission): return Tool_Product_Settings.objects.none() if user.is_superuser: - return Tool_Product_Settings.objects.all() + return Tool_Product_Settings.objects.all().order_by("id") if user_has_global_permission(user, permission): - return Tool_Product_Settings.objects.all() + return Tool_Product_Settings.objects.all().order_by("id") roles = get_roles_for_permission(permission) authorized_product_type_roles = Product_Type_Member.objects.filter( @@ -38,7 +38,7 @@ def get_authorized_tool_product_settings(permission): product__prod_type__member=Exists(authorized_product_type_roles), product__member=Exists(authorized_product_roles), product__prod_type__authorized_group=Exists(authorized_product_type_groups), - product__authorized_group=Exists(authorized_product_groups)) + product__authorized_group=Exists(authorized_product_groups)).order_by("id") tool_product_settings = tool_product_settings.filter( Q(product__prod_type__member=True) | Q(product__member=True) | Q(product__prod_type__authorized_group=True) | Q(product__authorized_group=True)) diff --git a/dojo/urls.py b/dojo/urls.py index b9d9493c66a..0793ef37e97 100644 --- a/dojo/urls.py +++ b/dojo/urls.py @@ -116,65 +116,65 @@ # v2 api written in django-rest-framework v2_api = DefaultRouter() -v2_api.register(r'technologies', AppAnalysisViewSet) -v2_api.register(r'configuration_permissions', ConfigurationPermissionViewSet) -v2_api.register(r'credentials', CredentialsViewSet) -v2_api.register(r'credential_mappings', CredentialsMappingViewSet) -v2_api.register(r'endpoints', EndPointViewSet) +v2_api.register(r'announcements', AnnouncementViewSet, basename="announcement") +v2_api.register(r'configuration_permissions', ConfigurationPermissionViewSet, basename="permission") +v2_api.register(r'credential_mappings', CredentialsMappingViewSet, basename="cred_mapping") +v2_api.register(r'credentials', CredentialsViewSet, basename="cred_user") +v2_api.register(r'development_environments', DevelopmentEnvironmentViewSet, basename="development_environment") +v2_api.register(r'dojo_groups', DojoGroupViewSet, basename="dojo_group") +v2_api.register(r'dojo_group_members', DojoGroupMemberViewSet, basename="dojo_group_member") +v2_api.register(r'endpoints', EndPointViewSet, basename="endpoint") v2_api.register(r'endpoint_meta_import', EndpointMetaImporterView, basename='endpointmetaimport') -v2_api.register(r'endpoint_status', EndpointStatusViewSet) -v2_api.register(r'engagements', EngagementViewSet) -v2_api.register(r'development_environments', DevelopmentEnvironmentViewSet) -v2_api.register(r'finding_templates', FindingTemplatesViewSet) -v2_api.register(r'findings', FindingViewSet, basename='finding') -v2_api.register(r'jira_configurations', JiraInstanceViewSet) # backwards compatibility -v2_api.register(r'jira_instances', JiraInstanceViewSet) -v2_api.register(r'jira_finding_mappings', JiraIssuesViewSet) -v2_api.register(r'jira_product_configurations', JiraProjectViewSet) # backwards compatibility -v2_api.register(r'jira_projects', JiraProjectViewSet) -v2_api.register(r'products', ProductViewSet) -v2_api.register(r'product_types', ProductTypeViewSet) -v2_api.register(r'dojo_groups', DojoGroupViewSet) -v2_api.register(r'dojo_group_members', DojoGroupMemberViewSet) -v2_api.register(r'product_type_members', ProductTypeMemberViewSet) -v2_api.register(r'product_members', ProductMemberViewSet) -v2_api.register(r'product_type_groups', ProductTypeGroupViewSet) -v2_api.register(r'product_groups', ProductGroupViewSet) -v2_api.register(r'roles', RoleViewSet) -v2_api.register(r'global_roles', GlobalRoleViewSet) -v2_api.register(r'sla_configurations', SLAConfigurationViewset) -v2_api.register(r'sonarqube_issues', SonarqubeIssueViewSet) -v2_api.register(r'sonarqube_transitions', SonarqubeIssueTransitionViewSet) -v2_api.register(r'product_api_scan_configurations', ProductAPIScanConfigurationViewSet) -v2_api.register(r'stub_findings', StubFindingsViewSet) -v2_api.register(r'tests', TestsViewSet) -v2_api.register(r'test_types', TestTypesViewSet) -v2_api.register(r'test_imports', TestImportViewSet) -v2_api.register(r'tool_configurations', ToolConfigurationsViewSet) -v2_api.register(r'tool_product_settings', ToolProductSettingsViewSet) -v2_api.register(r'tool_types', ToolTypesViewSet) -v2_api.register(r'users', UsersViewSet) -v2_api.register(r'user_contact_infos', UserContactInfoViewSet) +v2_api.register(r'endpoint_status', EndpointStatusViewSet, basename="endpoint_status") +v2_api.register(r'engagements', EngagementViewSet, basename="engagement") +v2_api.register(r'engagement_presets', EngagementPresetsViewset, basename="engagement_presets") +v2_api.register(r'finding_templates', FindingTemplatesViewSet, basename="finding_template") +v2_api.register(r'findings', FindingViewSet, basename="finding") +v2_api.register(r'global_roles', GlobalRoleViewSet, basename="global_role") +v2_api.register(r'import-languages', ImportLanguagesView, basename='importlanguages') v2_api.register(r'import-scan', ImportScanView, basename='importscan') -v2_api.register(r'reimport-scan', ReImportScanView, basename='reimportscan') +v2_api.register(r'jira_instances', JiraInstanceViewSet, basename="jira_instance") +v2_api.register(r'jira_configurations', JiraInstanceViewSet, basename="jira_configurations") # backwards compatibility +v2_api.register(r'jira_finding_mappings', JiraIssuesViewSet, basename="jira_issue") +v2_api.register(r'jira_product_configurations', JiraProjectViewSet, basename="jira_product_configurations") # backwards compatibility +v2_api.register(r'jira_projects', JiraProjectViewSet, basename="jira_project") +v2_api.register(r'languages', LanguageViewSet, basename="languages") +v2_api.register(r'language_types', LanguageTypeViewSet, basename="language_type") v2_api.register(r'metadata', DojoMetaViewSet, basename='metadata') -v2_api.register(r'notes', NotesViewSet) -v2_api.register(r'note_type', NoteTypeViewSet) -v2_api.register(r'system_settings', SystemSettingsViewSet) -v2_api.register(r'regulations', RegulationsViewSet) -v2_api.register(r'risk_acceptance', RiskAcceptanceViewSet) -v2_api.register(r'language_types', LanguageTypeViewSet) -v2_api.register(r'languages', LanguageViewSet) -v2_api.register(r'import-languages', ImportLanguagesView, basename='importlanguages') +v2_api.register(r'network_locations', NetworkLocationsViewset, basename="network_locations") +v2_api.register(r'notes', NotesViewSet, basename="notes") +v2_api.register(r'note_type', NoteTypeViewSet, basename="note_type") v2_api.register(r'notifications', NotificationsViewSet, basename='notifications') -v2_api.register(r'engagement_presets', EngagementPresetsViewset) -v2_api.register(r'network_locations', NetworkLocationsViewset) -v2_api.register(r'questionnaire_answers', QuestionnaireAnswerViewSet) -v2_api.register(r'questionnaire_answered_questionnaires', QuestionnaireAnsweredSurveyViewSet) -v2_api.register(r'questionnaire_engagement_questionnaires', QuestionnaireEngagementSurveyViewSet) -v2_api.register(r'questionnaire_general_questionnaires', QuestionnaireGeneralSurveyViewSet) -v2_api.register(r'questionnaire_questions', QuestionnaireQuestionViewSet) -v2_api.register(r'announcements', AnnouncementViewSet) +v2_api.register(r'products', ProductViewSet, basename="product") +v2_api.register(r'product_api_scan_configurations', ProductAPIScanConfigurationViewSet, basename="product_api_scan_configuration") +v2_api.register(r'product_groups', ProductGroupViewSet, basename="product_group") +v2_api.register(r'product_members', ProductMemberViewSet, basename="product_member") +v2_api.register(r'product_types', ProductTypeViewSet, basename="product_type") +v2_api.register(r'product_type_members', ProductTypeMemberViewSet, basename="product_type_member") +v2_api.register(r'product_type_groups', ProductTypeGroupViewSet, basename="product_type_group") +v2_api.register(r'regulations', RegulationsViewSet, basename="regulations") +v2_api.register(r'reimport-scan', ReImportScanView, basename='reimportscan') +v2_api.register(r'risk_acceptance', RiskAcceptanceViewSet, basename="risk_acceptance") +v2_api.register(r'roles', RoleViewSet, basename="role") +v2_api.register(r'sla_configurations', SLAConfigurationViewset, basename="sla_configurations") +v2_api.register(r'sonarqube_issues', SonarqubeIssueViewSet, basename="sonarqube_issue") +v2_api.register(r'sonarqube_transitions', SonarqubeIssueTransitionViewSet, basename="sonarqube_issue_transition") +v2_api.register(r'stub_findings', StubFindingsViewSet, basename="stub_finding") +v2_api.register(r'system_settings', SystemSettingsViewSet, basename="system_settings") +v2_api.register(r'technologies', AppAnalysisViewSet, basename="app_analysis") +v2_api.register(r'tests', TestsViewSet, basename="test") +v2_api.register(r'test_types', TestTypesViewSet, basename="test_type") +v2_api.register(r'test_imports', TestImportViewSet, basename="test_imports") +v2_api.register(r'tool_configurations', ToolConfigurationsViewSet, basename="tool_configuration") +v2_api.register(r'tool_product_settings', ToolProductSettingsViewSet, basename="tool_product_settings") +v2_api.register(r'tool_types', ToolTypesViewSet, basename="tool_type") +v2_api.register(r'users', UsersViewSet, basename="user") +v2_api.register(r'user_contact_infos', UserContactInfoViewSet, basename="usercontactinfo") +v2_api.register(r'questionnaire_answers', QuestionnaireAnswerViewSet, basename="answer") +v2_api.register(r'questionnaire_answered_questionnaires', QuestionnaireAnsweredSurveyViewSet, basename="answered_survey") +v2_api.register(r'questionnaire_engagement_questionnaires', QuestionnaireEngagementSurveyViewSet, basename="engagement_survey") +v2_api.register(r'questionnaire_general_questionnaires', QuestionnaireGeneralSurveyViewSet, basename="general_survey") +v2_api.register(r'questionnaire_questions', QuestionnaireQuestionViewSet, basename="question") ur = [] ur += dev_env_urls ur += endpoint_urls diff --git a/unittests/dojo_test_case.py b/unittests/dojo_test_case.py index 1a4468c6f07..5d4008ad157 100644 --- a/unittests/dojo_test_case.py +++ b/unittests/dojo_test_case.py @@ -468,7 +468,7 @@ def reimport_scan(self, payload, expected_http_status_code): def endpoint_meta_import_scan(self, payload, expected_http_status_code): logger.debug('endpoint_meta_import_scan payload %s', payload) response = self.client.post(reverse('endpointmetaimport-list'), payload) - print(response.content) + # print(response.content) self.assertEqual(expected_http_status_code, response.status_code, response.content[:1000]) return json.loads(response.content) @@ -478,6 +478,12 @@ def get_test_api(self, test_id): # print('test.content: ', response.content) return json.loads(response.content) + def get_results_by_id(self, results: list, object_id: int) -> dict | None: + for item in results: + if item.get("id") == object_id: + return item + return None + def import_scan_with_params(self, filename, scan_type='ZAP Scan', engagement=1, minimum_severity='Low', active=True, verified=False, push_to_jira=None, endpoint_to_add=None, tags=None, close_old_findings=False, group_by=None, engagement_name=None, product_name=None, product_type_name=None, auto_create_context=None, expected_http_status_code=201, test_title=None, @@ -740,18 +746,14 @@ def post_finding_notes_api(self, finding_id, note): return response.data def log_finding_summary_json_api(self, findings_content_json=None): - print('summary') - print(findings_content_json) - print(findings_content_json['count']) + logger.debug('summary') + logger.debug(findings_content_json) + logger.debug(findings_content_json['count']) if not findings_content_json or findings_content_json['count'] == 0: logger.debug('no findings') else: for finding in findings_content_json['results']: - print(str(finding['id']) + ': ' + finding['title'][:5] + ':' + finding['severity'] + ': active: ' + str(finding['active']) + ': verified: ' + str(finding['verified']) - + ': is_mitigated: ' + str(finding['is_mitigated']) + ": notes: " + str([n['id'] for n in finding['notes']]) - + ": endpoints: " + str(finding['endpoints'])) - logger.debug(str(finding['id']) + ': ' + finding['title'][:5] + ':' + finding['severity'] + ': active: ' + str(finding['active']) + ': verified: ' + str(finding['verified']) + ': is_mitigated: ' + str(finding['is_mitigated']) + ": notes: " + str([n['id'] for n in finding['notes']]) + ": endpoints: " + str(finding['endpoints'])) diff --git a/unittests/test_apiv2_notifications.py b/unittests/test_apiv2_notifications.py index 06aa7413c63..b136e579fd3 100644 --- a/unittests/test_apiv2_notifications.py +++ b/unittests/test_apiv2_notifications.py @@ -1,9 +1,11 @@ from django.urls import reverse from rest_framework.authtoken.models import Token -from rest_framework.test import APIClient, APITestCase +from rest_framework.test import APIClient +from unittests.dojo_test_case import DojoAPITestCase -class NotificationsTest(APITestCase): + +class NotificationsTest(DojoAPITestCase): """ Test the metadata APIv2 endpoint. """ @@ -18,8 +20,12 @@ def setUp(self): template=True, scan_added=['alert', 'slack'] ) + self.creation_id = r.json()["id"] self.assertEqual(r.status_code, 201) + def tearDown(self): + self.client.delete(f"{reverse('notifications-list')}/{self.creation_id}") + def create(self, **kwargs): return self.client.post(reverse('notifications-list'), kwargs, format='json') @@ -34,13 +40,15 @@ def create_test_user(self): def test_notification_get(self): r = self.client.get(reverse('notifications-list'), format='json') self.assertEqual(r.status_code, 200) - self.assertEqual(r.json()['results'][0]['template'], False) + item = self.get_results_by_id(r.json()['results'], 1) + self.assertEqual(item['template'], False) def test_notification_template(self): q = {'template': True} r = self.client.get(reverse('notifications-list'), q, format='json') self.assertEqual(r.status_code, 200) - self.assertEqual(r.json()['results'][0]['template'], True) + item = self.get_results_by_id(r.json()['results'], self.creation_id) + self.assertEqual(item['template'], True) def test_notification_template_multiple(self): q = {'template': True, 'scan_added': ['alert', 'slack']} @@ -54,6 +62,7 @@ def test_user_notifications(self): user = {"user": self.create_test_user()} r = self.client.get(reverse('notifications-list'), user, format='json') self.assertEqual(r.status_code, 200) - self.assertEqual(r.json()['results'][0]['template'], False) - self.assertIn('alert', r.json()['results'][0]['scan_added']) - self.assertIn('slack', r.json()['results'][0]['scan_added']) + item = r.json()['results'][-1] + self.assertEqual(item['template'], False) + self.assertIn('alert', item['scan_added']) + self.assertIn('slack', item['scan_added']) diff --git a/unittests/test_apiv2_scan_import_options.py b/unittests/test_apiv2_scan_import_options.py index 78200e48fd1..176abac8ec3 100644 --- a/unittests/test_apiv2_scan_import_options.py +++ b/unittests/test_apiv2_scan_import_options.py @@ -17,7 +17,7 @@ class ScanImportOptionsTest(APITestCase): """ - def __del__(self): + def tearDown(self): self.payload['file'].close() def setUp(self): diff --git a/unittests/test_import_reimport.py b/unittests/test_import_reimport.py index dfbd9c21ca7..f4eb92df7ea 100644 --- a/unittests/test_import_reimport.py +++ b/unittests/test_import_reimport.py @@ -315,7 +315,7 @@ def test_import_reimport_scan_date_parser_date(self): self.assert_finding_count_json(5, findings) # scan_date provided, so date should be equal to that overriding that from the parser self.log_finding_summary_json_api(findings) - self.assertEqual(findings['results'][2]['date'], "2020-02-02") + self.assertEqual(findings['results'][4]['date'], "2020-02-02") # Test re-import with unique_id_from_tool algorithm # import sonar scan with detailed parser, testing: diff --git a/unittests/test_importers_importer.py b/unittests/test_importers_importer.py index 0d26c5b0e8c..4274428744e 100644 --- a/unittests/test_importers_importer.py +++ b/unittests/test_importers_importer.py @@ -525,7 +525,7 @@ def setUp(self): self.client.force_authenticate(user=self.testuser, token=token) self.create_default_data() - def __del__(self): + def tearDown(self): self.test_last_by_scan_type.delete() self.test_with_title.delete() self.test_last_by_title.delete() diff --git a/unittests/test_jira_config_engagement.py b/unittests/test_jira_config_engagement.py index cf952b60682..7090f6278ed 100644 --- a/unittests/test_jira_config_engagement.py +++ b/unittests/test_jira_config_engagement.py @@ -1,4 +1,5 @@ # from unittest import skip +import contextlib import logging from unittest.mock import patch @@ -265,9 +266,11 @@ def test_add_jira_project_to_engagement_without_jira_project(self, jira_mock): @patch('dojo.jira_link.views.jira_helper.is_jira_project_valid') def test_add_empty_jira_project_to_engagement_without_jira_project(self, jira_mock): jira_mock.return_value = True # cannot set return_value in decorated AND have the mock into the method - engagement = self.add_engagement_without_jira_project(expected_delta_jira_project_db=0) - self.empty_jira_project_for_engagement(engagement, expected_delta_jira_project_db=0) - self.assertEqual(jira_mock.call_count, 0) + # Prevent the exception from being raised here so that the test can be ran in parallel + with contextlib.suppress(ValueError): + engagement = self.add_engagement_without_jira_project(expected_delta_jira_project_db=0) + self.empty_jira_project_for_engagement(engagement, expected_delta_jira_project_db=0) + self.assertEqual(jira_mock.call_count, 0) @patch('dojo.jira_link.views.jira_helper.is_jira_project_valid') def test_edit_jira_project_to_engagement_with_jira_project(self, jira_mock): @@ -279,15 +282,17 @@ def test_edit_jira_project_to_engagement_with_jira_project(self, jira_mock): @patch('dojo.jira_link.views.jira_helper.is_jira_project_valid') def test_edit_empty_jira_project_to_engagement_with_jira_project(self, jira_mock): jira_mock.return_value = True # cannot set return_value in decorated AND have the mock into the method - engagement = self.add_engagement_with_jira_project(expected_delta_jira_project_db=1) - # clearing out jira config used to be possible. what todo? - # - delete jira project? would disconnect all existing jira issues in defect dojo from the config? - # - allow jira project with empty jira instance and/or empty project_key? unpredictable behaviour - # - so prevent clearing out these values - # response = self.empty_jira_project_for_engagement(Engagement.objects.get(id=3), -1) - # expecting ValueError as we can't delete existing JIRA Projects - self.empty_jira_project_for_engagement(engagement, expected_delta_jira_project_db=0, expect_error=True) - self.assertEqual(jira_mock.call_count, 1) + # Prevent the exception from being raised here so that the test can be ran in parallel + with contextlib.suppress(ValueError): + engagement = self.add_engagement_with_jira_project(expected_delta_jira_project_db=1) + # clearing out jira config used to be possible. what todo? + # - delete jira project? would disconnect all existing jira issues in defect dojo from the config? + # - allow jira project with empty jira instance and/or empty project_key? unpredictable behaviour + # - so prevent clearing out these values + # response = self.empty_jira_project_for_engagement(Engagement.objects.get(id=3), -1) + # expecting ValueError as we can't delete existing JIRA Projects + self.empty_jira_project_for_engagement(engagement, expected_delta_jira_project_db=0, expect_error=True) + self.assertEqual(jira_mock.call_count, 1) @patch('dojo.jira_link.views.jira_helper.is_jira_project_valid') def test_add_jira_project_to_engagement_without_jira_project_invalid_project(self, jira_mock): diff --git a/unittests/test_rest_framework.py b/unittests/test_rest_framework.py index ce1ad77da16..e543bdd7b01 100644 --- a/unittests/test_rest_framework.py +++ b/unittests/test_rest_framework.py @@ -9,6 +9,7 @@ from unittest.mock import ANY, MagicMock, call, patch from django.contrib.auth.models import Permission +from django.test import tag as test_tag from django.urls import reverse from drf_spectacular.drainage import GENERATOR_STATS from drf_spectacular.settings import spectacular_settings @@ -690,7 +691,6 @@ def test_delete_preview(self): response = self.client.get(relative_url) # print('delete_preview response.data') - self.assertEqual(200, response.status_code, response.content[:1000]) self.check_schema_response('get', '200', response, detail=True) @@ -957,6 +957,7 @@ def __init__(self, *args, **kwargs): self.permission_update = Permissions.Endpoint_Edit self.permission_delete = Permissions.Endpoint_Delete self.deleted_objects = 2 + self.delete_id = 6 BaseClass.RESTEndpointTest.__init__(self, *args, **kwargs) @@ -1183,6 +1184,7 @@ def __init__(self, *args, **kwargs): self.permission_update = Permissions.Finding_Edit self.permission_delete = Permissions.Finding_Delete self.deleted_objects = 2 + self.delete_id = 3 BaseClass.RESTEndpointTest.__init__(self, *args, **kwargs) def test_duplicate(self): @@ -1268,11 +1270,10 @@ def setUp(self): self.client = APIClient() self.client.credentials(HTTP_AUTHORIZATION='Token ' + token.key) self.url = reverse(self.viewname + '-list') - - self.current_findings = self.client.get(self.url, format='json').data["results"] - finding = Finding.objects.get(id=self.current_findings[0]['id']) - - self.base_url = f"{self.url}{self.current_findings[0]['id']}/metadata/" + self.finding_id = 3 + self.delete_id = self.finding_id + finding = Finding.objects.get(id=self.finding_id) + self.base_url = f"{self.url}{self.finding_id}/metadata/" metadata = DojoMeta(finding=finding, name="test_meta", value="20") metadata.save() @@ -1763,6 +1764,7 @@ def test_user_should_not_have_access_to_product_3_in_detail(self): self.assertEqual(response.status_code, 404) +@test_tag("non-parallel") class ImportScanTest(BaseClass.BaseClassTest): fixtures = ['dojo_testdata.json'] @@ -2664,6 +2666,7 @@ def __init__(self, *args, **kwargs): self.update_fields = {'color': 'blue'} self.test_type = TestType.CONFIGURATION_PERMISSIONS self.deleted_objects = 1 + self.delete_id = 3 BaseClass.RESTEndpointTest.__init__(self, *args, **kwargs) @@ -2695,6 +2698,7 @@ def __init__(self, *args, **kwargs): BaseClass.RESTEndpointTest.__init__(self, *args, **kwargs) +@test_tag("non-parallel") class ImportLanguagesTest(BaseClass.BaseClassTest): fixtures = ['dojo_testdata.json'] diff --git a/unittests/tools/test_deepfence_threatmapper_parser.py b/unittests/tools/test_deepfence_threatmapper_parser.py index 2dc584b2259..9e5aa0e1ee4 100644 --- a/unittests/tools/test_deepfence_threatmapper_parser.py +++ b/unittests/tools/test_deepfence_threatmapper_parser.py @@ -7,37 +7,37 @@ class TestDeepfenceThreatmapperParser(DojoTestCase): def test_parse_file_compliance_report(self): - testfile = open("unittests/scans/deepfence_threatmapper/compliance_report.xlsx", "rb") - parser = DeepfenceThreatmapperParser() - findings = parser.get_findings(testfile, Test()) - self.assertEqual(7, len(findings)) - self.assertEqual(findings[0].title, "Threatmapper_Compliance_Report-gdpr_3.6") - self.assertEqual(findings[0].severity, "Info") + with open("unittests/scans/deepfence_threatmapper/compliance_report.xlsx", "rb") as testfile: + parser = DeepfenceThreatmapperParser() + findings = parser.get_findings(testfile, Test()) + self.assertEqual(7, len(findings)) + self.assertEqual(findings[0].title, "Threatmapper_Compliance_Report-gdpr_3.6") + self.assertEqual(findings[0].severity, "Info") def test_parse_file_malware_report(self): - testfile = open("unittests/scans/deepfence_threatmapper/malware_report.xlsx", "rb") - parser = DeepfenceThreatmapperParser() - findings = parser.get_findings(testfile, Test()) - self.assertEqual(9, len(findings)) - self.assertEqual(findings[0].title, "MD5_Constants") - self.assertEqual(findings[0].severity, "Low") - self.assertEqual(findings[0].file_path, "/tmp/Deepfence/YaraHunter/df_db09257b02e615049e0aecc05be2dc2401735e67db4ab74225df777c62c39753/usr/sbin/mkfs.cramfs") + with open("unittests/scans/deepfence_threatmapper/malware_report.xlsx", "rb") as testfile: + parser = DeepfenceThreatmapperParser() + findings = parser.get_findings(testfile, Test()) + self.assertEqual(9, len(findings)) + self.assertEqual(findings[0].title, "MD5_Constants") + self.assertEqual(findings[0].severity, "Low") + self.assertEqual(findings[0].file_path, "/tmp/Deepfence/YaraHunter/df_db09257b02e615049e0aecc05be2dc2401735e67db4ab74225df777c62c39753/usr/sbin/mkfs.cramfs") def test_parse_file_secret_report(self): - testfile = open("unittests/scans/deepfence_threatmapper/secret_report.xlsx", "rb") - parser = DeepfenceThreatmapperParser() - findings = parser.get_findings(testfile, Test()) - self.assertEqual(7, len(findings)) - self.assertEqual(findings[0].title, "Username and password in URI") - self.assertEqual(findings[0].severity, "High") - self.assertEqual(findings[0].file_path, "usr/share/doc/curl-8.3.0/TheArtOfHttpScripting.md") + with open("unittests/scans/deepfence_threatmapper/secret_report.xlsx", "rb") as testfile: + parser = DeepfenceThreatmapperParser() + findings = parser.get_findings(testfile, Test()) + self.assertEqual(7, len(findings)) + self.assertEqual(findings[0].title, "Username and password in URI") + self.assertEqual(findings[0].severity, "High") + self.assertEqual(findings[0].file_path, "usr/share/doc/curl-8.3.0/TheArtOfHttpScripting.md") def test_parse_file_vulnerability_report(self): - testfile = open("unittests/scans/deepfence_threatmapper/vulnerability_report.xlsx", "rb") - parser = DeepfenceThreatmapperParser() - findings = parser.get_findings(testfile, Test()) - self.assertEqual(3, len(findings)) - self.assertEqual(findings[0].title, "Threatmapper_Vuln_Report-CVE-2021-36084") - self.assertEqual(findings[0].severity, "Low") - self.assertEqual(findings[0].mitigation, "2.5-10.amzn2.0.1") - self.assertEqual(findings[0].cve, "CVE-2021-36084") + with open("unittests/scans/deepfence_threatmapper/vulnerability_report.xlsx", "rb") as testfile: + parser = DeepfenceThreatmapperParser() + findings = parser.get_findings(testfile, Test()) + self.assertEqual(3, len(findings)) + self.assertEqual(findings[0].title, "Threatmapper_Vuln_Report-CVE-2021-36084") + self.assertEqual(findings[0].severity, "Low") + self.assertEqual(findings[0].mitigation, "2.5-10.amzn2.0.1") + self.assertEqual(findings[0].cve, "CVE-2021-36084") From 8700090b653700faf1f8ac678f9f3126cf7fc7cc Mon Sep 17 00:00:00 2001 From: Cody Maffucci <46459665+Maffooch@users.noreply.github.com> Date: Mon, 8 Jul 2024 08:44:33 -0500 Subject: [PATCH 09/11] Ruff: Address migrations, reduce redundancy, and remove Flake8 (#10494) --- .github/renovate.json | 2 +- .github/workflows/flake8.yml | 36 ------------------------------------ .github/workflows/ruff.yml | 20 +------------------- ruff.toml | 1 - 4 files changed, 2 insertions(+), 57 deletions(-) delete mode 100644 .github/workflows/flake8.yml diff --git a/.github/renovate.json b/.github/renovate.json index 7acaf0f30bc..221c87442e1 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -6,11 +6,11 @@ "dependencyDashboardApproval": false, "baseBranches": ["dev"], "rebaseWhen": "conflicted", + "ignorePaths": ["requirements.txt", "requirements-lint.txt", "components/package.json", "components/package-lock.json", "dojo/components/yarn.lock", "dojo/components/package.json", "Dockerfile**"], "ignoreDeps": [ "mysql", "rabbitmq" ], - "ignorePaths": ["requirements.txt", "components/package.json", "components/package-lock.json", "dojo/components/yarn.lock", "dojo/components/package.json", "Dockerfile**"], "packageRules": [{ "packagePatterns": ["*"], "commitMessageExtra": "from {{currentVersion}} to {{#if isMajor}}v{{{newMajor}}}{{else}}{{#if isSingleVersion}}v{{{toVersion}}}{{else}}{{{newValue}}}{{/if}}{{/if}}", diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml deleted file mode 100644 index 188b39430fc..00000000000 --- a/.github/workflows/flake8.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Flake8 -# pull requests: -# run on pull_request_target instead of just pull_request as we need write access to update the status check -on: - workflow_dispatch: - pull_request_target: - push: - -jobs: - flake8-your-pr: - runs-on: ubuntu-latest - steps: - - name: Checkout - if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target' - uses: actions/checkout@v4 - # by default the pull_requst_target event checks out the base branch, i.e. dev - # so we need to explicitly checkout the head of the PR - # we use fetch-depth 0 to make sure the full history is checked out and we can compare against - # the base commit (branch) of the PR - # more info https://github.community/t/github-actions-are-severely-limited-on-prs/18179/16 - # we checkout merge_commit here as this contains all new code from dev also. we don't need to compare against base_commit - with: - persist-credentials: false - fetch-depth: 0 - ref: refs/pull/${{ github.event.pull_request.number }}/merge - # repository: ${{github.event.pull_request.head.repo.full_name}} - - - name: Checkout - # for non PR runs we just checkout the default, which is a sha on a branch probably - if: github.event_name != 'pull_request' && github.event_name != 'pull_request_target' - uses: actions/checkout@v4 - - # - uses: tayfun/flake8-your-pr@master - - uses: DefectDojo/flake8-your-pr@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index 132ffa89db1..04799cdd003 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -2,31 +2,13 @@ name: Ruff Linter on: workflow_dispatch: - pull_request_target: push: - + pull_request: jobs: ruff-linting: runs-on: ubuntu-latest steps: - name: Checkout - if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target' - uses: actions/checkout@v4 - # by default the pull_requst_target event checks out the base branch, i.e. dev - # so we need to explicitly checkout the head of the PR - # we use fetch-depth 0 to make sure the full history is checked out and we can compare against - # the base commit (branch) of the PR - # more info https://github.community/t/github-actions-are-severely-limited-on-prs/18179/16 - # we checkout merge_commit here as this contains all new code from dev also. we don't need to compare against base_commit - with: - persist-credentials: false - fetch-depth: 0 - ref: refs/pull/${{ github.event.pull_request.number }}/merge - # repository: ${{github.event.pull_request.head.repo.full_name}} - - - name: Checkout - # for non PR runs we just checkout the default, which is a sha on a branch probably - if: github.event_name != 'pull_request' && github.event_name != 'pull_request_target' uses: actions/checkout@v4 - name: Install Ruff Linter diff --git a/ruff.toml b/ruff.toml index 1349d475e92..24454faa66a 100644 --- a/ruff.toml +++ b/ruff.toml @@ -39,7 +39,6 @@ select = [ "UP", "YTT", "ASYNC", - "TRIO", "S2", "S5", "S7", "C4", "T10", From 4c0004c22ff224c308814a56abc9d6ba01b66955 Mon Sep 17 00:00:00 2001 From: kiblik <5609770+kiblik@users.noreply.github.com> Date: Mon, 8 Jul 2024 18:19:05 +0200 Subject: [PATCH 10/11] fix(doc): Breaking Change for HELM deployments with PostgreSQL (#10524) --- docs/content/en/getting_started/upgrading/2.36.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/content/en/getting_started/upgrading/2.36.md b/docs/content/en/getting_started/upgrading/2.36.md index 260c86960de..ceaa8c77d14 100644 --- a/docs/content/en/getting_started/upgrading/2.36.md +++ b/docs/content/en/getting_started/upgrading/2.36.md @@ -2,6 +2,15 @@ title: 'Upgrading to DefectDojo Version 2.36.x' toc_hide: true weight: -20240603 -description: No special instructions. +description: Breaking Change for HELM deployments with PostgreSQL --- -There are no special instructions for upgrading to 2.36.x. Check the [Release Notes](https://github.com/DefectDojo/django-DefectDojo/releases/tag/2.36.0) for the contents of the release. + +Previous HELM deployments (HELM chart `<=1.6.136`, DefectDojo `<=2.35.4`) used a pinned version of PostgreSQL in versions `11.x`. These are incompatible with Django in version `4.2` (used from DefectDojo version `3.36.0`; HELM chart `1.6.137`). Because of this, it is necessary to upgrade PostgreSQL to version `12.x` or higher. DefectDojo in version `3.36.1` (HELM chart `1.6.138`) uses this new version of PostgreSQL. + +Unfortunately, an upgrade of PostgreSQL is not enough because PostgreSQL does not support automatic migration of data structures in the filesystem. Because of this, migration is needed. There are different ways (many of them similar to migration between different database backends (e.g. from MySQL to PostgreSQL)). Please find inspiration and the best fitting way for you in: + +- https://github.com/DefectDojo/django-DefectDojo/discussions/9480 +- https://owasp.slack.com/archives/C2P5BA8MN/p1717610931766739?thread_ts=1717587117.831149&cid=C2P5BA8MN +- https://dev.to/jkostolansky/how-to-upgrade-postgresql-from-11-to-12-2la6 + +There are no other special instructions for upgrading to 2.36.x. Check the [Release Notes](https://github.com/DefectDojo/django-DefectDojo/releases/tag/2.36.0) for the contents of the release. From 4c9fe39a24afa96c0cdd6c2a2c28932e4bc354dd Mon Sep 17 00:00:00 2001 From: DefectDojo release bot Date: Mon, 8 Jul 2024 17:19:09 +0000 Subject: [PATCH 11/11] Update versions in application files --- components/package.json | 2 +- dojo/__init__.py | 2 +- helm/defectdojo/Chart.yaml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/package.json b/components/package.json index ab3201e6a41..05ce9098364 100644 --- a/components/package.json +++ b/components/package.json @@ -1,6 +1,6 @@ { "name": "defectdojo", - "version": "2.37.0-dev", + "version": "2.36.1", "license" : "BSD-3-Clause", "private": true, "dependencies": { diff --git a/dojo/__init__.py b/dojo/__init__.py index 707177ee3ee..27272ee3270 100644 --- a/dojo/__init__.py +++ b/dojo/__init__.py @@ -4,6 +4,6 @@ # Django starts so that shared_task will use this app. from .celery import app as celery_app # noqa: F401 -__version__ = '2.37.0-dev' +__version__ = '2.36.1' __url__ = 'https://github.com/DefectDojo/django-DefectDojo' __docs__ = 'https://documentation.defectdojo.com' diff --git a/helm/defectdojo/Chart.yaml b/helm/defectdojo/Chart.yaml index ab21a0409cb..b982afd9d77 100644 --- a/helm/defectdojo/Chart.yaml +++ b/helm/defectdojo/Chart.yaml @@ -1,8 +1,8 @@ apiVersion: v2 -appVersion: "2.37.0-dev" +appVersion: "2.36.1" description: A Helm chart for Kubernetes to install DefectDojo name: defectdojo -version: 1.6.138-dev +version: 1.6.138 icon: https://www.defectdojo.org/img/favicon.ico maintainers: - name: madchap