Skip to content

Commit

Permalink
Add resource URL to the vulnerability and package details view in the…
Browse files Browse the repository at this point in the history
… API serializers (aboutcode-org#1423)

* Add vulnerability_url in API

Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com>

* Add package_url in API

Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com>

* Fix tests

Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com>

* Address review comments

Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com>

* Address review comments

Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com>

* Fix tests

Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com>

---------

Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com>
  • Loading branch information
TG1999 committed Jul 19, 2024
1 parent abe84b0 commit 02effb2
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 48 deletions.
46 changes: 36 additions & 10 deletions vulnerabilities/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,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 rest_framework.throttling import UserRateThrottle

Expand Down Expand Up @@ -48,7 +49,32 @@ class Meta:
fields = ["reference_url", "reference_id", "scores", "url"]


class MinimalPackageSerializer(serializers.HyperlinkedModelSerializer):
class BaseResourceSerializer(serializers.HyperlinkedModelSerializer):
"""
Base serializer containing common methods.
"""

def get_fields(self):
fields = super().get_fields()
fields["resource_url"] = serializers.SerializerMethodField(method_name="get_resource_url")
return fields

def get_resource_url(self, instance):
"""
Return the instance fully qualified URL including the schema and domain.
Usage:
resource_url = serializers.SerializerMethodField()
"""
resource_url = instance.get_absolute_url()

if request := self.context.get("request", None):
return request.build_absolute_uri(location=resource_url)

return resource_url


class MinimalPackageSerializer(BaseResourceSerializer):
"""
Used for nesting inside vulnerability focused APIs.
"""
Expand Down Expand Up @@ -79,7 +105,7 @@ class Meta:
fields = ["url", "purl", "is_vulnerable", "affected_by_vulnerabilities"]


class MinimalVulnerabilitySerializer(serializers.HyperlinkedModelSerializer):
class MinimalVulnerabilitySerializer(BaseResourceSerializer):
"""
Lookup vulnerabilities by aliases (such as a CVE).
"""
Expand All @@ -99,7 +125,7 @@ class Meta:
fields = ["alias"]


class VulnSerializerRefsAndSummary(serializers.HyperlinkedModelSerializer):
class VulnSerializerRefsAndSummary(BaseResourceSerializer):
"""
Lookup vulnerabilities references by aliases (such as a CVE).
"""
Expand Down Expand Up @@ -141,7 +167,7 @@ def to_representation(self, instance):
return representation


class VulnerabilitySerializer(serializers.HyperlinkedModelSerializer):
class VulnerabilitySerializer(BaseResourceSerializer):
fixed_packages = MinimalPackageSerializer(
many=True, source="filtered_fixed_packages", read_only=True
)
Expand All @@ -152,13 +178,12 @@ class VulnerabilitySerializer(serializers.HyperlinkedModelSerializer):
weaknesses = WeaknessSerializer(many=True)

def to_representation(self, instance):
representation = super().to_representation(instance)
data = super().to_representation(instance)

# Exclude None values from the weaknesses list
weaknesses = representation.get("weaknesses", [])
representation["weaknesses"] = [weakness for weakness in weaknesses if weakness is not None]
weaknesses = data.get("weaknesses", [])
data["weaknesses"] = [weakness for weakness in weaknesses if weakness is not None]

return representation
return data

class Meta:
model = Vulnerability
Expand All @@ -174,14 +199,15 @@ class Meta:
]


class PackageSerializer(serializers.HyperlinkedModelSerializer):
class PackageSerializer(BaseResourceSerializer):
"""
Lookup software package using Package URLs
"""

def to_representation(self, instance):
data = super().to_representation(instance)
data["qualifiers"] = normalize_qualifiers(data["qualifiers"], encode=False)

return data

next_non_vulnerable_version = serializers.SerializerMethodField("get_next_non_vulnerable")
Expand Down
20 changes: 20 additions & 0 deletions vulnerabilities/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,18 @@ def get_absolute_url(self):
"""
return reverse("vulnerability_details", args=[self.vulnerability_id])

def get_details_url(self, request):
"""
Return this Package details URL.
"""
from rest_framework.reverse import reverse

return reverse(
"vulnerability_details",
kwargs={"vulnerability_id": self.vulnerability_id},
request=request,
)

def get_related_cpes(self):
"""
Return a list of CPE strings of this vulnerability.
Expand Down Expand Up @@ -633,6 +645,14 @@ def get_absolute_url(self):
"""
return reverse("package_details", args=[self.purl])

def get_details_url(self, request):
"""
Return this Package details URL.
"""
from rest_framework.reverse import reverse

return reverse("package_details", kwargs={"purl": self.purl}, request=request)

def sort_by_version(self, packages):
"""
Return a list of `packages` sorted by version.
Expand Down
139 changes: 101 additions & 38 deletions vulnerabilities/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import json
import os
from collections import OrderedDict
from urllib.parse import quote

from django.test import TestCase
Expand Down Expand Up @@ -223,18 +224,21 @@ def test_api_with_single_vulnerability(self):
"vulnerability_id": self.vulnerability.vulnerability_id,
"summary": "test",
"aliases": [],
"resource_url": f"http://testserver/vulnerabilities/{self.vulnerability.vulnerability_id}",
"fixed_packages": [
{
"url": f"http://testserver/api/packages/{self.pkg2.id}",
"purl": "pkg:deb/flask@0.1.2",
"is_vulnerable": False,
"affected_by_vulnerabilities": [],
"resource_url": f"http://testserver/packages/{self.pkg2.purl}",
},
{
"url": f"http://testserver/api/packages/{self.pkg1.id}",
"purl": "pkg:pypi/flask@0.1.2",
"is_vulnerable": False,
"affected_by_vulnerabilities": [],
"resource_url": f"http://testserver/packages/{self.pkg1.purl}",
},
],
"affected_packages": [],
Expand All @@ -257,11 +261,13 @@ def test_api_with_single_vulnerability_with_filters(self):
"vulnerability_id": self.vulnerability.vulnerability_id,
"summary": "test",
"aliases": [],
"resource_url": f"http://testserver/vulnerabilities/{self.vulnerability.vulnerability_id}",
"fixed_packages": [
{
"url": f"http://testserver/api/packages/{self.pkg1.id}",
"purl": "pkg:pypi/flask@0.1.2",
"is_vulnerable": False,
"resource_url": f"http://testserver/packages/{self.pkg1.purl}",
"affected_by_vulnerabilities": [],
},
],
Expand Down Expand Up @@ -443,49 +449,106 @@ def test_api_with_lesser_and_greater_fixed_by_packages(self):
"next_non_vulnerable_version": "2.14.0-rc1",
"latest_non_vulnerable_version": "2.14.0-rc1",
"affected_by_vulnerabilities": [
{
"url": f"http://testserver/api/vulnerabilities/{self.vuln_VCID_2nyb_8rwu_aaag.id}",
"vulnerability_id": "VCID-2nyb-8rwu-aaag",
"summary": "This is VCID-2nyb-8rwu-aaag",
"references": [],
"fixed_packages": [
{
"url": f"http://testserver/api/packages/{self.package_maven_jackson_databind_2_13_2.id}",
"purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2",
"is_vulnerable": True,
"affected_by_vulnerabilities": [
{"vulnerability": "VCID-gqhw-ngh8-aaap"}
OrderedDict(
[
(
"url",
f"http://testserver/api/vulnerabilities/{self.vuln_VCID_2nyb_8rwu_aaag.id}",
),
("vulnerability_id", "VCID-2nyb-8rwu-aaag"),
("summary", "This is VCID-2nyb-8rwu-aaag"),
("references", []),
(
"fixed_packages",
[
OrderedDict(
[
(
"url",
f"http://testserver/api/packages/{self.package_maven_jackson_databind_2_13_2.id}",
),
(
"purl",
"pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2",
),
("is_vulnerable", True),
(
"affected_by_vulnerabilities",
[{"vulnerability": "VCID-gqhw-ngh8-aaap"}],
),
(
"resource_url",
"http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2",
),
]
)
],
}
],
"aliases": ["CVE-2020-36518", "GHSA-57j2-w4cx-62h2"],
}
),
("aliases", ["CVE-2020-36518", "GHSA-57j2-w4cx-62h2"]),
("resource_url", "http://testserver/vulnerabilities/VCID-2nyb-8rwu-aaag"),
]
)
],
"fixing_vulnerabilities": [
{
"url": f"http://testserver/api/vulnerabilities/{self.vuln_VCID_ftmk_wbwx_aaar.id}",
"vulnerability_id": "VCID-ftmk-wbwx-aaar",
"summary": "This is VCID-ftmk-wbwx-aaar",
"references": [],
"fixed_packages": [
{
"url": f"http://testserver/api/packages/{self.package_maven_jackson_databind_2_12_6.id}",
"purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6",
"is_vulnerable": False,
"affected_by_vulnerabilities": [],
},
{
"url": f"http://testserver/api/packages/{self.package_maven_jackson_databind_2_13_1.id}",
"purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
"is_vulnerable": True,
"affected_by_vulnerabilities": [
{"vulnerability": "VCID-2nyb-8rwu-aaag"}
OrderedDict(
[
(
"url",
f"http://testserver/api/vulnerabilities/{self.vuln_VCID_ftmk_wbwx_aaar.id}",
),
("vulnerability_id", "VCID-ftmk-wbwx-aaar"),
("summary", "This is VCID-ftmk-wbwx-aaar"),
("references", []),
(
"fixed_packages",
[
OrderedDict(
[
(
"url",
f"http://testserver/api/packages/{self.package_maven_jackson_databind_2_12_6.id}",
),
(
"purl",
"pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6",
),
("is_vulnerable", False),
("affected_by_vulnerabilities", []),
(
"resource_url",
"http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6",
),
]
),
OrderedDict(
[
(
"url",
f"http://testserver/api/packages/{self.package_maven_jackson_databind_2_13_1.id}",
),
(
"purl",
"pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
),
("is_vulnerable", True),
(
"affected_by_vulnerabilities",
[{"vulnerability": "VCID-2nyb-8rwu-aaag"}],
),
(
"resource_url",
"http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
),
]
),
],
},
],
"aliases": ["CVE-2021-46877", "GHSA-3x8x-79m2-3w2w"],
},
),
("aliases", ["CVE-2021-46877", "GHSA-3x8x-79m2-3w2w"]),
("resource_url", "http://testserver/vulnerabilities/VCID-ftmk-wbwx-aaar"),
]
)
],
"resource_url": "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
}

assert response == expected_response
Expand Down

0 comments on commit 02effb2

Please sign in to comment.