Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion vulnerabilities/api_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()]


Expand Down
2 changes: 2 additions & 0 deletions vulnerabilities/improvers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
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
Expand Down Expand Up @@ -39,6 +40,7 @@
enhance_with_metasploit.MetasploitImproverPipeline,
enhance_with_exploitdb.ExploitDBImproverPipeline,
compute_package_risk.ComputePackageRiskPipeline,
add_fixed_by_packages.ComputeFixedByPackagesPipeline,
]

IMPROVERS_REGISTRY = {
Expand Down
Original file line number Diff line number Diff line change
@@ -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",
),
),
]
8 changes: 8 additions & 0 deletions vulnerabilities/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
70 changes: 70 additions & 0 deletions vulnerabilities/pipelines/add_fixed_by_packages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#
# 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
from vulnerabilities.models import FixingPackageRelatedVulnerability
from vulnerabilities.models import 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)