diff --git a/vulnerabilities/api.py b/vulnerabilities/api.py index 1fd480ce9..9bc4f1604 100644 --- a/vulnerabilities/api.py +++ b/vulnerabilities/api.py @@ -13,6 +13,10 @@ from cvss.exceptions import CVSS3MalformedError from cvss.exceptions import CVSS4MalformedError from django.db.models import Prefetch +from django.db.models import Exists +from django.db.models import OuterRef +from django.http import Http404 +from django.shortcuts import get_object_or_404 from django_filters import rest_framework as filters from drf_spectacular.utils import extend_schema from packageurl import PackageURL @@ -22,6 +26,7 @@ from rest_framework import viewsets from rest_framework.decorators import action from rest_framework.response import Response +from rest_framework.reverse import reverse from rest_framework.throttling import AnonRateThrottle from vulnerabilities.models import Alias @@ -236,6 +241,10 @@ class VulnerabilitySerializer(BaseResourceSerializer): exploits = ExploitSerializer(many=True, read_only=True) weaknesses = WeaknessSerializer(many=True) severity_range_score = serializers.SerializerMethodField() + url = serializers.HyperlinkedIdentityField( + view_name="vulnerability-detail", + lookup_field="vulnerability_id" + ) def to_representation(self, instance): data = super().to_representation(instance) @@ -308,6 +317,8 @@ class PackageSerializer(BaseResourceSerializer): next_non_vulnerable_version = serializers.CharField(read_only=True) latest_non_vulnerable_version = serializers.CharField(read_only=True) + url = serializers.SerializerMethodField() + purl = serializers.CharField(source="package_url") affected_by_vulnerabilities = serializers.SerializerMethodField("get_affected_vulnerabilities") @@ -318,6 +329,10 @@ class PackageSerializer(BaseResourceSerializer): is_vulnerable = serializers.BooleanField() + def get_url(self, package): + request = self.context.get("request") + return reverse("package_details", kwargs={'purl': package.purl}, request=request) + def get_qualifiers(self, package): return normalize_qualifiers(package.qualifiers, encode=False) @@ -469,6 +484,7 @@ class PackageViewSet(viewsets.ReadOnlyModelViewSet): queryset = Package.objects.all() serializer_class = PackageSerializer + lookup_field = "package_url" filter_backends = (filters.DjangoFilterBackend,) filterset_class = PackageFilterSet throttle_classes = [StaffUserRateThrottle, AnonRateThrottle] @@ -689,6 +705,7 @@ def get_queryset(self): filter_backends = (filters.DjangoFilterBackend,) filterset_class = VulnerabilityFilterSet throttle_classes = [StaffUserRateThrottle, AnonRateThrottle] + lookup_field = "vulnerability_id" class CPEFilterSet(filters.FilterSet): diff --git a/vulnerablecode/urls.py b/vulnerablecode/urls.py index c6dd3da44..bc8828008 100644 --- a/vulnerablecode/urls.py +++ b/vulnerablecode/urls.py @@ -69,6 +69,14 @@ def __init__(self, *args, **kwargs): PackageSearch.as_view(), name="package_search", ), + re_path( + r'^api/packages/(?Ppkg:.+?)/?$', + PackageViewSet.as_view({"get": "retrieve"}), + ), + re_path(r"^api/vulnerabilities/(?PVCID-[\w-]+)/$", + VulnerabilityViewSet.as_view({"get": "retrieve"}), + name="vulnerability-detail"), + re_path( r"^packages/(?Ppkg:.+)$", PackageDetails.as_view(),