Skip to content

Commit 924ba7f

Browse files
finding api: fix hash_code for vulnerability_ids (#13304)
1 parent 7de7ec3 commit 924ba7f

File tree

2 files changed

+70
-8
lines changed

2 files changed

+70
-8
lines changed

dojo/api_v2/serializers.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1757,13 +1757,15 @@ def update(self, instance, validated_data):
17571757
if reporter_id := validated_data.get("reporter"):
17581758
instance.reporter = reporter_id
17591759

1760+
# Persist vulnerability IDs first so model save computes hash including them (if there is no hash yet)
1761+
# we can't pass unsaved_vulnerabilitiy_ids to super.update()
1762+
if parsed_vulnerability_ids:
1763+
save_vulnerability_ids(instance, parsed_vulnerability_ids)
1764+
17601765
instance = super().update(
17611766
instance, validated_data,
17621767
)
17631768

1764-
if parsed_vulnerability_ids:
1765-
save_vulnerability_ids(instance, parsed_vulnerability_ids)
1766-
17671769
if push_to_jira:
17681770
jira_helper.push_to_jira(instance)
17691771

@@ -1880,11 +1882,15 @@ def create(self, validated_data):
18801882
if (vulnerability_ids := validated_data.pop("vulnerability_id_set", None)):
18811883
logger.debug("VULNERABILITY_ID_SET: %s", vulnerability_ids)
18821884
parsed_vulnerability_ids.extend(vulnerability_id["vulnerability_id"] for vulnerability_id in vulnerability_ids)
1885+
logger.debug("PARSED_VULNERABILITY_IDST: %s", parsed_vulnerability_ids)
18831886
logger.debug("SETTING CVE FROM VULNERABILITY_ID_SET: %s", parsed_vulnerability_ids[0])
18841887
validated_data["cve"] = parsed_vulnerability_ids[0]
1888+
# validated_data["unsaved_vulnerability_ids"] = parsed_vulnerability_ids
18851889

1886-
new_finding = super().create(
1887-
validated_data)
1890+
# super.create() doesn't accept unsaved_vulnerability_ids or dedupe_option=False, so call save directly.
1891+
new_finding = Finding(**validated_data)
1892+
new_finding.unsaved_vulnerability_ids = parsed_vulnerability_ids or []
1893+
new_finding.save()
18881894

18891895
logger.debug(f"New finding CVE: {new_finding.cve}")
18901896

@@ -1897,9 +1903,6 @@ def create(self, validated_data):
18971903
new_finding.reviewers.set(reviewers)
18981904
if parsed_vulnerability_ids:
18991905
save_vulnerability_ids(new_finding, parsed_vulnerability_ids)
1900-
# can we avoid this extra save? the cve has already been set above in validated_data. but there are no tests for this
1901-
# on finding update nothing is done # with vulnerability_ids?
1902-
# new_finding.save()
19031906

19041907
if push_to_jira:
19051908
jira_helper.push_to_jira(new_finding)

unittests/test_rest_framework.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111
# from drf_spectacular.renderers import OpenApiJsonRenderer
1212
from unittest.mock import ANY, MagicMock, call, patch
1313

14+
from django.conf import settings
1415
from django.contrib.auth.models import Permission
1516
from django.test import tag as test_tag
17+
from django.test.utils import override_settings
1618
from django.urls import reverse
1719
from drf_spectacular.drainage import GENERATOR_STATS
1820
from drf_spectacular.settings import spectacular_settings
@@ -1245,6 +1247,63 @@ def test_duplicate(self):
12451247
self.assertFalse(result_json["duplicate"])
12461248
self.assertIsNone(result_json["duplicate_finding"])
12471249

1250+
def test_hash_code_includes_vulnerability_ids_on_create(self):
1251+
zap_fields = ["title", "cwe", "severity", "vulnerability_ids"]
1252+
current = dict(getattr(settings, "HASHCODE_FIELDS_PER_SCANNER", {}))
1253+
current["ZAP Scan"] = zap_fields
1254+
1255+
with override_settings(HASHCODE_FIELDS_PER_SCANNER=current):
1256+
orig = Finding.objects.filter(test__test_type__name="ZAP Scan").first()
1257+
self.assertIsNotNone(orig, "Fixture must provide a ZAP Scan finding")
1258+
1259+
cve_value = "CVE-9999-0001"
1260+
1261+
model_clone = Finding(
1262+
test=orig.test,
1263+
title=orig.title,
1264+
date=orig.date,
1265+
cwe=orig.cwe,
1266+
severity=orig.severity,
1267+
description=orig.description,
1268+
mitigation=orig.mitigation,
1269+
impact=orig.impact,
1270+
references=orig.references,
1271+
active=orig.active,
1272+
verified=orig.verified,
1273+
false_p=orig.false_p,
1274+
duplicate=orig.duplicate,
1275+
out_of_scope=orig.out_of_scope,
1276+
under_review=orig.under_review,
1277+
under_defect_review=orig.under_defect_review,
1278+
numerical_severity=orig.numerical_severity,
1279+
reporter=orig.reporter,
1280+
static_finding=orig.static_finding,
1281+
dynamic_finding=orig.dynamic_finding,
1282+
file_path=orig.file_path,
1283+
line=orig.line,
1284+
)
1285+
model_clone.unsaved_vulnerability_ids = [cve_value]
1286+
model_clone.save()
1287+
model_hash = model_clone.hash_code
1288+
1289+
payload = self.payload.copy()
1290+
payload.update({
1291+
"test": orig.test.id,
1292+
"title": orig.title,
1293+
"cwe": orig.cwe,
1294+
"severity": orig.severity,
1295+
"vulnerability_ids": [{"vulnerability_id": cve_value}],
1296+
})
1297+
payload["found_by"] = []
1298+
1299+
response = self.client.post(self.url, payload, format="json")
1300+
self.assertEqual(201, response.status_code, response.content[:1000])
1301+
new_id = response.data.get("id")
1302+
self.assertIsNotNone(new_id)
1303+
created = Finding.objects.get(id=new_id)
1304+
1305+
self.assertEqual(model_hash, created.hash_code)
1306+
12481307
def test_filter_steps_to_reproduce(self):
12491308
# Confirm initial data
12501309
result = self.client.get(self.url + "?steps_to_reproduce=lorem")

0 commit comments

Comments
 (0)