• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Brunowar12 / TaskManagerSystem / 14985374433

13 May 2025 12:25AM UTC coverage: 93.727%. First build
14985374433

push

github

web-flow
Polishing & refinement (#22)

* update

+ optimized categories, security, tasks and users tests
+ api status version modified
+ custom middleware for control large request

* update

project update:
+ role permissions dictionary
+ dynamic api version update for urls
+ use error_response util
for users app intregrated error_response util
tasks:
+ split the responsibility of the save method (model)
+ reduce move task to proj service
+ integrate error_response ta status_response
+ changes for project logic
api:
+ adding global mixins

* projects update

+ post_migrate creating roles
+ fixed roles
+ clean method for role
+ is_active field for sharelink
+ settings for sharelink
+ new classes for check roles in permissions
+ description field in serializer
+ reduced project service
+ created share link service with validation and create share link methods
+ transaction atomic for assign role
+ nested router for access tasks crud in project instead of tasks actions in projects
+ roles settings
+ now user can be invited in project only by share link
+ generate sharelink reduce
+ kick method
+ roleviewset and projectmembershipviewset now only readonly
+ atomic transaction for join project

* tests update

+ permission tests update (+4 new tests)
+ fixed task test
+ a very extensive work on test projects, almost everything's been remodeled.

* feat

+ gh actions upload to coveralls
+ new updated readme file
+ swagger\redoc api documentation

* fix

+ api_status with no permissions
+ requirements

* feat

+ split userviewset
+ auth endpoint become account

* Update test_setup.py

* feat projects

+ deleted permissionviewset
+ serializer for kick user action and assign role for correct swagger
+ endpoints fixed from projects/projects/

* fix

* feat, fix

+ feat urls for projects, tasks
+ new serializers for tasks actions

699 of 735 new or added lines in 31 files covered. (95.1%)

1509 of 1610 relevant lines covered (93.73%)

9.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.97
/api/tests/test_security.py
1
from datetime import timedelta
10✔
2
from django.urls import reverse
10✔
3
from rest_framework import status
10✔
4
from rest_framework_simplejwt.tokens import AccessToken
10✔
5

6
from .test_setup import BaseAPITestCase
10✔
7
from .utils import TestHelper
10✔
8

9

10
class SecurityTests(BaseAPITestCase):
10✔
11
    """Tests for security measures"""
12

13
    def setUp(self):
10✔
14
        super().setUp()
10✔
15
        self.other_user, self.other_user_token, _ = (
10✔
16
            TestHelper.create_test_user_via_orm(
17
                email="other@example.com", password="otherpassword123"
18
            )
19
        )
20

21
    def test_unauthorized_access(self):
10✔
22
        """Check access to protected resources without authorization"""
23
        self.client.credentials()
10✔
24
        response = self.client.get(self.task_list_ep)
10✔
25

26
        self.assertEqual(
10✔
27
            response.status_code,
28
            status.HTTP_401_UNAUTHORIZED,
29
            "Access without token should be denied",
30
        )
31

32
    def test_invalid_json_payload(self):
10✔
33
        """Check for incorrect JSON processing"""
34
        response = self.client.post(
10✔
35
            self.task_list_ep,
36
            data="Invalid JSON",
37
            content_type="application/json",
38
        )
39

40
        self.assertEqual(
10✔
41
            response.status_code,
42
            status.HTTP_400_BAD_REQUEST,
43
            "Invalid JSON payload should return 400",
44
        )
45
        self.assertIn(
10✔
46
            "detail",
47
            response.json(),
48
            "Error message should include 'detail' field",
49
        )
50

51
    def test_large_payload_rejected(self):
10✔
52
        """Payload exceeding the size limit should return 413 Payload Too Large"""
53
        large_data = {
10✔
54
            "title": "X" * (1024 * 1024 * 25 + 1),
55
            "due_date": TestHelper.get_valid_due_date(),
56
        }
57
        response = self.api_post(self.task_list_ep, large_data)
10✔
58

59
        self.assertEqual(
10✔
60
            response.status_code,
61
            status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
62
            "Payload exceeding the limit should return 413 Payload Too Large",
63
        )
64

65
    def test_nonexistent_endpoint(self):
10✔
66
        """Checking the response to a request for a non-existent route"""
67
        response = self.client.get("/nonexistent-endpoint/")
10✔
68

69
        self.assertEqual(
10✔
70
            response.status_code,
71
            status.HTTP_404_NOT_FOUND,
72
            "Nonexistent endpoint should return 404",
73
        )
74

75
    def test_sql_injection(self):
10✔
76
        """SQL injection attempts should be rejected with 400 Bad Request"""
77
        malicious_input = "'; DROP TABLE tasks_task; --"
10✔
78
        response = self.api_post(self.task_list_ep, {
10✔
79
            "title": malicious_input, 
80
            "due_date": TestHelper.get_valid_due_date()
81
        })
82

83
        self.assertEqual(
10✔
84
            response.status_code,
85
            status.HTTP_400_BAD_REQUEST,
86
            "SQL injection payload should be rejected with 400 Bad Request",
87
        )
88
        self.assertIn(
10✔
89
            "title",
90
            response.json(),
91
            "Error response should include validation error for 'title'",
92
        )
93

94
    def test_xss_prevention(self):
10✔
95
        """Check for protection against XSS attacks"""
96
        malicious_input = '<script>alert("XSS")</script>'
10✔
97
        response = self.api_post(
10✔
98
            self.task_list_ep, {
99
                "title": malicious_input,
100
                "due_date": TestHelper.get_valid_due_date(),
101
        })
102
        data_json = response.json()
10✔
103
        self.assertEqual(
10✔
104
            response.status_code,
105
            status.HTTP_400_BAD_REQUEST,
106
            "XSS payload should be rejected by validation",
107
        )
108
        
109
        title = data_json.get("title")
10✔
110
        self.assertNotIn(
10✔
111
            "<script>",
112
            title,
113
            "Title should have scripts stripped out"
114
        )
115

116
        task_id = data_json.get("id")
10✔
117
        if task_id:
10✔
NEW
118
            response = self.client.get(
×
119
                reverse("task-detail", kwargs={"pk": task_id})
120
            )
NEW
121
            self.assertNotIn(
×
122
                "<script>",
123
                response.json().get("title"),
124
                "Retrieved title should be sanitized as well"
125
            )
126

127
    def test_http_methods_security(self):
10✔
128
        """Checking the unavailability of prohibited HTTP methods"""
129
        for method in ("put", "delete", "patch"):
10✔
130
            response = getattr(self.client, method)(self.task_list_ep)
10✔
131
            self.assertEqual(
10✔
132
                response.status_code,
133
                status.HTTP_405_METHOD_NOT_ALLOWED,
134
                f"{method.upper()} on task-list should return 405 Method Not Allowed",
135
            )
136

137
    def test_expired_token(self):
10✔
138
        """Create an expired token and test"""
139
        token = AccessToken()
10✔
140
        token.set_exp(lifetime=-timedelta(seconds=1))
10✔
141

142
        self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {str(token)}")
10✔
143

144
        response = self.client.get(self.task_list_ep)
10✔
145

146
        self.assertEqual(
10✔
147
            response.status_code,
148
            status.HTTP_401_UNAUTHORIZED,
149
            "Expired access token should be rejected (401 Unauthorized)",
150
        )
151
        self.assertIn(
10✔
152
            "detail",
153
            response.json(),
154
            "Error response should include 'detail' field",
155
        )
156

157
    def test_revoked_refresh_token(self):
10✔
158
        """A revoked refresh token should not be accepted by token refresh endpoint"""
159
        from json import dumps as jdumps
10✔
160
        from rest_framework_simplejwt.tokens import RefreshToken
10✔
161

162
        refresh = RefreshToken.for_user(self.user)
10✔
163

164
        # Revoke via logout endpoint
165
        self.client.post(
10✔
166
            self.user_logout_ep,
167
            data=jdumps({"refresh": str(refresh)}),
168
            content_type="application/json",
169
        )
170

171
        # Attempt to refresh using revoked token
172
        response = self.client.post(
10✔
173
            self.token_refresh_ep,
174
            data=jdumps({"refresh": str(refresh)}),
175
            content_type="application/json",
176
        )
177

178
        self.assertEqual(
10✔
179
            response.status_code,
180
            status.HTTP_401_UNAUTHORIZED,
181
            "Revoked refresh token should be rejected (401 Unauthorized)",
182
        )
183
        self.assertEqual(
10✔
184
            response.json().get("detail"),
185
            "Token is blacklisted",
186
            "Error message should indicate the token is blacklisted",
187
        )
188

189
    def test_access_other_user_resources(self):
10✔
190
        """Users should not access tasks belonging to other users"""
191

192
        # Create a task as the original user
193
        task = self.api_post(self.task_list_ep, {
10✔
194
                "title": "User Task",
195
                "due_date": TestHelper.get_valid_due_date()
196
            }).json()
197
        self.assertIn("id", task, "Task should be successfully created")
10✔
198

199
        # Attempt to GET the task with another user's token
200
        self.client.credentials(
10✔
201
            HTTP_AUTHORIZATION=f"Bearer {self.other_user_token}"
202
        )
203
        response = self.client.get(
10✔
204
            reverse("task-detail", kwargs={"pk": task["id"]})
205
        )
206

207
        self.assertEqual(
10✔
208
            response.status_code,
209
            status.HTTP_404_NOT_FOUND,
210
            "Should not access tasks of other users (404 Not Found)",
211
        )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc