diff --git a/dojo/api_v2/permissions.py b/dojo/api_v2/permissions.py index 10e012ede5b..3bf206d2f76 100644 --- a/dojo/api_v2/permissions.py +++ b/dojo/api_v2/permissions.py @@ -455,6 +455,23 @@ class UserHasFindingNotePermission(BaseRelatedObjectPermission): } +class UserHasBurpRawRequestResponsePermission(permissions.BasePermission): + def has_permission(self, request, view): + return check_post_permission( + request, Finding, "finding", Permissions.Finding_Edit, + ) + + def has_object_permission(self, request, view, obj): + return check_object_permission( + request, + obj.finding, + Permissions.Finding_View, + Permissions.Finding_Edit, + Permissions.Finding_Edit, + Permissions.Finding_Edit, + ) + + class UserHasImportPermission(permissions.BasePermission): def has_permission(self, request, view): # permission check takes place before validation, so we don't have access to serializer.validated_data() diff --git a/dojo/api_v2/views.py b/dojo/api_v2/views.py index 9e081d59696..bd61d76ee2a 100644 --- a/dojo/api_v2/views.py +++ b/dojo/api_v2/views.py @@ -3011,7 +3011,7 @@ class BurpRawRequestResponseViewSet( filterset_fields = ["finding"] permission_classes = ( IsAuthenticated, - permissions.UserHasFindingRelatedObjectPermission, + permissions.UserHasBurpRawRequestResponsePermission, ) def get_queryset(self): diff --git a/unittests/test_rest_framework.py b/unittests/test_rest_framework.py index ddaa59a4549..43483a13e3d 100644 --- a/unittests/test_rest_framework.py +++ b/unittests/test_rest_framework.py @@ -1788,6 +1788,67 @@ def test_request_response_get(self): self.assertEqual(200, response.status_code, response.content[:1000]) +@versioned_fixtures +class RequestResponsePairsAuthzTest(DojoAPITestCase): + + fixtures = ["dojo_testdata.json"] + + def _client_for(self, username): + user = User.objects.get(username=username) + token = Token.objects.get(user=user) + client = APIClient() + client.credentials(HTTP_AUTHORIZATION="Token " + token.key) + return client + + def test_admin_can_create_request_response_pair_positive_control(self): + client = self._client_for("admin") + before = BurpRawRequestResponse.objects.filter(finding_id=7).count() + response = client.post( + "/api/v2/request_response_pairs/", + dumps({ + "finding": 7, + "burpRequestBase64": "cmVxdWVzdAo=", + "burpResponseBase64": "cmVzcG9uc2UK", + }), + content_type="application/json", + ) + self.assertEqual(response.status_code, 201, response.content[:1000]) + self.assertEqual(BurpRawRequestResponse.objects.filter(finding_id=7).count(), before + 1) + + def test_unrelated_user_cannot_create_request_response_pair_on_hidden_finding(self): + client = self._client_for("user2") + # Sanity: the victim finding is genuinely hidden from this user. + get_response = client.get("/api/v2/findings/7/") + self.assertEqual(get_response.status_code, 404) + + before = BurpRawRequestResponse.objects.filter(finding_id=7).count() + response = client.post( + "/api/v2/request_response_pairs/", + dumps({ + "finding": 7, + "burpRequestBase64": "cmVxdWVzdAo=", + "burpResponseBase64": "cmVzcG9uc2UK", + }), + content_type="application/json", + ) + self.assertIn(response.status_code, (403, 404), response.content[:1000]) + self.assertEqual(BurpRawRequestResponse.objects.filter(finding_id=7).count(), before) + + def test_post_without_finding_returns_4xx(self): + client = self._client_for("user2") + response = client.post( + "/api/v2/request_response_pairs/", + dumps({ + "burpRequestBase64": "cmVxdWVzdAo=", + "burpResponseBase64": "cmVzcG9uc2UK", + }), + content_type="application/json", + ) + # check_post_permission raises ParseError (400) when "finding" is omitted. + self.assertGreaterEqual(response.status_code, 400) + self.assertLess(response.status_code, 500) + + @versioned_fixtures class FilesTest(DojoAPITestCase): fixtures = ["dojo_testdata.json"]