Skip to content

Commit 8bca5cc

Browse files
authored
Merge pull request #1636 from aboutcode-org/1597-refactor-vulnerability-severity
Refactor severity score model and fix incorrect suse scores
2 parents 8a68c97 + a008c37 commit 8bca5cc

File tree

17 files changed

+401
-178
lines changed

17 files changed

+401
-178
lines changed

vulnerabilities/api.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,25 @@ def to_representation(self, instance):
5454

5555

5656
class VulnerabilityReferenceSerializer(serializers.ModelSerializer):
57-
scores = VulnerabilitySeveritySerializer(many=True, source="vulnerabilityseverity_set")
57+
scores = serializers.SerializerMethodField()
5858
reference_url = serializers.CharField(source="url")
5959

6060
class Meta:
6161
model = VulnerabilityReference
6262
fields = ["reference_url", "reference_id", "reference_type", "scores", "url"]
6363

64+
def get_scores(self, instance):
65+
severities_related_to_reference = [
66+
severity
67+
for severity in self.context.get("severities", [])
68+
if severity.url == instance.url
69+
]
70+
71+
return VulnerabilitySeveritySerializer(
72+
severities_related_to_reference,
73+
many=True,
74+
).data
75+
6476

6577
class BaseResourceSerializer(serializers.HyperlinkedModelSerializer):
6678
"""
@@ -199,8 +211,7 @@ class VulnerabilitySerializer(BaseResourceSerializer):
199211
many=True, source="filtered_fixed_packages", read_only=True
200212
)
201213
affected_packages = MinimalPackageSerializer(many=True, read_only=True)
202-
203-
references = VulnerabilityReferenceSerializer(many=True, source="vulnerabilityreference_set")
214+
references = serializers.SerializerMethodField()
204215
aliases = AliasSerializer(many=True, source="alias")
205216
exploits = ExploitSerializer(many=True, read_only=True)
206217
weaknesses = WeaknessSerializer(many=True)
@@ -214,10 +225,22 @@ def to_representation(self, instance):
214225

215226
return data
216227

228+
def get_references(self, vulnerability):
229+
references = vulnerability.vulnerabilityreference_set.all()
230+
severities = vulnerability.severities.all()
231+
232+
serialized_references = VulnerabilityReferenceSerializer(
233+
references,
234+
context={"severities": severities},
235+
many=True,
236+
).data
237+
238+
return serialized_references
239+
217240
def get_severity_range_score(self, instance):
218241
severity_vectors = []
219242
severity_values = set()
220-
for s in instance.severities:
243+
for s in instance.severities.all():
221244
if s.scoring_system == EPSS.identifier:
222245
continue
223246

vulnerabilities/api_extension.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,10 @@ class Meta:
8484

8585
class V2VulnerabilitySeveritySerializer(ModelSerializer):
8686
score = CharField(source="value")
87-
reference = V2VulnerabilityReferenceSerializer()
8887

8988
class Meta:
9089
model = VulnerabilitySeverity
91-
fields = ("score", "scoring_system", "scoring_elements", "published_at", "reference")
90+
fields = ("url", "score", "scoring_system", "scoring_elements", "published_at")
9291

9392

9493
class V2WeaknessSerializer(ModelSerializer):
@@ -127,9 +126,9 @@ class V2VulnerabilitySerializer(ModelSerializer):
127126

128127
aliases = SerializerMethodField("get_aliases")
129128
weaknesses = V2WeaknessSerializer(many=True, source="weaknesses_set")
130-
scores = V2VulnerabilitySeveritySerializer(many=True, source="vulnerabilityseverity_set")
131129
references = V2VulnerabilityReferenceSerializer(many=True, source="vulnerabilityreference_set")
132130
exploits = V2ExploitSerializer(many=True, source="weaknesses")
131+
severities = V2VulnerabilitySeveritySerializer(many=True)
133132

134133
def get_aliases(self, vulnerability):
135134
return vulnerability.aliases.only("alias").values_list("alias", flat=True)
@@ -145,11 +144,11 @@ class Meta:
145144
"vulnerability_id",
146145
"aliases",
147146
"status",
148-
"scores",
149147
"weaknesses",
150148
"summary",
151149
"exploits",
152150
"references",
151+
"severities",
153152
)
154153

155154

@@ -358,7 +357,7 @@ def get_queryset(self):
358357
.get_queryset()
359358
.prefetch_related(
360359
"weaknesses",
361-
# "severities",
360+
"severities",
362361
# "exploits",
363362
)
364363
)

vulnerabilities/api_v2.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
from rest_framework.response import Response
1414
from rest_framework.reverse import reverse
1515

16-
from vulnerabilities.api import VulnerabilitySeveritySerializer
1716
from vulnerabilities.models import Package
1817
from vulnerabilities.models import Vulnerability
1918
from vulnerabilities.models import VulnerabilityReference
@@ -41,11 +40,24 @@ class Meta:
4140
fields = ["url", "reference_type", "reference_id"]
4241

4342

43+
class VulnerabilitySeverityV2Serializer(serializers.ModelSerializer):
44+
class Meta:
45+
model = VulnerabilitySeverity
46+
fields = ["url", "value", "scoring_system", "scoring_elements", "published_at"]
47+
48+
def to_representation(self, instance):
49+
data = super().to_representation(instance)
50+
published_at = data.get("published_at", None)
51+
if not published_at:
52+
data.pop("published_at")
53+
return data
54+
55+
4456
class VulnerabilityV2Serializer(serializers.ModelSerializer):
4557
aliases = serializers.SerializerMethodField()
4658
weaknesses = WeaknessV2Serializer(many=True)
4759
references = VulnerabilityReferenceV2Serializer(many=True, source="vulnerabilityreference_set")
48-
severities = VulnerabilitySeveritySerializer(many=True)
60+
severities = VulnerabilitySeverityV2Serializer(many=True)
4961

5062
class Meta:
5163
model = Vulnerability
@@ -61,9 +73,6 @@ class Meta:
6173
def get_aliases(self, obj):
6274
return [alias.alias for alias in obj.aliases.all()]
6375

64-
def get_severities(self, obj):
65-
return obj.severities
66-
6776

6877
class VulnerabilityListSerializer(serializers.ModelSerializer):
6978
url = serializers.SerializerMethodField()

vulnerabilities/import_runner.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -180,33 +180,36 @@ def process_inferences(inferences: List[Inference], advisory: Advisory, improver
180180
reference_id=ref.reference_id,
181181
url=ref.url,
182182
)
183-
if not reference:
184-
continue
185183

186-
VulnerabilityRelatedReference.objects.update_or_create(
187-
reference=reference,
188-
vulnerability=vulnerability,
189-
)
184+
if reference:
185+
VulnerabilityRelatedReference.objects.update_or_create(
186+
reference=reference,
187+
vulnerability=vulnerability,
188+
)
190189
updated = False
191190
for severity in ref.severities:
192191
try:
193192
published_at = str(severity.published_at) if severity.published_at else None
194-
_vs, updated = VulnerabilitySeverity.objects.update_or_create(
193+
(
194+
vulnerability_severity,
195+
updated,
196+
) = VulnerabilitySeverity.objects.update_or_create(
195197
scoring_system=severity.system.identifier,
196-
reference=reference,
198+
url=ref.url,
199+
value=severity.value,
200+
scoring_elements=severity.scoring_elements,
197201
defaults={
198-
"value": str(severity.value),
199-
"scoring_elements": str(severity.scoring_elements),
200202
"published_at": published_at,
201203
},
202204
)
205+
vulnerability.severities.add(vulnerability_severity)
203206
except:
204207
logger.error(
205208
f"Failed to create VulnerabilitySeverity for: {severity} with error:\n{traceback_format_exc()}"
206209
)
207210
if updated:
208211
logger.info(
209-
f"Severity updated for reference {ref!r} to value: {severity.value!r} "
212+
f"Severity updated for reference {ref.url!r} to value: {severity.value!r} "
210213
f"and scoring_elements: {severity.scoring_elements!r}"
211214
)
212215

vulnerabilities/management/commands/export.py

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,12 @@
2222

2323

2424
def serialize_severity(sev):
25-
# inlines refs
26-
ref = sev.reference
27-
sevref = {
28-
"url": ref.url,
29-
"reference_type": ref.reference_type,
30-
"reference_id": ref.reference_id,
31-
}
32-
3325
return {
3426
"score": sev.value,
3527
"scoring_system": sev.scoring_system,
3628
"scoring_elements": sev.scoring_elements,
3729
"published_at": sev.published_at,
38-
"reference": sevref,
30+
"url": sev.url,
3931
}
4032

4133

@@ -44,7 +36,7 @@ def serialize_vulnerability(vuln):
4436
Return a plain data mapping seralized from ``vuln`` Vulnerability instance.
4537
"""
4638
aliases = list(vuln.aliases.values_list("alias", flat=True))
47-
severities = [serialize_severity(sev) for sev in vuln.severities]
39+
severities = [serialize_severity(sev) for sev in vuln.severities.all()]
4840
weaknesses = [wkns.cwe for wkns in vuln.weaknesses.all()]
4941

5042
references = list(
@@ -161,11 +153,11 @@ def packages_by_type_ns_name():
161153
"affected_by_vulnerabilities",
162154
"affected_by_vulnerabilities__references",
163155
"affected_by_vulnerabilities__weaknesses",
164-
"affected_by_vulnerabilities__references__vulnerabilityseverity_set",
156+
"affected_by_vulnerabilities__severities",
165157
"fixing_vulnerabilities",
166158
"fixing_vulnerabilities__references",
167159
"fixing_vulnerabilities__weaknesses",
168-
"fixing_vulnerabilities__references__vulnerabilityseverity_set",
160+
"fixing_vulnerabilities__severities",
169161
)
170162
.paginated()
171163
)

0 commit comments

Comments
 (0)