Skip to content

Commit 7c2aad3

Browse files
revert back to overriding the score field always
1 parent 4bd6934 commit 7c2aad3

File tree

9 files changed

+103
-51
lines changed

9 files changed

+103
-51
lines changed

docs/content/en/open_source/contributing/how-to-write-a-parser.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,8 @@ There's also a helper method to validate the vector and extract the base score a
179179
if cvss_data:
180180
finding.severity = cvss_data["severity"]
181181
finding.cvssv3 = cvss_data["cvssv3"]
182-
finding.cvssv3_score = cvss_data["cvssv3_score"]
183182
finding.cvssv4 = cvss_data["cvssv4"]
184-
finding.cvssv4_score = cvss_data["cvssv4_score"]
183+
# we don't set any score fields as those will be overwritten by Defect Dojo
185184
```
186185
Not all values have to be used as scan reports usuyall provide their own value for `severity`.
187186
And sometimes also for `cvss_score`. Defect Dojo will not overwrite any `cvss3_score` or `cvss4_score`.

dojo/db_migrations/0234_finding_cvssv4_finding_cvssv4_score.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class Migration(migrations.Migration):
2020
migrations.AlterField(
2121
model_name='finding',
2222
name='cvssv3_score',
23-
field=models.FloatField(blank=True, help_text='Numerical CVSS3 score for the vulnerability. If the vector is given without a score, the score is calcaulated while saving the finding. The value must be between 0-10.', null=True, validators=[django.core.validators.MinValueValidator(0.0), django.core.validators.MaxValueValidator(10.0)], verbose_name='CVSS3 Score'),
23+
field=models.FloatField(blank=True, help_text='Numerical CVSSv3 score for the vulnerability. If the vector is given, the score is updated while saving the finding. The value must be between 0-10.', null=True, validators=[django.core.validators.MinValueValidator(0.0), django.core.validators.MaxValueValidator(10.0)], verbose_name='CVSS3 Score'),
2424
),
2525
migrations.AddField(
2626
model_name='finding',
@@ -30,16 +30,16 @@ class Migration(migrations.Migration):
3030
migrations.AddField(
3131
model_name='finding',
3232
name='cvssv4_score',
33-
field=models.FloatField(blank=True, help_text='Numerical CVSS4 score for the vulnerability. If the vector is given without a score, the score is calcaulated while saving the finding. The value must be between 0-10.', null=True, validators=[django.core.validators.MinValueValidator(0.0), django.core.validators.MaxValueValidator(10.0)], verbose_name='CVSSv4 Score'),
33+
field=models.FloatField(blank=True, help_text='Numerical CVSSv4 score for the vulnerability. If the vector is given, the score is updated while saving the finding. The value must be between 0-10.', null=True, validators=[django.core.validators.MinValueValidator(0.0), django.core.validators.MaxValueValidator(10.0)], verbose_name='CVSSv4 Score'),
3434
),
3535
migrations.AddField(
3636
model_name='system_settings',
3737
name='enable_cvss3_display',
38-
field=models.BooleanField(default=True, help_text='With this setting turned off, CVSS3 fields will be hidden in the user interface.', verbose_name='Enable CVSS3 Display'),
38+
field=models.BooleanField(blank=False, default=True, help_text='With this setting turned off, CVSS3 fields will be hidden in the user interface.', verbose_name='Enable CVSS3 Display'),
3939
),
4040
migrations.AddField(
4141
model_name='system_settings',
4242
name='enable_cvss4_display',
43-
field=models.BooleanField(default=True, help_text='With this setting turned off, CVSS4 fields will be hidden in the user interface.', verbose_name='Enable CVSS4 Display'),
43+
field=models.BooleanField(blank=False, default=True, help_text='With this setting turned off, CVSS4 fields will be hidden in the user interface.', verbose_name='Enable CVSS4 Display'),
4444
),
4545
]

dojo/models.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2363,7 +2363,7 @@ class Finding(models.Model):
23632363
cvssv3_score = models.FloatField(null=True,
23642364
blank=True,
23652365
verbose_name=_("CVSS3 Score"),
2366-
help_text=_("Numerical CVSS3 score for the vulnerability. If the vector is given without a score, the score is calcaulated while saving the finding. The value must be between 0-10."),
2366+
help_text=_("Numerical CVSSv3 score for the vulnerability. If the vector is given, the score is updated while saving the finding. The value must be between 0-10."),
23672367
validators=[MinValueValidator(0.0), MaxValueValidator(10.0)])
23682368

23692369
cvssv4 = models.TextField(validators=[cvss4_validator],
@@ -2374,7 +2374,7 @@ class Finding(models.Model):
23742374
cvssv4_score = models.FloatField(null=True,
23752375
blank=True,
23762376
verbose_name=_("CVSSv4 Score"),
2377-
help_text=_("Numerical CVSS4 score for the vulnerability. If the vector is given without a score, the score is calcaulated while saving the finding. The value must be between 0-10."),
2377+
help_text=_("Numerical CVSSv4 score for the vulnerability. If the vector is given, the score is updated while saving the finding. The value must be between 0-10."),
23782378
validators=[MinValueValidator(0.0), MaxValueValidator(10.0)])
23792379

23802380
url = models.TextField(null=True,
@@ -2739,8 +2739,7 @@ def save(self, dedupe_option=True, rules_option=True, product_grading_option=Tru
27392739
cvss_data = parse_cvss_data(self.cvssv3)
27402740
if cvss_data:
27412741
self.cvssv3 = cvss_data.get("cvssv3")
2742-
if not self.cvssv3_score:
2743-
self.cvssv3_score = cvss_data.get("cvssv3_score")
2742+
self.cvssv3_score = cvss_data.get("cvssv3_score")
27442743

27452744
except Exception as ex:
27462745
logger.warning("Can't compute cvssv3 score for finding id %i. Invalid cvssv3 vector found: '%s'. Exception: %s.", self.id, self.cvssv3, ex)
@@ -2754,8 +2753,7 @@ def save(self, dedupe_option=True, rules_option=True, product_grading_option=Tru
27542753
cvss_data = parse_cvss_data(self.cvssv4)
27552754
if cvss_data:
27562755
self.cvssv4 = cvss_data.get("cvssv4")
2757-
if not self.cvssv4_score:
2758-
self.cvssv4_score = cvss_data.get("cvssv4_score")
2756+
self.cvssv4_score = cvss_data.get("cvssv4_score")
27592757

27602758
except Exception as ex:
27612759
logger.warning("Can't compute cvssv4 score for finding id %i. Invalid cvssv4 vector found: '%s'. Exception: %s.", self.id, self.cvssv4, ex)

dojo/tools/auditjs/parser.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,7 @@ def get_findings(self, filename, test):
104104
cvssv4 = cvss_data["cvssv4"]
105105
# The score in the report can be different from what the cvss library calulates
106106
if cvss_data["major_version"] == 2:
107-
description += "\nCVSS V2 Vector:" + cvss_data["cvssv2"]
108-
if cvss_data["major_version"] == 3:
109-
cvssv3_score = cvss_score
110-
if cvss_data["major_version"] == 4:
111-
cvssv4_score = cvss_score
107+
description += "\nCVSS V2 Vector:" + cvss_data["cvssv2"] + " (Score: " + cvss_score + ")"
112108
else:
113109
# If there is no vector, calculate severity based on CVSS score
114110
severity = self.get_severity(cvss_score)

dojo/tools/ptart/assessment_parser.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,11 @@ def get_finding(self, assessment, hit):
4545
finding.cve = hit.get("id")
4646

4747
cvss_vector = hit.get("cvss_vector", None)
48-
cvss_score = hit.get("cvss_score", None)
4948
if cvss_vector:
5049
cvss_data = parse_cvss_data(cvss_vector)
5150
if cvss_data:
5251
finding.cvssv3 = cvss_data["cvssv3"]
5352
finding.cvssv4 = cvss_data["cvssv4"]
54-
# The score in the report can be different from what the cvss library calulates
55-
if cvss_data["major_version"] == 3:
56-
finding.cvssv3_score = cvss_score
57-
if cvss_data["major_version"] == 4:
58-
finding.cvssv4_score = cvss_score
5953

6054
if "labels" in hit:
6155
finding.unsaved_tags = hit["labels"]

dojo/tools/ptart/retest_parser.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,17 +79,11 @@ def get_finding(self, retest, hit):
7979
finding.cve = original_hit.get("id")
8080

8181
cvss_vector = original_hit.get("cvss_vector", None)
82-
cvss_score = original_hit.get("cvss_score", None)
8382
if cvss_vector:
8483
cvss_data = parse_cvss_data(cvss_vector)
8584
if cvss_data:
8685
finding.cvssv3 = cvss_data["cvssv3"]
8786
finding.cvssv4 = cvss_data["cvssv4"]
88-
# The score in the report can be different from what the cvss library calulates
89-
if cvss_data["major_version"] == 3:
90-
finding.cvssv3_score = cvss_score
91-
if cvss_data["major_version"] == 4:
92-
finding.cvssv4_score = cvss_score
9387

9488
if "labels" in original_hit:
9589
finding.unsaved_tags = original_hit["labels"]

dojo/utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2722,6 +2722,7 @@ def parse_cvss_data(cvss_vector_string: str) -> dict:
27222722
major_version = 4
27232723
cvssv4 = vector.clean_vector()
27242724
cvssv4_score = vector.scores()[0]
2725+
logger.debug("CVSS4 vector: %s, score: %s", cvssv4, cvssv4_score)
27252726
severity = vector.severities()[0]
27262727
elif type(vector) is CVSS3:
27272728
major_version = 3

run-unittest.sh

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#!/usr/bin/env bash
22
unset TEST_CASE
3+
unset FAIL_FAST
34

45
bash ./docker/docker-compose-check.sh
56
if [[ $? -eq 1 ]]; then exit 1; fi
@@ -10,6 +11,7 @@ usage() {
1011
echo
1112
echo "Options:"
1213
echo " --test-case -t {YOUR_FULLY_QUALIFIED_TEST_CASE}"
14+
echo " --fail-fast -f - stop on first test failure"
1315
echo " --help -h - prints this dialogue."
1416
echo
1517
echo "You must specify a test case (arg)!"
@@ -25,6 +27,10 @@ while [[ $# -gt 0 ]]; do
2527
shift # past argument
2628
shift # past value
2729
;;
30+
-f|--fail-fast)
31+
FAIL_FAST="--failfast"
32+
shift # past argument
33+
;;
2834
-h|--help)
2935
usage
3036
exit 0
@@ -49,8 +55,11 @@ then
4955
exit 1
5056
fi
5157

58+
# docker compose exec uwsgi bash -c "python manage.py migrate dojo 0233"
59+
# docker compose exec uwsgi bash -c "python manage.py migrate dojo 0234"
60+
5261
echo "Running docker compose unit tests with test case $TEST_CASE ..."
5362
# Compose V2 integrates compose functions into the Docker platform, continuing to support
5463
# most of the previous docker-compose features and flags. You can run Compose V2 by
5564
# replacing the hyphen (-) with a space, using docker compose, instead of docker-compose.
56-
docker compose exec uwsgi bash -c "python manage.py test $TEST_CASE -v2 --keepdb"
65+
docker compose exec uwsgi bash -c "python manage.py test $TEST_CASE -v2 --keepdb $FAIL_FAST"

unittests/test_rest_framework.py

Lines changed: 82 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,9 +1287,9 @@ def test_cvss3_validation(self):
12871287
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})
12881288
self.assertEqual(result.status_code, status.HTTP_200_OK)
12891289
finding = Finding.objects.get(id=2)
1290-
# valid so vector must be set and score calculated does not ovewrite the score provided by us/the report
1290+
# valid so vector must be set and score calculated overrides the provided score
12911291
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(3.0, finding.cvssv3_score)
1292+
self.assertEqual(8.8, finding.cvssv3_score)
12931293

12941294
with self.subTest(i=1):
12951295
result = self.client.patch(self.url + "5/", data={"cvssv3": "CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H"})
@@ -1329,24 +1329,6 @@ def test_cvss3_validation(self):
13291329
self.assertEqual(None, finding.cvssv3)
13301330
self.assertEqual(None, finding.cvssv3_score)
13311331

1332-
with self.subTest(i=4):
1333-
# CVSS4 version valid
1334-
result = self.client.patch(self.url + "3/", data={"cvssv4": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N", "cvssv4_score": 5})
1335-
self.assertEqual(result.status_code, status.HTTP_200_OK)
1336-
finding = Finding.objects.get(id=3)
1337-
# invalid vector, so no calculated score and our provided score is stored (not overwritten)
1338-
self.assertEqual("CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N", finding.cvssv4)
1339-
self.assertEqual(5.0, finding.cvssv4_score)
1340-
1341-
with self.subTest(i=14):
1342-
# CVSS4 version valid, calculate score
1343-
result = self.client.patch(self.url + "3/", data={"cvssv4": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N"})
1344-
self.assertEqual(result.status_code, status.HTTP_200_OK)
1345-
finding = Finding.objects.get(id=3)
1346-
# invalid vector, so no calculated score and our provided score is stored (not overwritten)
1347-
self.assertEqual("CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N", finding.cvssv4)
1348-
self.assertEqual(5.0, finding.cvssv4_score)
1349-
13501332
with self.subTest(i=5):
13511333
# CVSS2 style vector makes not supported
13521334
result = self.client.patch(self.url + "3/", data={"cvssv3": "AV:N/AC:L/Au:N/C:P/I:P/A:P", "cvssv3_score": 6})
@@ -1378,7 +1360,7 @@ def test_cvss3_validation(self):
13781360
self.assertEqual(None, finding.cvssv3_score)
13791361

13801362
with self.subTest(i=8):
1381-
# CVSS4
1363+
# CVSS4 in cvssv3 field
13821364
result = self.client.patch(self.url + "3/", data={"cvssv3": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N", "cvssv3_score": 7})
13831365
self.assertEqual(result.status_code, status.HTTP_400_BAD_REQUEST)
13841366
self.assertEqual(result.json()["cvssv3"], ["CVSS4 vector cannot be stored in the cvssv3 field. Use the cvssv4 field."])
@@ -1387,6 +1369,85 @@ def test_cvss3_validation(self):
13871369
self.assertEqual(None, finding.cvssv3)
13881370
self.assertEqual(None, finding.cvssv3_score)
13891371

1372+
def test_cvss4_validation(self):
1373+
self.maxDiff = None
1374+
with self.subTest(i=0):
1375+
self.assertEqual(None, Finding.objects.get(id=2).cvssv3)
1376+
result = self.client.patch(self.url + "2/", data={"cvssv4": "CVSS:4.0/AV:A/AC:H/AT:N/PR:H/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N/S:P/AU:Y/R:U/V:C/RE:M/U:Green/MAV:A/MAC:H/MAT:P/MPR:L/MUI:P/MVC:L/MVI:L/MVA:L/MSC:L/MSI:H/MSA:H/CR:M/IR:M/AR:M/E:A", "cvssv4_score": 3})
1377+
self.assertEqual(result.status_code, status.HTTP_200_OK)
1378+
finding = Finding.objects.get(id=2)
1379+
# valid so vector must be set and score calculated overrides the provided score
1380+
self.assertEqual("CVSS:4.0/AV:A/AC:H/AT:N/PR:H/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N/S:P/AU:Y/R:U/V:C/RE:M/U:Green/MAV:A/MAC:H/MAT:P/MPR:L/MUI:P/MVC:L/MVI:L/MVA:L/MSC:L/MSI:H/MSA:H/CR:M/IR:M/AR:M/E:A", finding.cvssv4)
1381+
self.assertEqual(2.3, finding.cvssv4_score)
1382+
1383+
with self.subTest(i=1):
1384+
result = self.client.patch(self.url + "5/", data={"cvssv4": "CVSS:4.0/AV:A/AC:H/AT:N/PR:H/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N/S:P/AU:Y/R:U/V:C/RE:M/U:Green/MAV:A/MAC:H/MAT:P/MPR:L/MUI:P/MVC:L/MVI:L/MVA:L/MSC:L/MSI:H/MSA:H/CR:M/IR:M/AR:M/E:A"})
1385+
self.assertEqual(result.status_code, status.HTTP_200_OK)
1386+
finding = Finding.objects.get(id=5)
1387+
# valid so vector must be set and score calculated
1388+
self.assertEqual("CVSS:4.0/AV:A/AC:H/AT:N/PR:H/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N/S:P/AU:Y/R:U/V:C/RE:M/U:Green/MAV:A/MAC:H/MAT:P/MPR:L/MUI:P/MVC:L/MVI:L/MVA:L/MSC:L/MSI:H/MSA:H/CR:M/IR:M/AR:M/E:A", finding.cvssv4)
1389+
self.assertEqual(2.3, finding.cvssv4_score)
1390+
1391+
with self.subTest(i=2):
1392+
# extra slash makes it invalid
1393+
result = self.client.patch(self.url + "3/", data={"cvssv4": "CVSS:4.0/AV:A/AC:H/AT:N/PR:H/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N/S:P/AU:Y/R:U/V:C/RE:M/U:Green/MAV:A/MAC:H/MAT:P/MPR:L/MUI:P/MVC:L/MVI:L/MVA:L/MSC:L/MSI:H/MSA:H/CR:M/IR:M/AR:M/E:A/", "cvssv4_score": 3})
1394+
self.assertEqual(result.status_code, status.HTTP_400_BAD_REQUEST)
1395+
finding = Finding.objects.get(id=3)
1396+
self.assertEqual(result.json()["cvssv4"], ["No valid CVSS4 vectors found by cvss.parse_cvss_from_text()"])
1397+
# invalid vector, so no calculated score and no score stored
1398+
self.assertEqual(None, finding.cvssv3)
1399+
self.assertEqual(None, finding.cvssv3_score)
1400+
1401+
with self.subTest(i=3):
1402+
# no CVSS version prefix makes it invalid
1403+
result = self.client.patch(self.url + "3/", data={"cvssv4": "AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N", "cvssv4_score": 4})
1404+
self.assertEqual(result.status_code, status.HTTP_400_BAD_REQUEST)
1405+
finding = Finding.objects.get(id=3)
1406+
self.assertEqual(result.json()["cvssv4"], ["No valid CVSS4 vectors found by cvss.parse_cvss_from_text()"])
1407+
# invalid vector, so no calculated score and no score stored
1408+
self.assertEqual(None, finding.cvssv3)
1409+
self.assertEqual(None, finding.cvssv3_score)
1410+
1411+
with self.subTest(i=5):
1412+
# CVSS2 style vector makes not supported
1413+
result = self.client.patch(self.url + "3/", data={"cvssv4": "AV:N/AC:L/Au:N/C:P/I:P/A:P", "cvssv4_score": 6})
1414+
self.assertEqual(result.status_code, status.HTTP_400_BAD_REQUEST)
1415+
self.assertEqual(result.json()["cvssv4"], ["Unsupported CVSS2 version detected."])
1416+
finding = Finding.objects.get(id=3)
1417+
# invalid vector, so no calculated score and no score stored
1418+
self.assertEqual(None, finding.cvssv4)
1419+
self.assertEqual(None, finding.cvssv4_score)
1420+
1421+
with self.subTest(i=6):
1422+
# CVSS2 prefix makes it invalid
1423+
result = self.client.patch(self.url + "3/", data={"cvssv4": "CVSS:2.0/AV:N/AC:L/Au:N/C:P/I:P/A:P", "cvssv4_score": 7})
1424+
self.assertEqual(result.status_code, status.HTTP_400_BAD_REQUEST)
1425+
self.assertEqual(result.json()["cvssv4"], ["No valid CVSS4 vectors found by cvss.parse_cvss_from_text()"])
1426+
finding = Finding.objects.get(id=3)
1427+
# invalid vector, so no calculated score and no score stored
1428+
self.assertEqual(None, finding.cvssv4)
1429+
self.assertEqual(None, finding.cvssv4_score)
1430+
1431+
with self.subTest(i=7):
1432+
# try to put rubbish in there
1433+
result = self.client.patch(self.url + "4/", data={"cvssv4": "happy little vector", "cvssv4_score": 3})
1434+
self.assertEqual(result.status_code, status.HTTP_400_BAD_REQUEST)
1435+
self.assertEqual(result.json()["cvssv4"], ["No valid CVSS4 vectors found by cvss.parse_cvss_from_text()"])
1436+
finding = Finding.objects.get(id=4)
1437+
# invalid vector, so no calculated score and no score stored
1438+
self.assertEqual(None, finding.cvssv4)
1439+
self.assertEqual(None, finding.cvssv4_score)
1440+
1441+
with self.subTest(i=8):
1442+
# CVSS3 in CVSS4 field
1443+
result = self.client.patch(self.url + "3/", data={"cvssv4": "CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", "cvssv4_score": 7})
1444+
self.assertEqual(result.status_code, status.HTTP_400_BAD_REQUEST)
1445+
self.assertEqual(result.json()["cvssv4"], ["CVSS3 vector cannot be stored in the cvssv4 field. Use the cvssv3 field."])
1446+
finding = Finding.objects.get(id=3)
1447+
# invalid vector, so no calculated score and no score stored
1448+
self.assertEqual(None, finding.cvssv4)
1449+
self.assertEqual(None, finding.cvssv4_score)
1450+
13901451

13911452
class FindingMetadataTest(BaseClass.BaseClassTest):
13921453
fixtures = ["dojo_testdata.json"]

0 commit comments

Comments
 (0)