Skip to content

Commit bf235bb

Browse files
committed
Move fixed_packages to vulnerabilities in packages endpoint #809
Reference: #809 Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com>
1 parent b63d0c0 commit bf235bb

3 files changed

Lines changed: 79 additions & 52 deletions

File tree

CHANGELOG.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ Version v30.0.0
5555
- Paginated initial listings to display a small number of records
5656
and provided page per size with a maximum limit of 100 records per page.
5757

58+
- Add fixed packages in vulnerabilities details in packages endpoint.
59+
5860
Other:
5961

6062
- we dropped calver to use a plain semver.

vulnerabilities/api.py

Lines changed: 55 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,15 @@ class VulnSerializerRefsAndSummary(serializers.HyperlinkedModelSerializer):
5656
Used for nesting inside package focused APIs.
5757
"""
5858

59+
fixed_packages = MinimalPackageSerializer(
60+
many=True, source="filtered_fixed_packages", read_only=True
61+
)
62+
5963
references = VulnerabilityReferenceSerializer(many=True, source="vulnerabilityreference_set")
6064

6165
class Meta:
6266
model = Vulnerability
63-
fields = ["url", "vulnerability_id", "summary", "references"]
67+
fields = ["url", "vulnerability_id", "summary", "references", "fixed_packages"]
6468

6569

6670
class MinimalVulnerabilitySerializer(serializers.HyperlinkedModelSerializer):
@@ -73,21 +77,6 @@ class Meta:
7377
fields = ["url", "vulnerability_id"]
7478

7579

76-
class PackageSerializerFixedVulns(serializers.HyperlinkedModelSerializer):
77-
"""
78-
Used for nesting inside vulnerability focused APIs.
79-
"""
80-
81-
purl = serializers.CharField(source="package_url")
82-
fixing_vulnerabilities = MinimalVulnerabilitySerializer(
83-
many=True, source="resolved_to", read_only=True
84-
)
85-
86-
class Meta:
87-
model = Package
88-
fields = ["url", "purl", "fixing_vulnerabilities"]
89-
90-
9180
class AliasSerializer(serializers.HyperlinkedModelSerializer):
9281
"""
9382
Used for nesting inside package focused APIs.
@@ -128,13 +117,56 @@ def to_representation(self, instance):
128117
return data
129118

130119
purl = serializers.CharField(source="package_url")
131-
affected_by_vulnerabilities = VulnSerializerRefsAndSummary(
132-
many=True, source="vulnerable_to", read_only=True
133-
)
134-
fixing_vulnerabilities = VulnSerializerRefsAndSummary(
135-
many=True, source="resolved_to", read_only=True
136-
)
137-
fixed_packages = PackageSerializerFixedVulns(many=True, read_only=True)
120+
121+
affected_by_vulnerabilities = serializers.SerializerMethodField("get_affected_vulnerabilities")
122+
123+
fixing_vulnerabilities = serializers.SerializerMethodField("get_fixed_vulnerabilities")
124+
125+
def get_fixed_packages(self, package):
126+
"""
127+
Return a queryset of packages that fixes a vulnerability in the given `package`.
128+
"""
129+
return Package.objects.filter(
130+
name=package.name,
131+
namespace=package.namespace,
132+
type=package.type,
133+
qualifiers=package.qualifiers,
134+
subpath=package.subpath,
135+
packagerelatedvulnerability__fix=True,
136+
).distinct()
137+
138+
def get_vulnerabilities_for_a_package(self, package, fix):
139+
"""
140+
Return a queryset of vulnerabilities related to the given `package`.
141+
Return vulnerabilities that affects the `package` if given `fix` flag is False,
142+
otherwise return vulnerabilities fixed by the `package`.
143+
"""
144+
fixed_packages = self.get_fixed_packages(package=package)
145+
qs = package.vulnerabilities.filter(packagerelatedvulnerability__fix=fix)
146+
qs = qs.prefetch_related(
147+
Prefetch(
148+
"packages",
149+
queryset=fixed_packages,
150+
to_attr="filtered_fixed_packages",
151+
)
152+
)
153+
return VulnSerializerRefsAndSummary(
154+
instance=qs,
155+
many=True,
156+
context={"request": self.context["request"]},
157+
).data
158+
159+
def get_fixed_vulnerabilities(self, package):
160+
"""
161+
Return a queryset of vulnerabilities fixed in the given `package`.
162+
"""
163+
return self.get_vulnerabilities_for_a_package(package=package, fix=True)
164+
165+
def get_affected_vulnerabilities(self, package):
166+
"""
167+
Return a queryset of vulnerabilities that affects the given `package`.
168+
"""
169+
return self.get_vulnerabilities_for_a_package(package=package, fix=False)
138170

139171
class Meta:
140172
model = Package
@@ -148,7 +180,6 @@ class Meta:
148180
"qualifiers",
149181
"subpath",
150182
"affected_by_vulnerabilities",
151-
"fixed_packages",
152183
"fixing_vulnerabilities",
153184
]
154185

vulnerabilities/tests/test_fix_api.py

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -139,30 +139,24 @@ def test_api_with_single_vulnerability_and_fixed_package(self):
139139
"namespace": "nginx",
140140
"name": "test",
141141
"version": "11",
142-
"unresolved_vulnerabilities": [],
143142
"qualifiers": {},
144143
"subpath": "",
145-
"fixed_packages": [
146-
{
147-
"url": f"http://testserver/api/packages/{self.package.id}",
148-
"purl": "pkg:generic/nginx/test@11",
149-
"fixing_vulnerabilities": [
150-
{
151-
"url": f"http://testserver/api/vulnerabilities/{self.vuln.id}",
152-
"vulnerability_id": f"VULCOID-{int_to_base36(self.vuln.id).upper()}",
153-
}
154-
],
155-
}
156-
],
157144
"affected_by_vulnerabilities": [],
158145
"fixing_vulnerabilities": [
159146
{
160147
"url": f"http://testserver/api/vulnerabilities/{self.vuln.id}",
161148
"vulnerability_id": f"VULCOID-{int_to_base36(self.vuln.id).upper()}",
162149
"summary": "test-vuln",
163150
"references": [],
164-
}
151+
"fixed_packages": [
152+
{
153+
"url": f"http://testserver/api/packages/{self.package.id}",
154+
"purl": "pkg:generic/nginx/test@11",
155+
}
156+
],
157+
},
165158
],
159+
"unresolved_vulnerabilities": [],
166160
}
167161

168162
def test_api_with_single_vulnerability_and_vulnerable_package(self):
@@ -174,37 +168,37 @@ def test_api_with_single_vulnerability_and_vulnerable_package(self):
174168
"namespace": "nginx",
175169
"name": "test",
176170
"version": "9",
177-
"unresolved_vulnerabilities": [
171+
"qualifiers": {},
172+
"subpath": "",
173+
"affected_by_vulnerabilities": [
178174
{
179175
"url": f"http://testserver/api/vulnerabilities/{self.vuln.id}",
180176
"vulnerability_id": f"VULCOID-{int_to_base36(self.vuln.id).upper()}",
181177
"summary": "test-vuln",
182178
"references": [],
183-
}
184-
],
185-
"qualifiers": {},
186-
"subpath": "",
187-
"fixed_packages": [
188-
{
189-
"url": f"http://testserver/api/packages/{self.package.id}",
190-
"purl": "pkg:generic/nginx/test@11",
191-
"fixing_vulnerabilities": [
179+
"fixed_packages": [
192180
{
193-
"url": f"http://testserver/api/vulnerabilities/{self.vuln.id}",
194-
"vulnerability_id": f"VULCOID-{int_to_base36(self.vuln.id).upper()}",
181+
"url": f"http://testserver/api/packages/{self.package.id}",
182+
"purl": "pkg:generic/nginx/test@11",
195183
}
196184
],
197185
}
198186
],
199-
"affected_by_vulnerabilities": [
187+
"fixing_vulnerabilities": [],
188+
"unresolved_vulnerabilities": [
200189
{
201190
"url": f"http://testserver/api/vulnerabilities/{self.vuln.id}",
202191
"vulnerability_id": f"VULCOID-{int_to_base36(self.vuln.id).upper()}",
203192
"summary": "test-vuln",
204193
"references": [],
194+
"fixed_packages": [
195+
{
196+
"url": f"http://testserver/api/packages/{self.package.id}",
197+
"purl": "pkg:generic/nginx/test@11",
198+
}
199+
],
205200
}
206201
],
207-
"fixing_vulnerabilities": [],
208202
}
209203

210204

0 commit comments

Comments
 (0)