Skip to content

Commit d9ebb93

Browse files
fix tests, cleanup
1 parent 1e276e3 commit d9ebb93

7 files changed

Lines changed: 117 additions & 57 deletions

File tree

dojo/api_v2/serializers.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@
118118
requires_tool_type,
119119
)
120120
from dojo.user.utils import get_configuration_permissions_codenames
121-
from dojo.utils import is_scan_file_too_large, tag_validator
121+
from dojo.utils import is_scan_file_too_large
122+
from dojo.validators import tag_validator
122123

123124
logger = logging.getLogger(__name__)
124125
deduplicationLogger = logging.getLogger("dojo.specific-loggers.deduplication")
@@ -1840,8 +1841,6 @@ def validate(self, data):
18401841
# doing it here instead of in update because update doesn't know if the value changed
18411842
self.process_risk_acceptance(data)
18421843

1843-
# cvss3_validator(data.get("cvssv3"), exception_class=RestFrameworkValidationError)
1844-
18451844
return data
18461845

18471846
def validate_severity(self, value: str) -> str:
@@ -1971,8 +1970,6 @@ def validate(self, data):
19711970
msg = "Active findings cannot be risk accepted."
19721971
raise serializers.ValidationError(msg)
19731972

1974-
# cvss3_validator(data.get("cvssv3"), exception_class=RestFrameworkValidationError)
1975-
19761973
return data
19771974

19781975
def validate_severity(self, value: str) -> str:
@@ -1999,7 +1996,6 @@ class Meta:
19991996
exclude = ("cve",)
20001997

20011998
def create(self, validated_data):
2002-
# cvss3_validator(validated_data.get("cvssv3"), exception_class=RestFrameworkValidationError)
20031999

20042000
to_be_tagged, validated_data = self._pop_tags(validated_data)
20052001

@@ -2028,8 +2024,6 @@ def create(self, validated_data):
20282024
return new_finding_template
20292025

20302026
def update(self, instance, validated_data):
2031-
# cvss3_validator(validated_data.get("cvssv3"), exception_class=RestFrameworkValidationError)
2032-
20332027
# Save vulnerability ids and pop them
20342028
if "vulnerability_id_template_set" in validated_data:
20352029
vulnerability_id_set = validated_data.pop(

dojo/forms.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@
110110
get_system_setting,
111111
is_finding_groups_enabled,
112112
is_scan_file_too_large,
113-
tag_validator,
114113
)
114+
from dojo.validators import tag_validator
115115
from dojo.widgets import TableCheckboxWidget
116116

117117
logger = logging.getLogger(__name__)

dojo/utils.py

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
from django.conf import settings
2626
from django.contrib import messages
2727
from django.contrib.auth.signals import user_logged_in, user_logged_out, user_login_failed
28-
from django.core.exceptions import ValidationError
2928
from django.core.paginator import Paginator
3029
from django.db.models import Case, Count, IntegerField, Q, Sum, Value, When
3130
from django.db.models.query import QuerySet
@@ -2655,20 +2654,3 @@ def generate_file_response_from_file_path(
26552654
response["Content-Disposition"] = f'attachment; filename="{full_file_name}"'
26562655
response["Content-Length"] = file_size
26572656
return response
2658-
2659-
2660-
def tag_validator(value: str | list[str], exception_class: Callable = ValidationError) -> None:
2661-
TAG_PATTERN = re.compile(r'[ ,\'"]')
2662-
error_messages = []
2663-
2664-
if isinstance(value, list):
2665-
error_messages.extend(f"Invalid tag: '{tag}'. Tags should not contain spaces, commas, or quotes." for tag in value if TAG_PATTERN.search(tag))
2666-
elif isinstance(value, str):
2667-
if TAG_PATTERN.search(value):
2668-
error_messages.append(f"Invalid tag: '{value}'. Tags should not contain spaces, commas, or quotes.")
2669-
else:
2670-
error_messages.append(f"Value must be a string or list of strings: {value} - {type(value)}.")
2671-
2672-
if error_messages:
2673-
logger.debug(f"Tag validation failed: {error_messages}")
2674-
raise exception_class(error_messages)

dojo/validators.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
import re
23
from collections.abc import Callable
34

45
import cvss.parser
@@ -8,6 +9,23 @@
89
logger = logging.getLogger(__name__)
910

1011

12+
def tag_validator(value: str | list[str], exception_class: Callable = ValidationError) -> None:
13+
TAG_PATTERN = re.compile(r'[ ,\'"]')
14+
error_messages = []
15+
16+
if isinstance(value, list):
17+
error_messages.extend(f"Invalid tag: '{tag}'. Tags should not contain spaces, commas, or quotes." for tag in value if TAG_PATTERN.search(tag))
18+
elif isinstance(value, str):
19+
if TAG_PATTERN.search(value):
20+
error_messages.append(f"Invalid tag: '{value}'. Tags should not contain spaces, commas, or quotes.")
21+
else:
22+
error_messages.append(f"Value must be a string or list of strings: {value} - {type(value)}.")
23+
24+
if error_messages:
25+
logger.debug(f"Tag validation failed: {error_messages}")
26+
raise exception_class(error_messages)
27+
28+
1129
def cvss3_validator(value: str | list[str], exception_class: Callable = ValidationError) -> None:
1230
logger.error("cvss3_validator called with value: %s", value)
1331
cvss_vectors = cvss.parser.parse_cvss_from_text(value)
@@ -31,5 +49,5 @@ def cvss3_validator(value: str | list[str], exception_class: Callable = Validati
3149

3250
# Explicitly raise an error if no CVSS vectors are found,
3351
# to avoid 'NoneType' errors during severity processing later.
34-
msg = "No CVSS vectors found by cvss.parse_cvss_from_text()"
52+
msg = "No valid CVSS vectors found by cvss.parse_cvss_from_text()"
3553
raise exception_class(msg)

tests/finding_test.py

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ def test_edit_finding(self):
112112
driver.find_element(By.ID, "dropdownMenu1").click()
113113
# Click on `Edit Finding`
114114
driver.find_element(By.LINK_TEXT, "Edit Finding").click()
115-
# Change: 'Severity' and 'cvssv3'
116115
# finding Severity
117116
Select(driver.find_element(By.ID, "id_severity")).select_by_visible_text("Critical")
118117
# finding Vulnerability Ids
@@ -165,6 +164,8 @@ def _edit_finding_cvssv3_and_assert(
165164
self.assertEqual(str(expected_cvssv3_score), driver.find_element(By.ID, "id_cvssv3_score").get_attribute("value"))
166165
else:
167166
self.assertTrue(self.is_error_message_present(text=error_message))
167+
self.assertEqual(expected_cvssv3_value, driver.find_element(By.ID, "id_cvssv3").get_attribute("value"))
168+
self.assertEqual(str(expected_cvssv3_score), driver.find_element(By.ID, "id_cvssv3_score").get_attribute("value"))
168169

169170
# See https://github.com/DefectDojo/django-DefectDojo/issues/8264
170171
# Capturing current behavior which might not be the desired one yet
@@ -184,8 +185,9 @@ def test_edit_finding_cvssv3_valid_vector_no_prefix(self):
184185
cvssv3_value="AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
185186
cvssv3_score="2",
186187
expected_cvssv3_value="AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
187-
expected_cvssv3_score="2.0",
188-
expect_success=True,
188+
expected_cvssv3_score="2",
189+
expect_success=False,
190+
error_message="No valid CVSS vectors found by cvss.parse_cvss_from_text()",
189191
)
190192

191193
@on_exception_html_source_logger
@@ -194,29 +196,53 @@ def test_edit_finding_cvssv3_valid_vector_with_trailing_slash(self):
194196
cvssv3_value="CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H/",
195197
cvssv3_score="3",
196198
expected_cvssv3_value="CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H/",
197-
expected_cvssv3_score="3.0",
198-
expect_success=True,
199+
expected_cvssv3_score="3",
200+
expect_success=False,
201+
error_message="No valid CVSS vectors found by cvss.parse_cvss_from_text()",
199202
)
200203

201204
@on_exception_html_source_logger
202-
def test_edit_finding_cvssv3_with_v2_vector(self):
205+
def test_edit_finding_cvssv3_with_v2_vector_invalid_due_to_prefix(self):
203206
self._edit_finding_cvssv3_and_assert(
204207
cvssv3_value="CVSS:2.0/AV:N/AC:L/Au:N/C:P/I:P/A:P",
205208
cvssv3_score="4",
206209
expected_cvssv3_value="CVSS:2.0/AV:N/AC:L/Au:N/C:P/I:P/A:P",
207-
expected_cvssv3_score="4.0",
208-
expect_success=True,
210+
expected_cvssv3_score="4",
211+
expect_success=False,
212+
error_message="No valid CVSS vectors found by cvss.parse_cvss_from_text()",
213+
)
214+
215+
@on_exception_html_source_logger
216+
def test_edit_finding_cvssv3_with_v2_vector(self):
217+
self._edit_finding_cvssv3_and_assert(
218+
cvssv3_value="AV:N/AC:L/Au:N/C:P/I:P/A:P",
219+
cvssv3_score="4",
220+
expected_cvssv3_value="AV:N/AC:L/Au:N/C:P/I:P/A:P",
221+
expected_cvssv3_score="4",
222+
expect_success=False,
223+
error_message="Unsupported CVSS(2) version detected.",
224+
)
225+
226+
@on_exception_html_source_logger
227+
def test_edit_finding_cvssv3_with_v4_vector(self):
228+
self._edit_finding_cvssv3_and_assert(
229+
cvssv3_value="CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/S:U/C:H/I:H/A:H",
230+
cvssv3_score="5",
231+
expected_cvssv3_value="CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/S:U/C:H/I:H/A:H",
232+
expected_cvssv3_score="5",
233+
expect_success=False,
234+
error_message="No valid CVSS vectors found by cvss.parse_cvss_from_text()",
209235
)
210236

211237
@on_exception_html_source_logger
212238
def test_edit_finding_cvssv3_with_rubbish(self):
213239
self._edit_finding_cvssv3_and_assert(
214240
cvssv3_value="happy little vector",
215-
cvssv3_score="4",
216-
expected_cvssv3_value=None,
217-
expected_cvssv3_score=None,
241+
cvssv3_score="5",
242+
expected_cvssv3_value="happy little vector",
243+
expected_cvssv3_score="5",
218244
expect_success=False,
219-
error_message="CVSS must be entered in format: 'AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H'",
245+
error_message="No valid CVSS vectors found by cvss.parse_cvss_from_text()",
220246
)
221247

222248
def test_add_image(self):
@@ -611,6 +637,8 @@ def add_finding_tests_to_suite(suite, *, jira=False, github=False, block_executi
611637
suite.addTest(FindingTest("test_edit_finding_cvssv3_valid_vector_no_prefix"))
612638
suite.addTest(FindingTest("test_edit_finding_cvssv3_valid_vector_with_trailing_slash"))
613639
suite.addTest(FindingTest("test_edit_finding_cvssv3_with_v2_vector"))
640+
suite.addTest(FindingTest("test_edit_finding_cvssv3_with_v2_vector_invalid_due_to_prefix"))
641+
suite.addTest(FindingTest("test_edit_finding_cvssv3_with_v4_vector"))
614642
suite.addTest(FindingTest("test_edit_finding_cvssv3_with_rubbish"))
615643
suite.addTest(FindingTest("test_add_note_to_finding"))
616644
suite.addTest(FindingTest("test_add_image"))

unittests/test_finding_model.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ def test_get_references_with_links_markdown(self):
318318

319319
# See https://github.com/DefectDojo/django-DefectDojo/issues/8264
320320
# Capturing current behavior which might not be the desired one yet
321+
# This test saves vectors without any validation. This is capturing current behavior.
321322
def test_cvssv3(self):
322323
"""Tests if the CVSSv3 score is calculated correctly"""
323324
user, _ = User.objects.get_or_create(username="admin")

unittests/test_rest_framework.py

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,48 +1281,85 @@ def test_severity_validation(self):
12811281
self.assertEqual(result.status_code, status.HTTP_400_BAD_REQUEST, "Severity just got set to something invalid")
12821282
self.assertEqual(result.json()["severity"], ["Severity must be one of the following: ['Info', 'Low', 'Medium', 'High', 'Critical']"])
12831283

1284-
# See https://github.com/DefectDojo/django-DefectDojo/issues/8264
12851284
def test_cvss3_validation(self):
12861285
with self.subTest(i=0):
12871286
self.assertEqual(None, Finding.objects.get(id=2).cvssv3)
1288-
result = self.client.patch(self.url + "2/", data={"cvssv3": "CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H"})
1287+
result = self.client.patch(self.url + "2/", data={"cvssv3": "CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", "cvssv3_score": 3})
12891288
self.assertEqual(result.status_code, status.HTTP_200_OK)
1290-
self.assertEqual("CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", Finding.objects.get(id=2).cvssv3)
1289+
finding = Finding.objects.get(id=2)
1290+
# valid so vector must be set and score calculated
1291+
self.assertEqual("CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", finding.cvssv3)
1292+
self.assertEqual(8.8, finding.cvssv3_score)
12911293

12921294
with self.subTest(i=1):
12931295
# extra slash makes it invalid
1294-
result = self.client.patch(self.url + "3/", data={"cvssv3": "CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H/"})
1296+
result = self.client.patch(self.url + "3/", data={"cvssv3": "CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H/", "cvssv3_score": 3})
12951297
self.assertEqual(result.status_code, status.HTTP_400_BAD_REQUEST)
1296-
self.assertEqual(result.json()["cvssv3"], ["No CVSS vectors found by cvss.parse_cvss_from_text()"])
1297-
self.assertEqual(None, Finding.objects.get(id=3).cvssv3)
1298+
finding = Finding.objects.get(id=3)
1299+
self.assertEqual(result.json()["cvssv3"], ["No valid CVSS vectors found by cvss.parse_cvss_from_text()"])
1300+
# invalid vector, so no calculated score and no score stored
1301+
self.assertEqual(None, finding.cvssv3)
1302+
self.assertEqual(None, finding.cvssv3_score)
12981303

12991304
with self.subTest(i=2):
13001305
# no CVSS version prefix makes it invalid
1301-
result = self.client.patch(self.url + "3/", data={"cvssv3": "AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H"})
1306+
result = self.client.patch(self.url + "3/", data={"cvssv3": "AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", "cvssv3_score": 4})
13021307
self.assertEqual(result.status_code, status.HTTP_400_BAD_REQUEST)
1303-
self.assertEqual(result.json()["cvssv3"], ["No CVSS vectors found by cvss.parse_cvss_from_text()"])
1304-
self.assertEqual(None, Finding.objects.get(id=3).cvssv3)
1308+
finding = Finding.objects.get(id=3)
1309+
self.assertEqual(result.json()["cvssv3"], ["No valid CVSS vectors found by cvss.parse_cvss_from_text()"])
1310+
# invalid vector, so no calculated score and no score stored
1311+
self.assertEqual(None, finding.cvssv3)
1312+
self.assertEqual(None, finding.cvssv3_score)
13051313

13061314
with self.subTest(i=3):
13071315
# CVSS4 version makes it invalid
1308-
result = self.client.patch(self.url + "3/", data={"cvssv3": "CVSS:4.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H"})
1316+
result = self.client.patch(self.url + "3/", data={"cvssv3": "CVSS:4.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", "cvssv3_score": 5})
13091317
self.assertEqual(result.status_code, status.HTTP_400_BAD_REQUEST)
1310-
self.assertEqual(result.json()["cvssv3"], ["No CVSS vectors found by cvss.parse_cvss_from_text()"])
1311-
self.assertEqual(None, Finding.objects.get(id=3).cvssv3)
1318+
self.assertEqual(result.json()["cvssv3"], ["No valid CVSS vectors found by cvss.parse_cvss_from_text()"])
1319+
finding = Finding.objects.get(id=3)
1320+
# invalid vector, so no calculated score and no score stored
1321+
self.assertEqual(None, finding.cvssv3)
1322+
self.assertEqual(None, finding.cvssv3_score)
13121323

13131324
with self.subTest(i=4):
13141325
# CVSS2 style vector makes not supported
1315-
result = self.client.patch(self.url + "3/", data={"cvssv3": "AV:N/AC:L/Au:N/C:P/I:P/A:P"})
1326+
result = self.client.patch(self.url + "3/", data={"cvssv3": "AV:N/AC:L/Au:N/C:P/I:P/A:P", "cvssv3_score": 6})
13161327
self.assertEqual(result.status_code, status.HTTP_400_BAD_REQUEST)
13171328
self.assertEqual(result.json()["cvssv3"], ["Unsupported CVSS(2) version detected."])
1318-
self.assertEqual(None, Finding.objects.get(id=3).cvssv3)
1329+
finding = Finding.objects.get(id=3)
1330+
# invalid vector, so no calculated score and no score stored
1331+
self.assertEqual(None, finding.cvssv3)
1332+
self.assertEqual(None, finding.cvssv3_score)
13191333

13201334
with self.subTest(i=5):
13211335
# CVSS2 prefix makes it invalid
1322-
result = self.client.patch(self.url + "3/", data={"cvssv3": "CVSS:2.0/AV:N/AC:L/Au:N/C:P/I:P/A:P"})
1336+
result = self.client.patch(self.url + "3/", data={"cvssv3": "CVSS:2.0/AV:N/AC:L/Au:N/C:P/I:P/A:P", "cvssv3_score": 7})
13231337
self.assertEqual(result.status_code, status.HTTP_400_BAD_REQUEST)
1324-
self.assertEqual(result.json()["cvssv3"], ["No CVSS vectors found by cvss.parse_cvss_from_text()"])
1325-
self.assertEqual(None, Finding.objects.get(id=3).cvssv3)
1338+
self.assertEqual(result.json()["cvssv3"], ["No valid CVSS vectors found by cvss.parse_cvss_from_text()"])
1339+
finding = Finding.objects.get(id=3)
1340+
# invalid vector, so no calculated score and no score stored
1341+
self.assertEqual(None, finding.cvssv3)
1342+
self.assertEqual(None, finding.cvssv3_score)
1343+
1344+
with self.subTest(i=6):
1345+
# try to put rubbish in there
1346+
result = self.client.patch(self.url + "4/", data={"cvssv3": "happy little vector", "cvssv3_score": 3})
1347+
self.assertEqual(result.status_code, status.HTTP_400_BAD_REQUEST)
1348+
self.assertEqual(result.json()["cvssv3"], ["No valid CVSS vectors found by cvss.parse_cvss_from_text()"])
1349+
finding = Finding.objects.get(id=4)
1350+
# invalid vector, so no calculated score and no score stored
1351+
self.assertEqual(None, finding.cvssv3)
1352+
self.assertEqual(None, finding.cvssv3_score)
1353+
1354+
with self.subTest(i=7):
1355+
# CVSS4 prefix makes it invalid
1356+
result = self.client.patch(self.url + "3/", data={"cvssv3": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/S:U/C:H/I:H/A:H", "cvssv3_score": 7})
1357+
self.assertEqual(result.status_code, status.HTTP_400_BAD_REQUEST)
1358+
self.assertEqual(result.json()["cvssv3"], ["No valid CVSS vectors found by cvss.parse_cvss_from_text()"])
1359+
finding = Finding.objects.get(id=3)
1360+
# invalid vector, so no calculated score and no score stored
1361+
self.assertEqual(None, finding.cvssv3)
1362+
self.assertEqual(None, finding.cvssv3_score)
13261363

13271364

13281365
class FindingMetadataTest(BaseClass.BaseClassTest):

0 commit comments

Comments
 (0)