Skip to content

Commit 772848f

Browse files
committed
Use V2 views
Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com>
1 parent 2b31e17 commit 772848f

File tree

4 files changed

+156
-65
lines changed

4 files changed

+156
-65
lines changed

vulnerabilities/templates/package_details_v2.html

Lines changed: 10 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,6 @@
136136
<thead>
137137
<tr>
138138
<th style="width: 200px;">Advisory</th>
139-
<th style="width: 310px;">Source</th>
140-
<th style="width: 200px;">Date Published</th>
141139
<th>Summary</th>
142140
<th style="width: 310px;">Fixed in package version</th>
143141
</tr>
@@ -147,15 +145,15 @@
147145
{% for advisory in affected_by_advisories_v2 %}
148146
<tr>
149147
<td>
150-
<a href="{{advisory.primary.get_absolute_url}}">
151-
{{advisory.primary.avid }}
148+
<a href="{{advisory.advisory.get_absolute_url}}">
149+
{{advisory.identifier }}
152150
</a>
153151
<br />
154-
{% if advisory.primary.alias|length != 0 %}
152+
{% if advisory.aliases|length != 0 %}
155153
Aliases:
156154
{% endif %}
157155
<br />
158-
{% for alias in advisory.primary.alias %}
156+
{% for alias in advisory.aliases %}
159157
{% if alias.url %}
160158
<a href="{{ alias.url }}" target="_blank">{{ alias }}<i
161159
class="fa fa-external-link fa_link_custom"></i></a>
@@ -166,26 +164,12 @@
166164
{% endif %}
167165
{% endfor %}
168166

169-
{% if advisory.secondary|length != 0 %}
170-
<p>Supporting advisories are listed below the primary advisory.</p>
171-
{% for secondary in advisory.secondary %}
172-
<a href="{{secondary.get_absolute_url}}">
173-
{{secondary.avid }}
174-
</a>
175-
{% endfor %}
176-
{% endif %}
177167
</td>
178168
<td style="word-wrap: break-word; word-break: break-word;">
179-
<a href={{advisory.primary.url}} target="_blank">{{advisory.primary.url}}</a>
180-
</td>
181-
<td style="word-wrap: break-word; word-break: break-word;">
182-
{{advisory.primary.date_published}}
183-
</td>
184-
<td style="word-wrap: break-word; word-break: break-word;">
185-
{{ advisory.primary.summary }}
169+
{{ advisory.advisory.summary|truncatewords:20 }}
186170
</td>
187171
<td style="word-wrap: break-word; word-break: break-all;">
188-
{% with fixed=fixed_package_details|get_item:advisory.primary.avid %}
172+
{% with fixed=fixed_package_details|get_item:advisory.advisory.avid %}
189173
{% if fixed %}
190174
{% for item in fixed %}
191175
<section>
@@ -240,8 +224,6 @@
240224
<thead>
241225
<tr>
242226
<th style="width: 200px;">Advisory</th>
243-
<th style="width: 225px;">Source</th>
244-
<th style="width: 225px;">Date Published</th>
245227
<th>Summary</th>
246228
<th style="width: 225px;">Aliases</th>
247229
</tr>
@@ -250,30 +232,16 @@
250232
{% for advisory in fixing_advisories_v2 %}
251233
<tr>
252234
<td>
253-
<a href="{{advisory.primary.get_absolute_url}}">
254-
{{advisory.primary.avid }}
235+
<a href="{{advisory.advisory.get_absolute_url}}">
236+
{{advisory.identifier }}
255237
</a>
256238
<br />
257-
{% if advisory.secondary|length != 0 %}
258-
<p>Supporting advisories are listed below the primary advisory.</p>
259-
{% for secondary in advisory.secondary %}
260-
<a href="{{secondary.get_absolute_url}}">
261-
{{secondary.avid }}
262-
</a>
263-
{% endfor %}
264-
{% endif %}
265-
</td>
266-
<td>
267-
<a href={{advisory.primary.url}} target="_blank">{{advisory.primary.url}}</a>
268-
</td>
269-
<td>
270-
{{advisory.primary.date_published}}
271239
</td>
272240
<td>
273-
{{ advisory.primary.summary }}
241+
{{ advisory.advisory.summary|truncatewords:20 }}
274242
</td>
275243
<td>
276-
{% for alias in advisory.primary.alias %}
244+
{% for alias in advisory.aliases %}
277245
{% if alias.url %}
278246
<a href="{{ alias.url }}" target="_blank">{{ alias }}<i
279247
class="fa fa-external-link fa_link_custom"></i></a>

vulnerabilities/utils.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -895,3 +895,103 @@ def compute_advisory_content(advisory_data):
895895
content_hash = hashlib.sha256(normalized_json.encode("utf-8")).hexdigest()
896896

897897
return content_hash
898+
899+
900+
def merge_advisories(advisories, package):
901+
902+
advisories = list(advisories)
903+
904+
content_hash_map = defaultdict(list)
905+
906+
for adv in advisories:
907+
content_hash = compute_advisory_content_hash(adv, package)
908+
content_hash_map[content_hash].append(adv)
909+
910+
final_groups = []
911+
912+
for group in content_hash_map.values():
913+
groups = get_merged_identifier_groups(group)
914+
final_groups.extend(groups)
915+
916+
return final_groups
917+
918+
919+
def compute_advisory_content_hash(adv, package):
920+
affected = []
921+
fixed = []
922+
923+
version_less_purl = PackageURL(
924+
type=package.type,
925+
namespace=package.namespace,
926+
name=package.name,
927+
qualifiers=package.qualifiers,
928+
subpath=package.subpath,
929+
)
930+
931+
for impact in adv.impacted_packages.filter(base_purl=str(version_less_purl)):
932+
affected.extend([pkg.package_url for pkg in impact.affecting_packages.all()])
933+
fixed.extend([pkg.package_url for pkg in impact.fixed_by_packages.all()])
934+
935+
normalized_data = {
936+
"affected_packages": normalize_list(affected),
937+
"fixed_packages": normalize_list(fixed),
938+
}
939+
940+
normalized_json = json.dumps(normalized_data, separators=(",", ":"), sort_keys=True)
941+
content_hash = hashlib.sha256(normalized_json.encode("utf-8")).hexdigest()
942+
return content_hash
943+
944+
945+
def get_merged_identifier_groups(advisories):
946+
947+
identifier_groups = defaultdict(set)
948+
949+
advisories = list(advisories)
950+
951+
for adv in advisories:
952+
953+
identifier_groups[adv.advisory_id].add(adv)
954+
955+
for alias in adv.aliases.values_list("alias", flat=True):
956+
identifier_groups[alias].add(adv)
957+
958+
groups = [set(advs) for advs in identifier_groups.values() if len(advs) > 1]
959+
960+
merged = []
961+
962+
for group in groups:
963+
group = set(group)
964+
965+
i = 0
966+
while i < len(merged):
967+
if group & merged[i]:
968+
group |= merged[i]
969+
merged.pop(i)
970+
else:
971+
i += 1
972+
973+
merged.append(group)
974+
975+
all_grouped = set()
976+
for g in merged:
977+
all_grouped |= g
978+
979+
for adv in advisories:
980+
if adv not in all_grouped:
981+
merged.append({adv})
982+
983+
final_groups = []
984+
985+
for group in merged:
986+
identifiers = set()
987+
for adv in group:
988+
for alias in adv.aliases.all():
989+
identifiers.add(alias)
990+
991+
primary = max(group, key=lambda a: a.precedence if a.precedence is not None else -1)
992+
993+
secondary = [a for a in group if a != primary]
994+
995+
final_groups.append((identifiers, primary, secondary))
996+
997+
return final_groups

vulnerabilities/views.py

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,15 @@
3838
from vulnerabilities.forms import PipelineSchedulePackageForm
3939
from vulnerabilities.forms import VulnerabilitySearchForm
4040
from vulnerabilities.models import AdvisorySetMember
41+
from vulnerabilities.models import AdvisoryV2
4142
from vulnerabilities.models import ImpactedPackage
4243
from vulnerabilities.models import PipelineRun
4344
from vulnerabilities.models import PipelineSchedule
4445
from vulnerabilities.pipelines.v2_importers.epss_importer_v2 import EPSSImporterPipeline
4546
from vulnerabilities.severity_systems import EPSS
4647
from vulnerabilities.severity_systems import SCORING_SYSTEMS
4748
from vulnerabilities.utils import group_advisories_by_content
49+
from vulnerabilities.utils import merge_advisories
4850
from vulnerablecode import __version__ as VULNERABLECODE_VERSION
4951
from vulnerablecode.settings import env
5052

@@ -218,22 +220,30 @@ def get_context_data(self, **kwargs):
218220
context["latest_non_vulnerable"] = latest_non_vulnerable
219221
context["package_search_form"] = PackageSearchForm(self.request.GET)
220222

221-
affected_by_advisories_qs = models.AdvisoryV2.objects.latest_affecting_advisories_for_purl(
222-
package.package_url
223+
affecting_advisories = AdvisoryV2.objects.latest_affecting_advisories_for_purl(
224+
purl=package.purl
225+
).prefetch_related(
226+
"aliases",
227+
"impacted_packages__affecting_packages",
228+
"impacted_packages__fixed_by_packages",
223229
)
224230

225-
fixing_advisories_qs = models.AdvisoryV2.objects.latest_fixed_by_advisories_for_purl(
226-
package.package_url
231+
fixed_by_advisories = AdvisoryV2.objects.latest_fixed_by_advisories_for_purl(
232+
purl=package.purl
233+
).prefetch_related(
234+
"aliases",
235+
"impacted_packages__affecting_packages",
236+
"impacted_packages__fixed_by_packages",
227237
)
228238

229239
affected_by_advisories_url = None
230240
fixing_advisories_url = None
231241

232-
affected_by_advisories_qs_ids = affected_by_advisories_qs.only("id")
233-
fixing_advisories_qs_ids = fixing_advisories_qs.only("id")
242+
affected_by_advisories_qs_ids = affecting_advisories.only("id")
243+
fixing_advisories_qs_ids = fixed_by_advisories.only("id")
234244

235-
affected_by_advisories = list(affected_by_advisories_qs_ids[:101])
236-
if len(affected_by_advisories) > 100:
245+
affected_by_advisories = list(affected_by_advisories_qs_ids[:1001])
246+
if len(affected_by_advisories) > 1001:
237247
affected_by_advisories_url = reverse_lazy(
238248
"affected_by_advisories_v2", kwargs={"purl": package.package_url}
239249
)
@@ -242,33 +252,46 @@ def get_context_data(self, **kwargs):
242252
context["fixed_package_details"] = {}
243253

244254
else:
255+
advisories = []
256+
245257
fixed_pkg_details = get_fixed_package_details(package)
246-
affected_avid_by_hash = {}
247-
affected_avid_by_hash = group_advisories_by_content(affected_by_advisories_qs)
248-
affecting_advs = []
258+
groups = merge_advisories(affecting_advisories, package)
259+
for aliases, primary, _ in groups:
260+
identifier = primary.advisory_id.split("/")[-1]
261+
262+
filtered_aliases = [alias for alias in aliases if alias.alias != identifier]
263+
264+
advisories.append(
265+
{"aliases": filtered_aliases, "advisory": primary, "identifier": identifier}
266+
)
249267

250-
for hash in affected_avid_by_hash:
251-
affecting_advs.append(affected_avid_by_hash[hash])
252-
context["affected_by_advisories_v2"] = affecting_advs
268+
context["affected_by_advisories_v2"] = advisories
253269
context["fixed_package_details"] = fixed_pkg_details
254270
context["affected_by_advisories_v2_url"] = None
255271

256-
fixing_advisories = list(fixing_advisories_qs_ids[:101])
257-
if len(fixing_advisories) > 100:
272+
fixing_advisories = list(fixing_advisories_qs_ids[:1001])
273+
if len(fixing_advisories) > 1001:
258274
fixing_advisories_url = reverse_lazy(
259275
"fixing_advisories_v2", kwargs={"purl": package.package_url}
260276
)
261277
context["fixing_advisories_v2_url"] = fixing_advisories_url
262278
context["fixing_advisories_v2"] = []
263279

264280
else:
265-
fixing_avid_by_hash = {}
266-
fixing_avid_by_hash = group_advisories_by_content(fixing_advisories_qs)
267-
fixing_advs = []
281+
advisories = []
282+
283+
fixed_pkg_details = get_fixed_package_details(package)
284+
groups = merge_advisories(fixing_advisories, package)
285+
for aliases, primary, _ in groups:
286+
identifier = primary.advisory_id.split("/")[-1]
287+
288+
filtered_aliases = [alias for alias in aliases if alias.alias != identifier]
289+
290+
advisories.append(
291+
{"aliases": filtered_aliases, "advisory": primary, "identifier": identifier}
292+
)
268293

269-
for hash in fixing_avid_by_hash:
270-
fixing_advs.append(fixing_avid_by_hash[hash])
271-
context["fixing_advisories_v2"] = fixing_advs
294+
context["fixing_advisories_v2"] = advisories
272295
context["fixing_advisories_v2_url"] = None
273296

274297
return context

vulnerablecode/urls.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def __init__(self, *args, **kwargs):
142142
),
143143
re_path(
144144
r"^packages/v2/(?P<purl>pkg:.+)$",
145-
PackageV3Details.as_view(),
145+
PackageV2Details.as_view(),
146146
name="package_details_v2",
147147
),
148148
re_path(

0 commit comments

Comments
 (0)