Skip to content

Commit 050d580

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 050d580

3 files changed

Lines changed: 80 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: 56 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,57 @@ 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 all packages that fixes a vulnerability with
128+
same type, namespace, name, subpath and qualifiers of the `package`
129+
"""
130+
return Package.objects.filter(
131+
name=package.name,
132+
namespace=package.namespace,
133+
type=package.type,
134+
qualifiers=package.qualifiers,
135+
subpath=package.subpath,
136+
packagerelatedvulnerability__fix=True,
137+
).distinct()
138+
139+
def get_vulnerabilities_for_a_package(self, package, fix):
140+
"""
141+
Return a queryset of vulnerabilities related to the given `package`.
142+
Return vulnerabilities that affects the `package` if given `fix` flag is False,
143+
otherwise return vulnerabilities fixed by the `package`.
144+
"""
145+
fixed_packages = self.get_fixed_packages(package=package)
146+
qs = package.vulnerabilities.filter(packagerelatedvulnerability__fix=fix)
147+
qs = qs.prefetch_related(
148+
Prefetch(
149+
"packages",
150+
queryset=fixed_packages,
151+
to_attr="filtered_fixed_packages",
152+
)
153+
)
154+
return VulnSerializerRefsAndSummary(
155+
instance=qs,
156+
many=True,
157+
context={"request": self.context["request"]},
158+
).data
159+
160+
def get_fixed_vulnerabilities(self, package):
161+
"""
162+
Return a queryset of vulnerabilities fixed in the given `package`.
163+
"""
164+
return self.get_vulnerabilities_for_a_package(package=package, fix=True)
165+
166+
def get_affected_vulnerabilities(self, package):
167+
"""
168+
Return a queryset of vulnerabilities that affects the given `package`.
169+
"""
170+
return self.get_vulnerabilities_for_a_package(package=package, fix=False)
138171

139172
class Meta:
140173
model = Package
@@ -148,7 +181,6 @@ class Meta:
148181
"qualifiers",
149182
"subpath",
150183
"affected_by_vulnerabilities",
151-
"fixed_packages",
152184
"fixing_vulnerabilities",
153185
]
154186

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)