3030from typing import Set
3131from typing import Tuple
3232from typing import Optional
33+ from typing import Sequence
3334
3435import packageurl
3536from django .db import DataError
36- #remove this
37- from django .db import connection
3837
3938from vulnerabilities import models
4039from vulnerabilities .data_source import Advisory , DataSource
@@ -52,12 +51,18 @@ def __post_init__(self):
5251 if not any ([self .reference_id ,self .url ]) :
5352 raise TypeError ("VulnerabilityReference_inserter expects either reference_id or url" )
5453
54+ def to_model_object (self ):
55+ return models .VulnerabilityReference (** dataclasses .asdict (self ))
56+
5557@dataclasses .dataclass (frozen = True )
5658class Vulnerability_Package_inserter :
5759 vulnerability : models .Vulnerability
5860 is_vulnerable : bool
5961 package : models .Package
6062
63+ def to_model_object (self ):
64+ return models .Vulnerability_Package_Relation (** dataclasses .asdict (self ))
65+
6166class ImportRunner :
6267 """
6368 The ImportRunner is responsible for inserting and updating data about vulnerabilities and
@@ -99,7 +104,6 @@ def run(self, cutoff_date: datetime.datetime = None) -> None:
99104 self .importer .save ()
100105
101106 logger .debug (f'Successfully finished import for { self .importer .name } .' )
102-
103107
104108def _process_updated_advisories (data_source : DataSource ) -> None :
105109 """
@@ -110,77 +114,19 @@ def _process_updated_advisories(data_source: DataSource) -> None:
110114 bulk_update_vuln_pkg_refs = set ()
111115 for batch in data_source .updated_advisories ():
112116 for advisory in batch :
113- vuln , vuln_created = _get_or_create_vulnerability (advisory )
114-
115- if vuln_created :
116- # This means vulnerability didn't previously exist in the db, so bulk create
117- # is used without any hesitation
118- for id_ in set (advisory .reference_ids ):
119- # bulk_create_vuln_refs.append(models.VulnerabilityReference(vulnerability=vuln, reference_id=id_))
120- bulk_create_vuln_refs .add (VulnerabilityReference_inserter (vulnerability = vuln , reference_id = id_ ))
121-
122- for url in set (advisory .reference_urls ):
123- bulk_create_vuln_refs .add (VulnerabilityReference_inserter (vulnerability = vuln , url = url ))
124-
125- else :
126- vuln_refs_qs = models .VulnerabilityReference .objects .filter (vulnerability = vuln )
127- # This is to avoid making additional SELECT queries, and do further filtering in python
128- # Check https://stackoverflow.com/a/5989530
129- vuln_ids = {ref .reference_id for ref in vuln_refs_qs }
130- vuln_urls = {ref .url for ref in vuln_refs_qs }
131-
132- for id_ in advisory .reference_ids :
133- # Add the item preventing duplicates pass to through.
134- if id_ not in vuln_ids :
135- vuln_ids .add (id_ )
136- bulk_create_vuln_refs .add (VulnerabilityReference_inserter (vulnerability = vuln , reference_id = id_ ))
137-
138- for url in advisory .reference_urls :
139- # Add the item preventing duplicates pass to through.
140- if url not in vuln_urls :
141- vuln_urls .add (url )
142- bulk_create_vuln_refs .add (VulnerabilityReference_inserter (vulnerability = vuln , url = url ))
143-
144-
145- for ipurl in advisory .impacted_package_urls :
146- pkg , pkg_created = _get_or_create_package (ipurl )
147- vuln_pkgs_ref = Vulnerability_Package_inserter (package = pkg ,vulnerability = vuln ,is_vulnerable = True )
148-
149- if pkg_created or vuln_created :
150- bulk_create_vuln_pkg_refs .add (vuln_pkgs_ref )
151-
152- else :
153- qs = models .Vulnerability_Package_Relation .objects .filter (package = pkg ,vulnerability = vuln )
154- if qs :
155- if not qs [0 ].is_vulnerable :
156- qs [0 ].is_vulnerable = True
157- bulk_update_vuln_pkg_refs .add (qs [0 ])
158-
159- else :
160- bulk_create_vuln_pkg_refs .add (vuln_pkgs_ref )
161-
162- for rpurl in advisory .resolved_package_urls :
163- pkg , pkg_created = _get_or_create_package (rpurl )
164- vuln_pkgs_ref = Vulnerability_Package_inserter (package = pkg ,vulnerability = vuln ,is_vulnerable = True )
165-
166- if pkg_created or vuln_created :
167- # For a `Vulnerability_Package_Relation` tp exist it needs both, the package and
168- # vulnerability to already exist in the db.
169- bulk_create_vuln_pkg_refs .add (vuln_pkgs_ref )
170-
171- else :
172- qs = models .Vulnerability_Package_Relation .objects .filter (package = pkg ,vulnerability = vuln )
173- if qs :
174- if qs [0 ].is_vulnerable :
175- qs [0 ].is_vulnerable = False
176- bulk_update_vuln_pkg_refs .add (qs [0 ])
177-
178- else :
179- bulk_create_vuln_pkg_refs .add (vuln_pkgs_ref )
180-
117+
118+ vuln , vuln_created , references = _create_vulnerability_and_references (advisory )
119+ bulk_create_vuln_refs .update (references )
120+ inew_refs , iupdated_refs = _create_pkg_vuln_refs (vuln , vuln_created , advisory .impacted_package_urls , is_vulnerable = True )
121+ rnew_refs , rupdated_refs = _create_pkg_vuln_refs (vuln , vuln_created , advisory .resolved_package_urls , is_vulnerable = False )
122+ bulk_update_vuln_pkg_refs .update (iupdated_refs .union (rupdated_refs ))
123+ bulk_create_vuln_pkg_refs .update (inew_refs .union (rnew_refs ))
124+
125+ models .VulnerabilityReference .objects .bulk_create ([i .to_model_object () for i in bulk_create_vuln_refs ])
181126 models .Vulnerability_Package_Relation .objects .bulk_update (bulk_update_vuln_pkg_refs , ['is_vulnerable' ])
182- models .Vulnerability_Package_Relation .objects .bulk_create ([models .Vulnerability_Package_Relation (** dataclasses .asdict (i )) for i in bulk_create_vuln_pkg_refs ])
183- models .VulnerabilityReference .objects .bulk_create ([models .VulnerabilityReference (** dataclasses .asdict (i )) for i in bulk_create_vuln_refs ])
127+ print (bulk_update_vuln_pkg_refs )
128+ # print(bulk_create_vuln_pkg_refs)
129+ models .Vulnerability_Package_Relation .objects .bulk_create ([i .to_model_object () for i in bulk_create_vuln_pkg_refs ])
184130
185131def _process_added_advisories (data_source : DataSource ) -> None :
186132 for batch in data_source .added_advisories ():
@@ -198,6 +144,66 @@ def _process_added_advisories(data_source: DataSource) -> None:
198144 logger .exception (e )
199145
200146
147+ def _create_vulnerability_and_references (advisory : Advisory ) :
148+ vuln , vuln_created = _get_or_create_vulnerability (advisory )
149+ vuln_references = set ()
150+
151+ if vuln_created :
152+ # print('old vuln')
153+ # This means vulnerability didn't previously exist in the db, so bulk create
154+ # is used without any hesitation
155+ for id_ in set (advisory .reference_ids ):
156+ # bulk_create_vuln_refs.append(models.VulnerabilityReference(vulnerability=vuln, reference_id=id_))
157+ vuln_references .add (VulnerabilityReference_inserter (vulnerability = vuln , reference_id = id_ ))
158+
159+ for url in set (advisory .reference_urls ):
160+ vuln_references .add (VulnerabilityReference_inserter (vulnerability = vuln , url = url ))
161+
162+ else :
163+ # print('new vuln')
164+ vuln_refs_qs = models .VulnerabilityReference .objects .filter (vulnerability = vuln )
165+ # This is to avoid making additional SELECT queries, and do further filtering in python
166+ # Check https://stackoverflow.com/a/5989530
167+ vuln_ids = {ref .reference_id for ref in vuln_refs_qs }
168+ vuln_urls = {ref .url for ref in vuln_refs_qs }
169+
170+ for id_ in advisory .reference_ids :
171+ # Add the item preventing duplicates pass to through.
172+ if id_ not in vuln_ids :
173+ vuln_ids .add (id_ )
174+ vuln_references .add (VulnerabilityReference_inserter (vulnerability = vuln , reference_id = id_ ))
175+
176+ for url in advisory .reference_urls :
177+ # Add the item preventing duplicates pass to through.
178+ if url not in vuln_urls :
179+ vuln_urls .add (url )
180+ vuln_references .add (VulnerabilityReference_inserter (vulnerability = vuln , url = url ))
181+
182+ return vuln , vuln_created , vuln_references
183+
184+ def _create_pkg_vuln_refs (vuln :models .Vulnerability , vuln_created :bool , purls :Sequence [PackageURL ], is_vulnerable :bool ) :
185+
186+ new_refs , updated_refs = set (), set ()
187+ for purl in purls :
188+ pkg , pkg_created = _get_or_create_package (purl )
189+ vuln_pkgs_ref = Vulnerability_Package_inserter (package = pkg ,vulnerability = vuln ,is_vulnerable = is_vulnerable )
190+
191+ if pkg_created or vuln_created :
192+ new_refs .add (vuln_pkgs_ref )
193+
194+ else :
195+ qs = models .Vulnerability_Package_Relation .objects .filter (package = pkg ,vulnerability = vuln )
196+ if qs :
197+ if qs [0 ].is_vulnerable != is_vulnerable :
198+ qs [0 ].is_vulnerable = is_vulnerable
199+ updated_refs .add (qs [0 ])
200+
201+ else :
202+ new_refs .add (vuln_pkgs_ref )
203+
204+ return new_refs , updated_refs
205+
206+
201207def _get_or_create_vulnerability (advisory : Advisory ) -> Tuple [models .Vulnerability , bool ]:
202208 if advisory .cve_id :
203209 query_kwargs = {'cve_id' : advisory .cve_id }
@@ -207,6 +213,7 @@ def _get_or_create_vulnerability(advisory: Advisory) -> Tuple[models.Vulnerabili
207213 return models .Vulnerability .objects .create (), True
208214
209215 vuln , created = models .Vulnerability .objects .get_or_create (** query_kwargs )
216+ print (vuln ," " , created )
210217
211218 if advisory .summary and vuln .summary != advisory .summary :
212219 vuln .summary = advisory .summary
0 commit comments