From bc0f9922e35312ef7b1852a7c0bec46b761f11a4 Mon Sep 17 00:00:00 2001 From: tdruez Date: Tue, 10 Dec 2024 18:08:44 +0400 Subject: [PATCH] Add a VulnerabilityAnalysis REST API endpoint #103 Signed-off-by: tdruez --- dejacode/urls.py | 2 ++ dje/models.py | 16 ++++++++++++++++ product_portfolio/api.py | 18 ++++++++++++++++++ product_portfolio/models.py | 21 ++++----------------- vulnerabilities/api.py | 19 +++++++++++++++++++ vulnerabilities/models.py | 3 +++ 6 files changed, 62 insertions(+), 17 deletions(-) diff --git a/dejacode/urls.py b/dejacode/urls.py index 047dfb9c..3b03801c 100644 --- a/dejacode/urls.py +++ b/dejacode/urls.py @@ -51,6 +51,7 @@ from product_portfolio.api import ProductDependencyViewSet from product_portfolio.api import ProductPackageViewSet from product_portfolio.api import ProductViewSet +from product_portfolio.api import VulnerabilityAnalysisViewSet from reporting.api import ReportViewSet from vulnerabilities.api import VulnerabilityViewSet from workflow.api import RequestTemplateViewSet @@ -80,6 +81,7 @@ api_router.register("external_references", ExternalReferenceViewSet) api_router.register("usage_policies", UsagePolicyViewSet) api_router.register("vulnerabilities", VulnerabilityViewSet) +api_router.register("vulnerability_analyses", VulnerabilityAnalysisViewSet) urlpatterns = [ diff --git a/dje/models.py b/dje/models.py index e330520c..86562fc1 100644 --- a/dje/models.py +++ b/dje/models.py @@ -16,6 +16,7 @@ from itertools import groupby from operator import attrgetter +from django.apps import apps from django.conf import settings from django.contrib.admin.models import ADDITION from django.contrib.admin.models import CHANGE @@ -620,6 +621,21 @@ def secure_queryset_relational_fields(queryset, user): return queryset +class ProductSecuredQuerySet(DataspacedQuerySet): + def product_secured(self, user=None, perms="view_product"): + """Filter based on the Product object permission.""" + if not user: + return self.none() + + Product = apps.get_model("product_portfolio", "Product") + product_qs = Product.objects.get_queryset(user, perms) + return self.filter(product__in=product_qs) + + def product(self, product): + """Filter based on the provided ``product`` object.""" + return self.filter(product=product) + + class DataspacedModel(models.Model): """Abstract base model for all models that are keyed by Dataspace.""" diff --git a/product_portfolio/api.py b/product_portfolio/api.py index c5e21fda..b9d72156 100644 --- a/product_portfolio/api.py +++ b/product_portfolio/api.py @@ -48,6 +48,8 @@ from product_portfolio.models import ProductComponent from product_portfolio.models import ProductDependency from product_portfolio.models import ProductPackage +from vulnerabilities.api import VulnerabilityAnalysis +from vulnerabilities.api import VulnerabilityAnalysisFilterSet from vulnerabilities.api import VulnerabilityAnalysisSerializer base_extra_kwargs = { @@ -933,3 +935,19 @@ def get_queryset(self): "resolved_to_package__dataspace", ) ) + + +class VulnerabilityAnalysisViewSet(ProductRelatedViewSet): + queryset = VulnerabilityAnalysis.objects.none() + serializer_class = VulnerabilityAnalysisSerializer + filterset_class = VulnerabilityAnalysisFilterSet + + def get_queryset(self): + return ( + super() + .get_queryset() + .select_related( + "vulnerability", + "product_package", + ) + ) diff --git a/product_portfolio/models.py b/product_portfolio/models.py index ee8db99f..1257b8cc 100644 --- a/product_portfolio/models.py +++ b/product_portfolio/models.py @@ -33,17 +33,15 @@ from dje.fields import LastModifiedByField from dje.models import DataspacedManager from dje.models import DataspacedModel -from dje.models import DataspacedQuerySet from dje.models import History from dje.models import HistoryFieldsMixin +from dje.models import ProductSecuredQuerySet from dje.models import ReferenceNotesMixin from dje.models import colored_icon_mixin_factory from dje.validators import generic_uri_validator from dje.validators import validate_url_segment from dje.validators import validate_version from vulnerabilities.fetch import fetch_for_packages -from vulnerabilities.models import Vulnerability -from vulnerabilities.models import VulnerabilityAnalysis RELATION_LICENSE_EXPRESSION_HELP_TEXT = _( "The License Expression assigned to a DejaCode Product Package or Product " @@ -519,6 +517,9 @@ def fetch_vulnerabilities(self): def get_vulnerability_qs(self, prefetch_related_packages=False): """Return a QuerySet of all Vulnerability instances related to this product""" + from vulnerabilities.models import Vulnerability + from vulnerabilities.models import VulnerabilityAnalysis + vulnerability_qs = Vulnerability.objects.filter( affected_packages__in=self.packages.all() ).distinct() @@ -583,20 +584,6 @@ def label_with_icon(self): return self.label -class ProductSecuredQuerySet(DataspacedQuerySet): - def product_secured(self, user=None, perms="view_product"): - """Filter based on the Product object permission.""" - if not user: - return self.none() - - product_qs = Product.objects.get_queryset(user, perms) - return self.filter(product__in=product_qs) - - def product(self, product): - """Filter based on the provided ``product`` object.""" - return self.filter(product=product) - - class ProductComponentQuerySet(ProductSecuredQuerySet): def catalogs(self): return self.filter(component__isnull=False) diff --git a/vulnerabilities/api.py b/vulnerabilities/api.py index 81c008c0..98e9185a 100644 --- a/vulnerabilities/api.py +++ b/vulnerabilities/api.py @@ -143,6 +143,8 @@ class Meta: "responses", "detail", "is_reachable", + "first_issued", + "last_updated", ) extra_kwargs = { "product_package": { @@ -154,3 +156,20 @@ class Meta: "lookup_field": "uuid", }, } + + +class VulnerabilityAnalysisFilterSet(DataspacedAPIFilterSet): + uuid = MultipleUUIDFilter() + last_updated = LastModifiedDateFilter() + + class Meta: + model = VulnerabilityAnalysis + fields = ( + "uuid", + "state", + "justification", + "detail", + "is_reachable", + "first_issued", + "last_updated", + ) diff --git a/vulnerabilities/models.py b/vulnerabilities/models.py index 6206a560..6ae33eaf 100644 --- a/vulnerabilities/models.py +++ b/vulnerabilities/models.py @@ -22,6 +22,7 @@ from dje.models import DataspacedQuerySet from dje.models import HistoryDateFieldsMixin from dje.models import HistoryUserFieldsMixin +from dje.models import ProductSecuredQuerySet logger = logging.getLogger("dje") @@ -511,6 +512,8 @@ class VulnerabilityAnalysis( ), ) + objects = DataspacedManager.from_queryset(ProductSecuredQuerySet)() + class Meta: unique_together = (("product_package", "vulnerability"), ("dataspace", "uuid")) indexes = [