Skip to content

Commit de105b8

Browse files
committed
WIP
Signed-off-by: Shivam Sandbhor <shivam.sandbhor@gmail.com>
1 parent 5fcb5ea commit de105b8

2 files changed

Lines changed: 83 additions & 74 deletions

File tree

vulnerabilities/import_runner.py

Lines changed: 80 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,10 @@
3030
from typing import Set
3131
from typing import Tuple
3232
from typing import Optional
33+
from typing import Sequence
3334

3435
import packageurl
3536
from django.db import DataError
36-
#remove this
37-
from django.db import connection
3837

3938
from vulnerabilities import models
4039
from 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)
5658
class 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+
6166
class 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

104108
def _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

185131
def _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+
201207
def _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

vulnerabilities/models.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,9 @@ class Vulnerability_Package_Relation(models.Model):
116116
vulnerability = models.ForeignKey(Vulnerability, on_delete=models.CASCADE)
117117
is_vulnerable = models.BooleanField()
118118

119-
119+
class Meta:
120+
unique_together = ('package', 'vulnerability')
121+
120122

121123
class Importer(models.Model):
122124
"""

0 commit comments

Comments
 (0)