From 624f047cb21f04de0fbad927ccdd6c7e7ae8bbd2 Mon Sep 17 00:00:00 2001 From: "John M. Horan" Date: Fri, 28 Jul 2023 09:26:40 -0700 Subject: [PATCH] Explore context and Package class approaches for affected-fixed package matching #1228 Reference: https://github.com/nexB/vulnerablecode/issues/1228 Note that my updated code is still in testing/dev stage and has not yet been completed or cleaned. Signed-off-by: John M. Horan --- vulnerabilities/models.py | 80 +++++++++-- .../templates/package_details.html | 134 +++++++++++++++--- vulnerabilities/views.py | 35 +++++ 3 files changed, 221 insertions(+), 28 deletions(-) diff --git a/vulnerabilities/models.py b/vulnerabilities/models.py index 545953316..dbdf45ab8 100644 --- a/vulnerabilities/models.py +++ b/vulnerabilities/models.py @@ -22,6 +22,7 @@ from django.core.validators import MinValueValidator from django.db import models from django.db.models import Count +from django.db.models import Prefetch from django.db.models import Q from django.db.models.functions import Length from django.db.models.functions import Trim @@ -588,18 +589,6 @@ def affected_by(self): # legacy aliases vulnerable_to = affected_by - @property - def test_get_fixing_purls(self): - """ - This is a test -- the goal is to display the closest fixing version for a PURL that is greater - than the affected version and is the same type. We want to filter on type, namespace, - name, qualifiers and subpath for the affected PURL. - """ - return [ - abc.fixed_by_packages - for abc in self.vulnerabilities.filter(packagerelatedvulnerability__fix=False) - ] - @property # TODO: consider renaming to "fixes" or "fixing" ? (TBD) and updating the docstring def fixing(self): @@ -631,6 +620,73 @@ def get_absolute_url(self): """ return reverse("package_details", args=[self.purl]) + def get_fixed_packages(self, package): + """ + Return a queryset of all packages that fix a vulnerability with + same type, namespace, name, subpath and qualifiers of the `package` + """ + return Package.objects.filter( + name=package.name, + namespace=package.namespace, + type=package.type, + qualifiers=package.qualifiers, + subpath=package.subpath, + packagerelatedvulnerability__fix=True, + ).distinct() + + @property + def test_get_fixing_purls(self): + """ + This is a test -- the goal is to display the closest fixing version for a PURL that is greater + than the affected version and is the same type. We want to filter on type, namespace, + name, qualifiers and subpath for the affected PURL. + """ + + fixed_packages = self.get_fixed_packages(package=self) + + # Prefetch: + + # Where do we get "fix" from? + # qs = self.vulnerabilities.filter(packagerelatedvulnerability__fix=fix) + + # Not clear to me how we use this: + # qs = qs.prefetch_related( + # Prefetch( + # "packages", + # queryset=fixed_packages, + # to_attr="filtered_fixed_packages", + # ) + # ) + + matching_fixed_packages = [] + + # closest_subsequent_fixed_package = [] + + test_dict = {"affected_purl": self.purl} + test_dict["vulnerabilities"] = [] + + for vuln in self.affected_by: + test_dict["vulnerabilities"].append({"vulnerability": vuln.vulnerability_id}) + + for fixing_pkg in vuln.fixed_by_packages: + # Do not add "backports". TODO: Do we need to use univers to compare versions? + if fixing_pkg in fixed_packages and fixing_pkg.version > self.version: + matching_fixed_packages.append(fixing_pkg) + + # TODO: We also need to check if there is more than one fixing package and if so, keep only the package closest to the affect package's version. + # TODO: Do we want to check whether the fixing version has any vulnerabilities of its own? + + # ======================================================== + print("\ntest_dict = \n{}\n".format(test_dict)) + print(json.dumps(test_dict, indent=4)) + # ======================================================== + + return matching_fixed_packages + + +# 2023-07-27 Thursday 09:50:45. JMH: check this out re related api.py code +# TODO: Not clear how this is involved. + class PackageRelatedVulnerability(models.Model): """ diff --git a/vulnerabilities/templates/package_details.html b/vulnerabilities/templates/package_details.html index bb076c930..63c0fc37e 100644 --- a/vulnerabilities/templates/package_details.html +++ b/vulnerabilities/templates/package_details.html @@ -43,52 +43,131 @@
Let's try to display fixing packages for this package: {{ package.purl }}
- package.purl = {{ package.purl }} + package.purl = {{ package.purl }}
- package.qualifiers = {{ package.qualifiers }} + package.qualifiers = {{ package.qualifiers }}
- package.vulnerabilities = {{ package.vulnerabilities }} + package.vulnerabilities = {{ package.vulnerabilities }}
- package.package_url = {{ package.package_url }} + package.package_url = {{ package.package_url }}
- package.plain_package_url = {{ package.plain_package_url }} + package.plain_package_url = {{ package.plain_package_url }}
- package.purl_object = {{ package.purl_object }} + package.purl_object = {{ package.purl_object }}
- package.fixing = {{ package.fixing }} + package.fixing = {{ package.fixing }}
- package.fixed_packages = Server Error (500) + package.fixed_packages = Server Error (500)
- package.is_vulnerable = {{ package.is_vulnerable }} + package.is_vulnerable = {{ package.is_vulnerable }}
- package.get_absolute_url = {{ package.get_absolute_url }} + package.get_absolute_url = {{ package.get_absolute_url }}
- package.affected_by = {{ package.affected_by }} + package.affected_by = {{ package.affected_by }}
+ +
+ package.test_get_fixing_purls = {{ package.test_get_fixing_purls }} +
+
- package.test_get_fixing_purls = {{ package.test_get_fixing_purls }} + package.test_get_fixing_purls = {{ package.test_get_fixing_purls }}
{% for abc in package.affected_by %} -
{{ abc }} -- {{ abc.fixed_by_packages }}
-
{{ abc }} -- {% for pkg in abc.fixed_by_packages %}{{ pkg.purl }}{% endfor %}
+
{{ abc }} -- {{ abc.fixed_by_packages }}
+
{{ abc }} -- {% for pkg in abc.fixed_by_packages %}{{ pkg.purl }}{% endfor %}
+ + {% endfor %} + +
abc.test_matching_fixed_by_packages
+ {% for abc in package.affected_by %} + + + +
{{ abc }} -- + {% for pkg in abc.test_matching_fixed_by_packages %} +
{{ pkg.purl }}
+ {% endfor %} +
{% endfor %} +
+
test01
+ {% for thing in test01 %} + {{ thing }} +
+ {% endfor %} +
+ +
+
test02
+ {% for thing in test02 %} + - {{ thing }} +
+ {% endfor %} +
+ +
+
test03
+ {% for thing in test03 %} + - {{ thing }} +
+ {% for stuff in thing.test_matching_fixed_by_packages %} + ==> {{ stuff }} +
+ {% endfor %} +
+ {% endfor %} +
+ +
+
test04
+ {% for thing in test04 %} + - {{ thing }} +
+ {% endfor %} +
+ +
+ +
context["test_get_fixing_purls"]
+ {% for thing in test_get_fixing_purls %} + - {{ thing }} +
+ {% endfor %} + {% for vulnerability in affected_by_vulnerabilities %} +
+ {{ vulnerability }} +
+
+ {% for pkg in vulnerability.fixed_by_packages %} + {% if pkg in test_get_fixing_purls %} +
+ {{ pkg.purl }} +
+ {% endif %} + {% endfor %} +
+ {% endfor %} +
@@ -103,7 +182,7 @@ Vulnerability Summary Aliases - Test fixing PURLs + Fixing Packages @@ -131,8 +210,31 @@ {% for pkg in vulnerability.fixed_by_packages %} - {{ pkg.purl }} +
+ {{ pkg.purl }} +
+ {% endfor %} + +
+ + {% for pkg in vulnerability.fixed_by_packages %} + {% if pkg in test04 %} +
+ {{ pkg.purl }} +
+ {% endif %} + {% endfor %} + +
+ + {% for pkg in vulnerability.fixed_by_packages %} + {% if pkg in test_get_fixing_purls %} +
+ {{ pkg.purl }} +
+ {% endif %} {% endfor %} + {% empty %} diff --git a/vulnerabilities/views.py b/vulnerabilities/views.py index fad42eae8..eb181ff93 100644 --- a/vulnerabilities/views.py +++ b/vulnerabilities/views.py @@ -83,6 +83,41 @@ def get_context_data(self, **kwargs): context["affected_by_vulnerabilities"] = package.affected_by.order_by("vulnerability_id") context["fixing_vulnerabilities"] = package.fixing.order_by("vulnerability_id") context["package_search_form"] = PackageSearchForm(self.request.GET) + context["test01"] = [ + "aaa", + "bbb", + "ccc", + ] + context["test02"] = [ + package, + package.type, + package.namespace, + package.name, + package.version, + package.qualifiers, + package.subpath, + ] + context["test03"] = package.affected_by + + # ======================================================== + context["test04"] = [ + fixing_pkg + for vuln in package.affected_by + # for fixing_pkg in vuln.test_matching_fixed_by_packages + for fixing_pkg in vuln.fixed_by_packages + if fixing_pkg.type == package.type + and fixing_pkg.namespace == package.namespace + and fixing_pkg.name == package.name + and fixing_pkg.qualifiers == package.qualifiers + and fixing_pkg.subpath == package.subpath + # I think this version comparison requires the use of univers. + # Plus we just need the closest one, not all that are greater than. + and fixing_pkg.version > package.version + ] + # ======================================================== + context["test_get_fixing_purls"] = package.test_get_fixing_purls + # ======================================================== + return context def get_object(self, queryset=None):