From 6c5bb53a17c8ad9946512da8b7f76a4b883c8e05 Mon Sep 17 00:00:00 2001 From: Tushar Goel Date: Thu, 20 Jul 2023 12:55:58 +0530 Subject: [PATCH] Add package context in vulnerability details view Signed-off-by: Tushar Goel --- vulnerabilities/models.py | 41 ++++++++----------- .../templates/package_details.html | 4 +- vulnerabilities/views.py | 13 +++++- 3 files changed, 31 insertions(+), 27 deletions(-) diff --git a/vulnerabilities/models.py b/vulnerabilities/models.py index 7b6c9fcc6..bd856bd74 100644 --- a/vulnerabilities/models.py +++ b/vulnerabilities/models.py @@ -341,28 +341,32 @@ class Meta: ordering = ["vulnerability", "reference"] -def purl_to_dict(purl: PackageURL): +def purl_to_dict(purl: PackageURL, without_version=False): """ Return a dict of purl components suitable for use in a queryset. We need to have specific empty values for using in querysets because of our peculiar model structure. For example:: >>> purl_to_dict(PackageURL.from_string("pkg:generic/postgres")) - {'type': 'generic', 'namespace': '', 'name': 'postgres', 'version': '', 'qualifiers': {}, 'subpath': ''} + {'type': 'generic', 'namespace': '', 'name': 'postgres', 'qualifiers': {}, 'subpath': '', 'version': ''} >>> purl_to_dict(PackageURL.from_string("pkg:generic/postgres/postgres@1.2?foo=bar#baz")) - {'type': 'generic', 'namespace': 'postgres', 'name': 'postgres', 'version': '1.2', 'qualifiers': {'foo': 'bar'}, 'subpath': 'baz'} + {'type': 'generic', 'namespace': 'postgres', 'name': 'postgres', 'qualifiers': {'foo': 'bar'}, 'subpath': 'baz', 'version': '1.2'} + >>> purl_to_dict(purl = PackageURL.from_string("pkg:generic/postgres/postgres@1.2?foo=bar#baz"), without_version=True) + {'type': 'generic', 'namespace': 'postgres', 'name': 'postgres', 'qualifiers': {'foo': 'bar'}, 'subpath': 'baz'} """ if isinstance(purl, str): purl = PackageURL.from_string(purl) - return dict( + lookup = dict( type=purl.type, namespace=purl.namespace or "", name=purl.name, - version=purl.version or "", qualifiers=purl.qualifiers or {}, subpath=purl.subpath or "", ) + if not without_version: + lookup["version"] = purl.version or "" + return lookup class PackageQuerySet(BaseQuerySet, PackageURLQuerySet): @@ -416,17 +420,6 @@ def with_vulnerability_counts(self): ), ) - def fixing_packages(self, package, with_qualifiers_and_subpath=True): - """ - Return a queryset of packages that are fixing the vulnerability of - ``package``. - """ - - return self.match_purl( - purl=package.purl, - with_qualifiers_and_subpath=with_qualifiers_and_subpath, - ).fixing() - def search(self, query=None): """ Return a Package queryset searching for the ``query``. @@ -481,6 +474,15 @@ def for_cve(self, cve): """ return self.filter(vulnerabilities__vulnerabilityreference__reference_id__exact=cve) + def matching_packages(self, purl): + if not purl: + return self + if not isinstance(purl, PackageURL): + purl = str(purl) + purl = PackageURL.from_string(purl) + lookups = purl_to_dict(purl=purl, without_version=True) + return self.filter(**lookups) + def get_purl_query_lookups(purl): """ @@ -584,13 +586,6 @@ def fixing(self): # legacy aliases resolved_to = fixing - @property - def fixed_packages(self): - """ - Return a queryset of packages that are fixed. - """ - return Package.objects.fixing_packages(package=self).distinct() - @property def is_vulnerable(self) -> bool: """ diff --git a/vulnerabilities/templates/package_details.html b/vulnerabilities/templates/package_details.html index 6a391d3d3..7fc639b53 100644 --- a/vulnerabilities/templates/package_details.html +++ b/vulnerabilities/templates/package_details.html @@ -58,7 +58,7 @@ {% for vulnerability in affected_by_vulnerabilities %} - {{ vulnerability.vulnerability_id }} + {{ vulnerability.vulnerability_id }} {{ vulnerability.summary }} @@ -105,7 +105,7 @@ {% for vulnerability in fixing_vulnerabilities %} - {{ vulnerability.vulnerability_id }} + {{ vulnerability.vulnerability_id }} {{ vulnerability.summary }} diff --git a/vulnerabilities/views.py b/vulnerabilities/views.py index e96f43a6d..7e82c666b 100644 --- a/vulnerabilities/views.py +++ b/vulnerabilities/views.py @@ -18,6 +18,7 @@ from django.views import generic from django.views.generic.detail import DetailView from django.views.generic.list import ListView +from packageurl import PackageURL from vulnerabilities import models from vulnerabilities.forms import ApiUserCreationForm @@ -116,6 +117,14 @@ def get_queryset(self): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) + request_query = self.request.GET + package = request_query.get("package") + purl = None + if package: + try: + purl = PackageURL.from_string(package) + except: + purl = None context.update( { "vulnerability": self.object, @@ -123,8 +132,8 @@ def get_context_data(self, **kwargs): "severities": list(self.object.severities), "references": self.object.references.all(), "aliases": self.object.aliases.all(), - "affected_packages": self.object.affected_packages.all(), - "fixed_by_packages": self.object.fixed_by_packages.all(), + "affected_packages": self.object.affected_packages.matching_packages(purl), + "fixed_by_packages": self.object.fixed_by_packages.matching_packages(purl), "weaknesses": self.object.weaknesses.all(), } )