From 9cb4fe8d46b441824b8020ce3e18a638d043d81d Mon Sep 17 00:00:00 2001 From: Tushar Goel Date: Wed, 20 Nov 2024 13:04:36 +0530 Subject: [PATCH 1/3] Add fixed_by packages pipeline Signed-off-by: Tushar Goel --- vulnerabilities/improvers/__init__.py | 2 + ...erelatedvulnerability_fixed_by_packages.py | 23 +++++++ vulnerabilities/models.py | 8 +++ .../pipelines/add_fixed_by_packages.py | 68 +++++++++++++++++++ 4 files changed, 101 insertions(+) create mode 100644 vulnerabilities/migrations/0084_affectedbypackagerelatedvulnerability_fixed_by_packages.py create mode 100644 vulnerabilities/pipelines/add_fixed_by_packages.py diff --git a/vulnerabilities/improvers/__init__.py b/vulnerabilities/improvers/__init__.py index fd18fb28c..788ab3cc3 100644 --- a/vulnerabilities/improvers/__init__.py +++ b/vulnerabilities/improvers/__init__.py @@ -15,6 +15,7 @@ from vulnerabilities.pipelines import enhance_with_kev from vulnerabilities.pipelines import enhance_with_metasploit from vulnerabilities.pipelines import flag_ghost_packages +from vulnerabilities.pipelines import add_fixed_by_packages IMPROVERS_REGISTRY = [ valid_versions.GitHubBasicImprover, @@ -39,6 +40,7 @@ enhance_with_metasploit.MetasploitImproverPipeline, enhance_with_exploitdb.ExploitDBImproverPipeline, compute_package_risk.ComputePackageRiskPipeline, + add_fixed_by_packages.ComputeFixedByPackagesPipeline, ] IMPROVERS_REGISTRY = { diff --git a/vulnerabilities/migrations/0084_affectedbypackagerelatedvulnerability_fixed_by_packages.py b/vulnerabilities/migrations/0084_affectedbypackagerelatedvulnerability_fixed_by_packages.py new file mode 100644 index 000000000..6dc90d1ae --- /dev/null +++ b/vulnerabilities/migrations/0084_affectedbypackagerelatedvulnerability_fixed_by_packages.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.16 on 2024-11-20 07:23 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("vulnerabilities", "0083_alter_packagechangelog_software_version_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="affectedbypackagerelatedvulnerability", + name="fixed_by_packages", + field=models.ManyToManyField( + blank=True, + help_text="Packages that fix this vulnerability for\n the affected package.", + related_name="fixing_relationships", + to="vulnerabilities.package", + ), + ), + ] diff --git a/vulnerabilities/models.py b/vulnerabilities/models.py index d6fae2408..f1456f457 100644 --- a/vulnerabilities/models.py +++ b/vulnerabilities/models.py @@ -1053,6 +1053,14 @@ class AffectedByPackageRelatedVulnerability(PackageRelatedVulnerabilityBase): related_name="affected_package_vulnerability_relations", ) + fixed_by_packages = models.ManyToManyField( + "Package", + blank=True, + related_name="fixing_relationships", + help_text="""Packages that fix this vulnerability for + the affected package.""", + ) + class Meta(PackageRelatedVulnerabilityBase.Meta): verbose_name_plural = "Affected By Package Related Vulnerabilities" diff --git a/vulnerabilities/pipelines/add_fixed_by_packages.py b/vulnerabilities/pipelines/add_fixed_by_packages.py new file mode 100644 index 000000000..f52d6ec93 --- /dev/null +++ b/vulnerabilities/pipelines/add_fixed_by_packages.py @@ -0,0 +1,68 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# VulnerableCode is a trademark of nexB Inc. +# SPDX-License-Identifier: Apache-2.0 +# See http://www.apache.org/licenses/LICENSE-2.0 for the license text. +# See https://github.com/aboutcode-org/vulnerablecode for support or download. +# See https://aboutcode.org for more information about nexB OSS projects. +# +from aboutcode.pipeline import LoopProgress +from django.db import transaction + +from vulnerabilities.models import AffectedByPackageRelatedVulnerability, FixingPackageRelatedVulnerability, Package +from vulnerabilities.pipelines import VulnerableCodePipeline + + +class ComputeFixedByPackagesPipeline(VulnerableCodePipeline): + """ + Compute and populate the `fixed_by_packages` field in AffectedByPackageRelatedVulnerability. + + See https://github.com/aboutcode-org/vulnerablecode/issues/1543 + """ + + pipeline_id = "compute_fixed_by_packages" + license_expression = None + + @classmethod + def steps(cls): + return (cls.compute_and_store_fixed_by_packages,) + + def compute_and_store_fixed_by_packages(self): + affected_relationships = AffectedByPackageRelatedVulnerability.objects.all() + + self.log(f"Calculating `fixed_by_packages` for {affected_relationships.count():,d} records") + + progress = LoopProgress( + total_iterations=affected_relationships.count(), + logger=self.log, + progress_step=5, + ) + + updated_relationship_count = 0 + + for relationship in progress.iter(affected_relationships): + # Get fixing packages for this relationship + fixing_package_ids = FixingPackageRelatedVulnerability.objects.filter( + package__name=relationship.package.name, + package__type=relationship.package.type, + package__namespace=relationship.package.namespace, + vulnerability=relationship.vulnerability, + ).values_list("package__id", flat=True) + + # Update the ManyToMany field using the provided method + self.update_fixed_by_packages(relationship, fixing_package_ids) + updated_relationship_count += 1 + + self.log( + f"Successfully populated `fixed_by_packages` for {updated_relationship_count:,d} records" + ) + + @transaction.atomic + def update_fixed_by_packages(self, relationship, fixing_package_ids): + """ + Update the fixed_by_packages field for a given relationship. + """ + # Clear existing relations and add new ones + relationship.fixed_by_packages.clear() + packages = Package.objects.filter(id__in=fixing_package_ids) + relationship.fixed_by_packages.add(*packages) From 5cb0be0d6cfd3a3ccf66256afba0b6fd70b8d2de Mon Sep 17 00:00:00 2001 From: Tushar Goel Date: Wed, 20 Nov 2024 13:05:09 +0530 Subject: [PATCH 2/3] Formatting changes Signed-off-by: Tushar Goel --- vulnerabilities/improvers/__init__.py | 2 +- vulnerabilities/pipelines/add_fixed_by_packages.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/vulnerabilities/improvers/__init__.py b/vulnerabilities/improvers/__init__.py index 788ab3cc3..6b261b777 100644 --- a/vulnerabilities/improvers/__init__.py +++ b/vulnerabilities/improvers/__init__.py @@ -10,12 +10,12 @@ from vulnerabilities.improvers import valid_versions from vulnerabilities.improvers import vulnerability_status from vulnerabilities.pipelines import VulnerableCodePipeline +from vulnerabilities.pipelines import add_fixed_by_packages from vulnerabilities.pipelines import compute_package_risk from vulnerabilities.pipelines import enhance_with_exploitdb from vulnerabilities.pipelines import enhance_with_kev from vulnerabilities.pipelines import enhance_with_metasploit from vulnerabilities.pipelines import flag_ghost_packages -from vulnerabilities.pipelines import add_fixed_by_packages IMPROVERS_REGISTRY = [ valid_versions.GitHubBasicImprover, diff --git a/vulnerabilities/pipelines/add_fixed_by_packages.py b/vulnerabilities/pipelines/add_fixed_by_packages.py index f52d6ec93..9728d6827 100644 --- a/vulnerabilities/pipelines/add_fixed_by_packages.py +++ b/vulnerabilities/pipelines/add_fixed_by_packages.py @@ -9,7 +9,9 @@ from aboutcode.pipeline import LoopProgress from django.db import transaction -from vulnerabilities.models import AffectedByPackageRelatedVulnerability, FixingPackageRelatedVulnerability, Package +from vulnerabilities.models import AffectedByPackageRelatedVulnerability +from vulnerabilities.models import FixingPackageRelatedVulnerability +from vulnerabilities.models import Package from vulnerabilities.pipelines import VulnerableCodePipeline From 538464be06ffb2e27617fe0a3a567e898577ac87 Mon Sep 17 00:00:00 2001 From: Tushar Goel Date: Wed, 20 Nov 2024 13:25:58 +0530 Subject: [PATCH 3/3] Add fixed_by_packages in V2 API Signed-off-by: Tushar Goel --- vulnerabilities/api_v2.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/vulnerabilities/api_v2.py b/vulnerabilities/api_v2.py index 58771c916..3f3024ffa 100644 --- a/vulnerabilities/api_v2.py +++ b/vulnerabilities/api_v2.py @@ -195,9 +195,24 @@ class Meta: ] def get_affected_by_vulnerabilities(self, obj): - return [vuln.vulnerability_id for vuln in obj.affected_by_vulnerabilities.all()] + """ + Return a dictionary with vulnerabilities as keys and their details, including fixed_by_packages. + """ + vulnerabilities = obj.affected_by_vulnerabilities.prefetch_related("fixed_by_packages") + result = {} + for vuln in vulnerabilities: + result[vuln.vulnerability_id] = { + "vulnerability_id": vuln.vulnerability_id, + "fixed_by_packages": [ + package.package_url for package in vuln.fixed_by_packages.all() + ], + } + return result def get_fixing_vulnerabilities(self, obj): + """ + Return a list of IDs of vulnerabilities that the package fixes. + """ return [vuln.vulnerability_id for vuln in obj.fixing_vulnerabilities.all()]