1111from datetime import datetime
1212from datetime import timezone
1313from typing import List
14- from typing import Tuple
1514
15+ from django .core .exceptions import ValidationError
1616from django .db import transaction
1717
18- from vulnerabilities import models
19- from vulnerabilities .importer import PackageURL
2018from vulnerabilities .improver import Inference
2119from vulnerabilities .models import Advisory
20+ from vulnerabilities .models import Alias
21+ from vulnerabilities .models import Package
22+ from vulnerabilities .models import PackageRelatedVulnerability
23+ from vulnerabilities .models import Vulnerability
24+ from vulnerabilities .models import VulnerabilityReference
25+ from vulnerabilities .models import VulnerabilityRelatedReference
26+ from vulnerabilities .models import VulnerabilitySeverity
2227
2328logger = logging .getLogger (__name__ )
2429
@@ -63,46 +68,59 @@ def process_inferences(inferences: List[Inference], advisory: Advisory, improver
6368 logger .info (f"Improving advisory id: { advisory .id } " )
6469
6570 for inference in inferences :
66- vuln = get_or_create_vulnerability_and_aliases (
67- inference .vulnerability_id , inference .aliases , inference .summary
71+ vulnerability = get_or_create_vulnerability_and_aliases (
72+ vulnerability_id = inference .vulnerability_id ,
73+ alias_names = inference .aliases ,
74+ summary = inference .summary ,
6875 )
69- if not vuln :
76+
77+ if not vulnerability :
7078 logger .warn (f"Unable to get vulnerability for inference: { inference !r} " )
7179 continue
7280
7381 for ref in inference .references :
74- reference , _ = models .VulnerabilityReference .objects .get_or_create (
75- reference_id = ref .reference_id , url = ref .url
82+
83+ reference = VulnerabilityReference .objects .get_or_none (
84+ reference_id = ref .reference_id ,
85+ url = ref .url ,
7686 )
7787
78- models .VulnerabilityRelatedReference .objects .update_or_create (
79- reference = reference , vulnerability = vuln
88+ if not reference :
89+ reference = create_valid_vulnerability_reference (
90+ reference_id = ref .reference_id ,
91+ url = ref .url ,
92+ )
93+ if not reference :
94+ continue
95+
96+ VulnerabilityRelatedReference .objects .update_or_create (
97+ reference = reference ,
98+ vulnerability = vulnerability ,
8099 )
81100
82101 for severity in ref .severities :
83- _vs , updated = models . VulnerabilitySeverity .objects .update_or_create (
102+ _vs , updated = VulnerabilitySeverity .objects .update_or_create (
84103 scoring_system = severity .system .identifier ,
85104 reference = reference ,
86105 defaults = {"value" : str (severity .value )},
87106 )
88107 if updated :
89108 logger .info (f"Severity updated for reference { ref !r} to { severity .value !r} " )
90109
91- if inference .affected_purls :
92- for pkg in inference .affected_purls :
93- vulnerable_package , _ = _get_or_create_package (pkg )
94- models .PackageRelatedVulnerability (
95- vulnerability = vuln ,
96- package = vulnerable_package ,
97- created_by = improver_name ,
98- confidence = inference .confidence ,
99- fix = False ,
100- ).update_or_create ()
110+ for affected_purl in inference .affected_purls or []:
111+ vulnerable_package = Package .objects .get_or_create_from_purl (purl = affected_purl )
112+ PackageRelatedVulnerability (
113+ vulnerability = vulnerability ,
114+ package = vulnerable_package ,
115+ created_by = improver_name ,
116+ confidence = inference .confidence ,
117+ fix = False ,
118+ ).update_or_create ()
101119
102120 if inference .fixed_purl :
103- fixed_package , _ = _get_or_create_package ( inference .fixed_purl )
104- models . PackageRelatedVulnerability (
105- vulnerability = vuln ,
121+ fixed_package = Package . objects . get_or_create_from_purl ( purl = inference .fixed_purl )
122+ PackageRelatedVulnerability (
123+ vulnerability = vulnerability ,
106124 package = fixed_package ,
107125 created_by = improver_name ,
108126 confidence = inference .confidence ,
@@ -113,26 +131,25 @@ def process_inferences(inferences: List[Inference], advisory: Advisory, improver
113131 advisory .save ()
114132
115133
116- def _get_or_create_package (p : PackageURL ) -> Tuple [models .Package , bool ]:
117- query_kwargs = {}
118- # TODO: this should be revisited as this should best be a model or manager method... and possibly streamlined
119- query_kwargs = dict (
120- type = p .type or "" ,
121- namespace = p .namespace or "" ,
122- name = p .name or "" ,
123- version = p .version or "" ,
124- qualifiers = p .qualifiers or {},
125- subpath = p .subpath or "" ,
134+ def create_valid_vulnerability_reference (url , reference_id = None ):
135+ """
136+ Create and return a new validated VulnerabilityReference from a
137+ ``url`` and ``reference_id``.
138+ Return None and log a warning if this is not a valid reference.
139+ """
140+ reference = VulnerabilityReference (
141+ reference_id = reference_id ,
142+ url = url ,
126143 )
127144
128- return models .Package .objects .get_or_create (** query_kwargs )
129-
145+ try :
146+ reference .full_clean ()
147+ except ValidationError as e :
148+ logger .warning (f"Invalid vulnerability reference: { reference !r} : { e } " )
149+ return
130150
131- def _package_url_to_package (purl : PackageURL ) -> models .Package :
132- # FIXME: this is is likely creating a package from a purl?
133- p = models .Package ()
134- p .set_package_url (purl )
135- return p
151+ reference .save ()
152+ return reference
136153
137154
138155def get_or_create_vulnerability_and_aliases (vulnerability_id , alias_names , summary ):
@@ -145,9 +162,9 @@ def get_or_create_vulnerability_and_aliases(vulnerability_id, alias_names, summa
145162 new_alias_names = set ()
146163 for alias_name in alias_names :
147164 try :
148- alias = models . Alias .objects .get (alias = alias_name )
165+ alias = Alias .objects .get (alias = alias_name )
149166 existing_vulns .add (alias .vulnerability )
150- except models . Alias .DoesNotExist :
167+ except Alias .DoesNotExist :
151168 new_alias_names .add (alias_name )
152169
153170 # If given set of aliases point to different vulnerabilities in the
@@ -179,14 +196,14 @@ def get_or_create_vulnerability_and_aliases(vulnerability_id, alias_names, summa
179196 vulnerability = existing_alias_vuln
180197 elif vulnerability_id :
181198 try :
182- vulnerability = models . Vulnerability .objects .get (vulnerability_id = vulnerability_id )
183- except models . Vulnerability .DoesNotExist :
199+ vulnerability = Vulnerability .objects .get (vulnerability_id = vulnerability_id )
200+ except Vulnerability .DoesNotExist :
184201 logger .warn (
185202 f"Given vulnerability_id: { vulnerability_id } does not exist in the database"
186203 )
187204 return
188205 else :
189- vulnerability = models . Vulnerability (summary = summary )
206+ vulnerability = Vulnerability (summary = summary )
190207 vulnerability .save ()
191208
192209 if summary and summary != vulnerability .summary :
@@ -196,7 +213,7 @@ def get_or_create_vulnerability_and_aliases(vulnerability_id, alias_names, summa
196213 )
197214
198215 for alias_name in new_alias_names :
199- alias = models . Alias (alias = alias_name , vulnerability = vulnerability )
216+ alias = Alias (alias = alias_name , vulnerability = vulnerability )
200217 alias .save ()
201218 logger .info (f"New alias for { vulnerability !r} : { alias_name } " )
202219
0 commit comments