Skip to content

Commit

Permalink
Add a VulnerabilityAnalysis REST API endpoint #103
Browse files Browse the repository at this point in the history
Signed-off-by: tdruez <[email protected]>
  • Loading branch information
tdruez committed Dec 10, 2024
1 parent b93d53d commit bc0f992
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 17 deletions.
2 changes: 2 additions & 0 deletions dejacode/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 = [
Expand Down
16 changes: 16 additions & 0 deletions dje/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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."""

Expand Down
18 changes: 18 additions & 0 deletions product_portfolio/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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",
)
)
21 changes: 4 additions & 17 deletions product_portfolio/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 "
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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)
Expand Down
19 changes: 19 additions & 0 deletions vulnerabilities/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ class Meta:
"responses",
"detail",
"is_reachable",
"first_issued",
"last_updated",
)
extra_kwargs = {
"product_package": {
Expand All @@ -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",
)
3 changes: 3 additions & 0 deletions vulnerabilities/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand Down Expand Up @@ -511,6 +512,8 @@ class VulnerabilityAnalysis(
),
)

objects = DataspacedManager.from_queryset(ProductSecuredQuerySet)()

class Meta:
unique_together = (("product_package", "vulnerability"), ("dataspace", "uuid"))
indexes = [
Expand Down

0 comments on commit bc0f992

Please sign in to comment.