From 9ab1c2f2088fd7d0373838835c999d7dda1a7997 Mon Sep 17 00:00:00 2001 From: Shivam Sandbhor Date: Sun, 17 Jan 2021 17:42:12 +0530 Subject: [PATCH] Trim api response and add documentation for the bulk endpoints Signed-off-by: Shivam Sandbhor --- vulnerabilities/api.py | 54 +++++++++++++++++++++++-------- vulnerabilities/tests/test_api.py | 29 ++--------------- 2 files changed, 43 insertions(+), 40 deletions(-) diff --git a/vulnerabilities/api.py b/vulnerabilities/api.py index 2d8fecc32..8eb02ca77 100644 --- a/vulnerabilities/api.py +++ b/vulnerabilities/api.py @@ -22,6 +22,7 @@ # Visit https://github.com/nexB/vulnerablecode/ for support and download. from urllib.parse import unquote +from typing import List from django.db.models import Q from django.urls import reverse @@ -31,11 +32,17 @@ from rest_framework import viewsets from rest_framework.decorators import action from rest_framework.response import Response +from drf_spectacular.utils import extend_schema, inline_serializer +from drf_spectacular.types import OpenApiTypes from vulnerabilities.models import Package from vulnerabilities.models import Vulnerability from vulnerabilities.models import VulnerabilityReference +# This serializer is used for the bulk apis, to prevent wrong auto documentation +# TODO: Fix the swagger documentation for bulk apis +placeholder_serializer = inline_serializer(name="Placeholder", fields={}) + class VulnerabilityReferenceSerializer(serializers.ModelSerializer): class Meta: @@ -63,8 +70,8 @@ class Meta: fields = ["url", "vulnerability_id"] -class VulnerabilitySerializer(serializers.HyperlinkedModelSerializer): - references = VulnerabilityReferenceSerializer(many=True, source="vulnerabilityreference_set") +class MinimalVulnerabilitySerializer(serializers.HyperlinkedModelSerializer): + resolved_packages = HyperLinkedPackageSerializer( many=True, source="resolved_to", read_only=True ) @@ -72,36 +79,43 @@ class VulnerabilitySerializer(serializers.HyperlinkedModelSerializer): many=True, source="vulnerable_to", read_only=True ) + class Meta: + model = Vulnerability + fields = ["url", "unresolved_packages", "resolved_packages"] + + +class VulnerabilitySerializer(MinimalVulnerabilitySerializer): + references = VulnerabilityReferenceSerializer(many=True, source="vulnerabilityreference_set") + class Meta: model = Vulnerability fields = "__all__" -class PackageSerializer(serializers.HyperlinkedModelSerializer): +class MinimalPackageSerializer(serializers.HyperlinkedModelSerializer): unresolved_vulnerabilities = HyperLinkedVulnerabilitySerializer( many=True, source="vulnerable_to", read_only=True ) resolved_vulnerabilities = HyperLinkedVulnerabilitySerializer( many=True, source="resolved_to", read_only=True ) - purl = serializers.CharField(source="package_url") class Meta: model = Package fields = [ - "url", - "type", - "namespace", - "name", - "version", - "qualifiers", - "subpath", - "purl", "resolved_vulnerabilities", "unresolved_vulnerabilities", ] +class PackageSerializer(MinimalPackageSerializer): + purl = serializers.CharField(source="package_url") + + class Meta: + model = Package + exclude = ["vulnerabilities"] + + class PackageFilterSet(filters.FilterSet): purl = filters.CharFilter(method="filter_purl") @@ -129,8 +143,13 @@ class PackageViewSet(viewsets.ReadOnlyModelViewSet): filter_backends = (filters.DjangoFilterBackend,) filterset_class = PackageFilterSet + # TODO: Fix the swagger documentation for this endpoint + @extend_schema(request=placeholder_serializer, responses=placeholder_serializer) @action(detail=False, methods=["post"]) def bulk_search(self, request): + """ + See https://github.com/nexB/vulnerablecode/pull/303#issuecomment-761801639 for docs + """ filter_list = Q() response = {} if not isinstance(request.data.get("packages"), list): @@ -152,7 +171,7 @@ def bulk_search(self, request): response[purl] = {} res = Package.objects.filter(filter_list) for p in res: - response[p.package_url] = PackageSerializer(p, context={"request": request}).data + response[p.package_url] = MinimalPackageSerializer(p, context={"request": request}).data return Response(response) @@ -172,8 +191,13 @@ class VulnerabilityViewSet(viewsets.ReadOnlyModelViewSet): filter_backends = (filters.DjangoFilterBackend,) filterset_class = VulnerabilityFilterSet + # TODO: Fix the swagger documentation for this endpoint + @extend_schema(request=placeholder_serializer, responses=placeholder_serializer) @action(detail=False, methods=["post"]) def bulk_search(self, request): + """ + See https://github.com/nexB/vulnerablecode/pull/303#issuecomment-761801619 for docs + """ filter_list = [] response = {} if not isinstance(request.data.get("vulnerabilities"), list): @@ -190,5 +214,7 @@ def bulk_search(self, request): response[cve_id] = {} res = Vulnerability.objects.filter(cve_id__in=filter_list) for vuln in res: - response[vuln.cve_id] = VulnerabilitySerializer(vuln, context={"request": request}).data + response[vuln.cve_id] = MinimalVulnerabilitySerializer( + vuln, context={"request": request} + ).data return Response(response) diff --git a/vulnerabilities/tests/test_api.py b/vulnerabilities/tests/test_api.py index 37153d890..092349e42 100644 --- a/vulnerabilities/tests/test_api.py +++ b/vulnerabilities/tests/test_api.py @@ -200,8 +200,6 @@ def test_bulk_vulnerabilities_api(self): request_body = {"vulnerabilities": ["CVE-2009-1382", "CVE-2014-8242", "RANDOM-CVE"]} expected_response = { "CVE-2009-1382": { - "url": "http://testserver/api/vulnerabilities/2/", - "references": [], "resolved_packages": [ OrderedDict( [ @@ -217,13 +215,9 @@ def test_bulk_vulnerabilities_api(self): ), ], "unresolved_packages": [], - "cve_id": "CVE-2009-1382", - "summary": "", - "cvss": None, + "url": "http://testserver/api/vulnerabilities/2/", }, "CVE-2014-8242": { - "url": "http://testserver/api/vulnerabilities/1/", - "references": [], "resolved_packages": [], "unresolved_packages": [ OrderedDict( @@ -233,12 +227,11 @@ def test_bulk_vulnerabilities_api(self): ] ) ], - "cve_id": "CVE-2014-8242", - "summary": "", - "cvss": None, + "url": "http://testserver/api/vulnerabilities/1/", }, "RANDOM-CVE": {}, } + response = self.client.post( "/api/vulnerabilities/bulk_search/", data=request_body, content_type="application/json" ).data @@ -256,14 +249,6 @@ def test_bulk_packages_api(self): ).data expected_response = { "pkg:deb/debian/librsync@0.9.7-10?distro=jessie": { - "url": "http://testserver/api/packages/1/", - "type": "deb", - "namespace": "debian", - "name": "librsync", - "version": "0.9.7-10", - "qualifiers": {"distro": "jessie"}, - "subpath": "", - "purl": "pkg:deb/debian/librsync@0.9.7-10?distro=jessie", "resolved_vulnerabilities": [], "unresolved_vulnerabilities": [ OrderedDict( @@ -275,14 +260,6 @@ def test_bulk_packages_api(self): ], }, "pkg:deb/debian/mimetex@1.50-1.1?distro=jessie": { - "url": "http://testserver/api/packages/3/", - "type": "deb", - "namespace": "debian", - "name": "mimetex", - "version": "1.50-1.1", - "qualifiers": {"distro": "jessie"}, - "subpath": "", - "purl": "pkg:deb/debian/mimetex@1.50-1.1?distro=jessie", "resolved_vulnerabilities": [ OrderedDict( [