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")