diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index 8b817b3f2..c7391e2f1 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -2037,10 +2037,10 @@ class BulkEditSerializer( raise serializers.ValidationError("remove_custom_fields not specified") def _validate_owner(self, owner): - ownerUser = User.objects.get(pk=owner) - if ownerUser is None: + try: + return User.objects.get(pk=owner) + except User.DoesNotExist: raise serializers.ValidationError("Specified owner cannot be found") - return ownerUser def _validate_parameters_set_permissions(self, parameters) -> None: if "set_permissions" not in parameters: @@ -2060,7 +2060,7 @@ class BulkEditSerializer( or not float(parameters["degrees"]).is_integer() ): raise serializers.ValidationError("invalid rotation degrees") - except ValueError: + except (TypeError, ValueError): raise serializers.ValidationError("invalid rotation degrees") def _validate_source_mode(self, parameters) -> None: @@ -2073,6 +2073,8 @@ class BulkEditSerializer( def _validate_parameters_split(self, parameters) -> None: if "pages" not in parameters: raise serializers.ValidationError("pages not specified") + if not isinstance(parameters["pages"], str): + raise serializers.ValidationError("invalid pages specified") try: pages = [] docs = parameters["pages"].split(",") diff --git a/src/documents/tests/test_api_bulk_edit.py b/src/documents/tests/test_api_bulk_edit.py index 714e1bcfd..2009694e3 100644 --- a/src/documents/tests/test_api_bulk_edit.py +++ b/src/documents/tests/test_api_bulk_edit.py @@ -1165,6 +1165,33 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase): self.assertIn(b"set_permissions not specified", response.content) m.assert_not_called() + @mock.patch("documents.serialisers.bulk_edit.set_permissions") + def test_set_permissions_rejects_nonexistent_owner(self, m) -> None: + """ + BulkEditSerializer._validate_owner called User.objects.get(pk=owner) + with no try/except, so a syntactically valid but nonexistent user + id raised an uncaught User.DoesNotExist instead of a clean 400. + """ + self.setup_mock(m, "set_permissions") + + response = self.client.post( + "/api/documents/bulk_edit/", + json.dumps( + { + "documents": [self.doc2.id], + "method": "set_permissions", + "parameters": { + "set_permissions": {"view": {"users": [self.user.id]}}, + "owner": 999999, + }, + }, + ), + content_type="application/json", + ) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + m.assert_not_called() + @mock.patch("documents.serialisers.bulk_edit.set_permissions") def test_set_permissions_merge(self, m) -> None: self.setup_mock(m, "set_permissions") @@ -1438,6 +1465,53 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase): self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) m.assert_not_called() + @mock.patch("documents.serialisers.bulk_edit.rotate") + def test_bulk_edit_rotate_rejects_null_degrees(self, m) -> None: + """ + BulkEditSerializer._validate_parameters_rotate's + `float(parameters["degrees"])` raised an uncaught TypeError for + None (only ValueError was caught), reachable via the legacy + generic /api/documents/bulk_edit/ method="rotate" path (the + dedicated /api/documents/rotate/ endpoint isn't affected, its + `degrees` field is a typed IntegerField). + """ + self.setup_mock(m, "rotate") + response = self.client.post( + "/api/documents/bulk_edit/", + json.dumps( + { + "documents": [self.doc2.id], + "method": "rotate", + "parameters": {"degrees": None}, + }, + ), + content_type="application/json", + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + m.assert_not_called() + + @mock.patch("documents.serialisers.bulk_edit.split") + def test_bulk_edit_split_rejects_null_pages(self, m) -> None: + """ + BulkEditSerializer._validate_parameters_split called + parameters["pages"].split(",") with no type check, so a null + value raised an uncaught AttributeError instead of a clean 400. + """ + self.setup_mock(m, "split") + response = self.client.post( + "/api/documents/bulk_edit/", + json.dumps( + { + "documents": [self.doc2.id], + "method": "split", + "parameters": {"pages": None}, + }, + ), + content_type="application/json", + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + m.assert_not_called() + @mock.patch("documents.views.bulk_edit.rotate") def test_rotate_insufficient_permissions(self, m) -> None: self.doc1.owner = User.objects.get(username="temp_admin")