Skip to content

Commit 189582d

Browse files
committed
Revert _process_added_advisories, make style changes
Signed-off-by: Shivam Sandbhor <shivam.sandbhor@gmail.com>
1 parent 35280ff commit 189582d

1 file changed

Lines changed: 62 additions & 55 deletions

File tree

vulnerabilities/import_runner.py

Lines changed: 62 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import dataclasses
2525
import datetime
2626
import logging
27-
from itertools import chain
2827
from typing import Dict
2928
from typing import List
3029
from typing import Set
@@ -49,15 +48,15 @@
4948

5049

5150
@dataclasses.dataclass(frozen=True)
52-
class VulnerabilityReference_inserter:
51+
class VulnerabilityReferenceInserter:
5352
vulnerability: models.Vulnerability
5453
reference_id: Optional[str] = ''
5554
url: Optional[str] = ''
5655

5756
def __post_init__(self):
5857
if not any([self.reference_id, self.url]):
5958
raise TypeError(
60-
"VulnerabilityReference_inserter expects either reference_id or url")
59+
"VulnerabilityReferenceInserter expects either reference_id or url")
6160

6261
def to_model_object(self):
6362
return models.VulnerabilityReference(**dataclasses.asdict(self))
@@ -69,7 +68,7 @@ def to_model_object(self):
6968

7069

7170
@dataclasses.dataclass(frozen=True)
72-
class PackageRelatedVulnerability_inserter:
71+
class PackageRelatedVulnerabilityInserter:
7372
vulnerability: models.Vulnerability
7473
is_vulnerable: bool
7574
package: models.Package
@@ -111,28 +110,22 @@ def run(self, cutoff_date: datetime.datetime = None) -> None:
111110
from all Linux distributions that package this kernel version.
112111
"""
113112
logger.debug(f'Starting import for {self.importer.name}.')
114-
data_source = self.importer.make_data_source(
115-
self.batch_size, cutoff_date=cutoff_date)
113+
data_source = self.importer.make_data_source(self.batch_size, cutoff_date=cutoff_date)
116114
with data_source:
117115
_process_added_advisories(data_source)
118116
_process_updated_advisories(data_source)
119-
self.importer.last_run = datetime.datetime.now(
120-
tz=datetime.timezone.utc)
117+
self.importer.last_run = datetime.datetime.now(tz=datetime.timezone.utc)
121118
self.importer.data_source_cfg = dataclasses.asdict(data_source.config)
122119
self.importer.save()
123120

124121
logger.debug(f'Successfully finished import for {self.importer.name}.')
125122

126123

127124
def _process_updated_advisories(data_source: DataSource) -> None:
128-
"""
129-
TODO: Break this method into smaller functions
130-
"""
131125
bulk_create_vuln_refs = set()
132126
bulk_create_vuln_pkg_refs = set()
133127
for batch in data_source.updated_advisories():
134128
for advisory in batch:
135-
136129
vuln, vuln_created, references = _create_vulnerability_and_references(
137130
advisory)
138131
bulk_create_vuln_refs.update(references)
@@ -142,6 +135,11 @@ def _process_updated_advisories(data_source: DataSource) -> None:
142135
vuln, vuln_created, advisory.resolved_package_urls, is_vulnerable=False)
143136
bulk_create_vuln_pkg_refs.update(inew_refs.union(rnew_refs))
144137

138+
# FIXME: _create_pkg_vuln_refs handles conflicts between the data we encounter via
139+
# updated_advisories() and the data which already exists in the DB. It is not designed
140+
# to handle the conflicting data present within the entries of updated_advisories() itself.
141+
# This can be done by filtering bulk_create_vuln_pkg_refs for pairs (vulnerability, package)
142+
# occurring more than once. Also update the constraints in the models after this is fixed.
145143
models.VulnerabilityReference.objects.bulk_create(
146144
[i.to_model_object() for i in bulk_create_vuln_refs])
147145
models.PackageRelatedVulnerability.objects.bulk_create(
@@ -170,63 +168,68 @@ def _create_vulnerability_and_references(advisory: Advisory):
170168
vuln_references = set()
171169

172170
if vuln_created:
173-
# This means vulnerability didn't previously exist in the db, so bulk create
174-
# is used without any hesitation
175-
for id_ in set(advisory.reference_ids):
176-
vuln_references.add(VulnerabilityReference_inserter(
177-
vulnerability=vuln, reference_id=id_))
171+
# This means vulnerability didn't previously exist in the DB, so add
172+
# the references to bulk create queue without any hesitation
173+
for id_ in advisory.reference_ids:
174+
vuln_references.add(VulnerabilityReferenceInserter(vulnerability=vuln, reference_id=id_)) # nopep8
178175

179-
for url in set(advisory.reference_urls):
180-
vuln_references.add(VulnerabilityReference_inserter(
181-
vulnerability=vuln, url=url))
176+
for url in advisory.reference_urls:
177+
vuln_references.add(VulnerabilityReferenceInserter(vulnerability=vuln, url=url))
182178

183179
else:
184-
vuln_refs_qs = models.VulnerabilityReference.objects.filter(
185-
vulnerability=vuln)
180+
vuln_refs_qs = models.VulnerabilityReference.objects.filter(vulnerability=vuln)
186181
vuln_ids = {ref.reference_id for ref in vuln_refs_qs}
187182
vuln_urls = {ref.url for ref in vuln_refs_qs}
188183

189184
for id_ in advisory.reference_ids:
190185
# Add the item preventing duplicates pass to through.
191186
if id_ not in vuln_ids:
192187
vuln_ids.add(id_)
193-
vuln_references.add(VulnerabilityReference_inserter(
188+
vuln_references.add(VulnerabilityReferenceInserter(
194189
vulnerability=vuln, reference_id=id_))
195190

196191
for url in advisory.reference_urls:
197192
# Add the item preventing duplicates pass to through.
198193
if url not in vuln_urls:
199194
vuln_urls.add(url)
200-
vuln_references.add(VulnerabilityReference_inserter(
195+
vuln_references.add(VulnerabilityReferenceInserter(
201196
vulnerability=vuln, url=url))
202197

203198
return vuln, vuln_created, vuln_references
204199

205200

206201
def _create_pkg_vuln_refs(vuln: models.Vulnerability, vuln_created: bool, purls: Sequence[PackageURL], is_vulnerable: bool): # nopep8
207-
new_refs, updated_refs = set(), set()
202+
new_refs = set()
208203
for purl in purls:
209204
pkg, pkg_created = _get_or_create_package(purl)
210-
vuln_pkg_ref = PackageRelatedVulnerability_inserter(
211-
package=pkg, vulnerability=vuln, is_vulnerable=is_vulnerable)
205+
vuln_pkg_ref = PackageRelatedVulnerabilityInserter(
206+
package=pkg, vulnerability=vuln, is_vulnerable=is_vulnerable)
207+
212208
if pkg_created or vuln_created:
213209
new_refs.add(vuln_pkg_ref)
214210

215211
else:
216-
qs = models.PackageRelatedVulnerability.objects.filter(
212+
existing_pkg_vuln_refs = models.PackageRelatedVulnerability.objects.filter(
217213
package=pkg, vulnerability=vuln)
218-
if not qs:
214+
if not existing_pkg_vuln_refs:
219215
new_refs.add(vuln_pkg_ref)
220216
else:
221217
# Note: PackageRelatedVulnerability has constraints
222218
# unique_together = ('package', 'vulnerability', 'is_vulnerable')
223-
# This fact is used below.
224-
vuln_impact = {i.is_vulnerable for i in qs}
225-
if len(vuln_impact) == 2 or is_vulnerable not in vuln_impact:
226-
conflicts = [i for i in qs]
219+
vuln_impact = {i.is_vulnerable for i in existing_pkg_vuln_refs}
220+
# is_vulnerable is a boolean which indicates the relationship of
221+
# a vulnerability's impact on a package. vuln_impact is a set of
222+
# all such booleans for a pair of (vulnerability, package). In cases
223+
# where vuln_impact == {True, False}, we know that conflicting relationships
224+
# of (vulnerability, package) ALREADY EXIST in the DB.
225+
# The other check `is_vulnerable not in vuln_impact` is used to know whether the
226+
# data we just found is not conflicting with the data already existing in DB.
227+
# In any of the above two cases we move the entries involved in ImportProblem
228+
if vuln_impact == {True, False} or is_vulnerable not in vuln_impact:
229+
conflicts = existing_pkg_vuln_refs[:]
227230
conflicts.append(vuln_pkg_ref.to_model_object())
228231
handle_conflicts(conflicts)
229-
qs.delete()
232+
existing_pkg_vuln_refs.delete()
230233

231234
return new_refs
232235

@@ -278,28 +281,32 @@ def _get_or_create_package(p: PackageURL) -> Tuple[models.Package, bool]:
278281

279282

280283
def _bulk_insert_packages(
281-
impacted: List[PackageURL],
282-
resolved: List[PackageURL]
283-
) -> Tuple[Dict[PackageURL, int], Dict[PackageURL, int]]:
284+
impacted: Set[PackageURL],
285+
resolved: Set[PackageURL]
286+
) -> Tuple[Dict[PackageURL, models.Package], Dict[PackageURL, models.Package]]:
287+
288+
packages = [_package_url_to_package(p) for p in impacted.union(resolved)]
289+
packages = models.Package.objects.bulk_create(packages)
290+
291+
impacted_packages, resolved_packages = {}, {}
284292

285-
impacted_packages = models.Package.objects.bulk_create(
286-
[_package_url_to_package(p) for p in impacted])
287-
resolved_packages = models.Package.objects.bulk_create(
288-
[_package_url_to_package(p) for p in resolved])
293+
for pkg in packages:
294+
# Unfortunately, PackageURLMixin.package_url returns a string, not a PackageURL
295+
purl = PackageURL.from_string(pkg.package_url)
289296

290-
impacted_packages = dict(
291-
zip(impacted, [pkg.id for pkg in impacted_packages]))
292-
resolved_packages = dict(
293-
zip(resolved, [pkg.id for pkg in resolved_packages]))
297+
if purl in impacted:
298+
impacted_packages[purl] = pkg
299+
elif purl in resolved:
300+
resolved_packages[purl] = pkg
294301

295302
return impacted_packages, resolved_packages
296303

297304

298305
def _bulk_insert_impacted_and_resolved_packages(
299306
batch: Set[Advisory],
300307
vulnerabilities: Set[models.Vulnerability],
301-
impacted_packages: Dict[PackageURL, int],
302-
resolved_packages: Dict[PackageURL, int],
308+
impacted_packages: Dict[PackageURL, models.Package],
309+
resolved_packages: Dict[PackageURL, models.Package],
303310
) -> None:
304311

305312
refs: List[models.ImpactedPackage] = []
@@ -313,11 +320,11 @@ def _bulk_insert_impacted_and_resolved_packages(
313320
if not p:
314321
p = _package_url_to_package(impacted_purl)
315322
p.save()
316-
impacted_packages[impacted_purl] = p.id
323+
impacted_packages[impacted_purl] = p
317324

318325
ip = models.PackageRelatedVulnerability(
319326
vulnerability=vuln,
320-
package_id=p,
327+
package=p,
321328
is_vulnerable=True
322329
)
323330
refs.append(ip)
@@ -328,11 +335,11 @@ def _bulk_insert_impacted_and_resolved_packages(
328335
if not p:
329336
p = _package_url_to_package(resolved_purl)
330337
p.save()
331-
resolved_packages[resolved_purl] = p.id
338+
resolved_packages[resolved_purl] = p
332339

333340
ip = models.PackageRelatedVulnerability(
334341
vulnerability=vuln,
335-
package_id=p,
342+
package=p,
336343
is_vulnerable=False
337344
)
338345
refs.append(ip)
@@ -390,12 +397,12 @@ def _advisory_to_vulnerability(
390397
raise RuntimeError(f'No Vulnerability model object found for this Advisory: {advisory.summary}')
391398

392399

393-
def _collect_package_urls(batch: Set[Advisory]) -> Tuple[List[PackageURL], List[PackageURL]]:
394-
impacted, resolved = [], []
400+
def _collect_package_urls(batch: Set[Advisory]) -> Tuple[Set[PackageURL], Set[PackageURL]]:
401+
impacted, resolved = set(), set()
395402

396403
for advisory in batch:
397-
impacted.extend(advisory.impacted_package_urls)
398-
resolved.extend(advisory.resolved_package_urls)
404+
impacted.update(advisory.impacted_package_urls)
405+
resolved.update(advisory.resolved_package_urls)
399406

400407
return impacted, resolved
401408

0 commit comments

Comments
 (0)