Skip to content

Commit c6aff9c

Browse files
authored
Merge pull request #2179 from ziadhany/api-commit-patch
Add API/ UI support for Patch/PackageCommitPatch
2 parents 73b2f75 + 1625418 commit c6aff9c

6 files changed

Lines changed: 529 additions & 5 deletions

File tree

vulnerabilities/api_v3.py

Lines changed: 127 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
from django.contrib.postgres.aggregates import ArrayAgg
1414
from django.db.models import Exists
15+
from django.db.models import F
1516
from django.db.models import Max
1617
from django.db.models import OuterRef
1718
from django.db.models import Prefetch
@@ -46,6 +47,7 @@ class PackageQuerySerializer(serializers.Serializer):
4647
details = serializers.BooleanField(default=False)
4748
ignore_qualifiers_subpath = serializers.BooleanField(default=False)
4849
max_advisories = serializers.IntegerField(default=100, min_value=1, max_value=10000)
50+
reachability = serializers.BooleanField(default=False)
4951

5052
def validate(self, data):
5153
if not data["purls"]:
@@ -258,6 +260,7 @@ def create(self, request, *args, **kwargs):
258260

259261
purls = serializer.validated_data["purls"]
260262
details = serializer.validated_data["details"]
263+
reachability = serializer.validated_data["reachability"]
261264
ignore_qualifiers_subpath = serializer.validated_data["ignore_qualifiers_subpath"]
262265
max_advisories = serializer.validated_data["max_advisories"]
263266

@@ -316,7 +319,9 @@ def create(self, request, *args, **kwargs):
316319
if request:
317320
base_url = request.build_absolute_uri("/")[:-1]
318321
page = self.paginate_queryset(query)
319-
affected_advisory_map = get_affected_advisories_bulk(page, max_advisories, base_url)
322+
affected_advisory_map = get_affected_advisories_bulk(
323+
page, max_advisories, base_url, reachability
324+
)
320325
fixing_advisory_map = get_fixing_advisories_bulk(page, max_advisories, base_url)
321326
serializer = self.get_serializer(
322327
page,
@@ -449,7 +454,99 @@ class AffectedByAdvisoriesViewSet(PackageAdvisoriesViewSet):
449454
serializer_class = AffectedByAdvisoryV3Serializer
450455

451456

452-
def get_affected_advisories_bulk(packages, max_advisories, base_url):
457+
def get_patches_bulk(package_ids, advisory_ids):
458+
"""Get introduced and fixed patches"""
459+
460+
base_qs = ImpactedPackageAffecting.objects.filter(
461+
package_id__in=package_ids,
462+
impacted_package__advisory_id__in=advisory_ids,
463+
impacted_package__advisory__is_latest=True,
464+
impacted_package__advisory___all_impacts_unfurled_at__isnull=False,
465+
)
466+
467+
introduced_rows = base_qs.filter(
468+
impacted_package__introduced_by_package_commit_patches__isnull=False
469+
).values(
470+
"package_id",
471+
"impacted_package__advisory_id",
472+
commit_hash=F("impacted_package__introduced_by_package_commit_patches__commit_hash"),
473+
vcs_url=F("impacted_package__introduced_by_package_commit_patches__vcs_url"),
474+
)
475+
476+
fixed_rows = base_qs.filter(
477+
impacted_package__fixed_by_package_commit_patches__isnull=False
478+
).values(
479+
"package_id",
480+
"impacted_package__advisory_id",
481+
commit_hash=F("impacted_package__fixed_by_package_commit_patches__commit_hash"),
482+
vcs_url=F("impacted_package__fixed_by_package_commit_patches__vcs_url"),
483+
)
484+
485+
introduced_patches_map = defaultdict(list)
486+
fixed_patches_map = defaultdict(list)
487+
488+
for row in introduced_rows:
489+
if row["commit_hash"] or row["vcs_url"]:
490+
key = (row["package_id"], row["impacted_package__advisory_id"])
491+
introduced_patches_map[key].append(
492+
{
493+
"commit_hash": row["commit_hash"],
494+
"vcs_url": row["vcs_url"],
495+
}
496+
)
497+
498+
for row in fixed_rows:
499+
if row["commit_hash"] or row["vcs_url"]:
500+
key = (row["package_id"], row["impacted_package__advisory_id"])
501+
fixed_patches_map[key].append(
502+
{
503+
"commit_hash": row["commit_hash"],
504+
"vcs_url": row["vcs_url"],
505+
}
506+
)
507+
508+
return introduced_patches_map, fixed_patches_map
509+
510+
511+
def build_patch_set_map(patches_map, advisory_ids_by_set):
512+
"""
513+
Returns:
514+
{
515+
advisory_set_id: [
516+
{
517+
"commit_hash": "...",
518+
"vcs_url": "...",
519+
}
520+
]
521+
}
522+
"""
523+
result = {}
524+
525+
for advisory_set_id, advisory_ids in advisory_ids_by_set.items():
526+
seen = set()
527+
collected = []
528+
529+
for (package_id, advisory_id), patches in patches_map.items():
530+
if advisory_id not in advisory_ids:
531+
continue
532+
533+
for patch in patches:
534+
key = (
535+
patch["commit_hash"],
536+
patch["vcs_url"],
537+
)
538+
539+
if key in seen:
540+
continue
541+
542+
seen.add(key)
543+
collected.append(patch)
544+
545+
result[advisory_set_id] = collected
546+
return result
547+
548+
549+
def get_affected_advisories_bulk(packages, max_advisories, base_url, reachability=False):
453550
package_ids = [p.id for p in packages]
454551

455552
package_ids_with_multiple_importers = PackageV2.objects.filter(
@@ -542,6 +639,24 @@ def get_affected_advisories_bulk(packages, max_advisories, base_url):
542639
advisory_ids_by_set[advisory_set_id].add(advisory_id)
543640
all_advisory_ids.add(advisory_id)
544641

642+
introduced_patches_by_set = {}
643+
fixed_patches_by_set = {}
644+
if reachability:
645+
introduced_patches_map, fixed_patches_map = get_patches_bulk(
646+
package_ids,
647+
advisory_ids=all_advisory_ids,
648+
)
649+
650+
introduced_patches_by_set = build_patch_set_map(
651+
introduced_patches_map,
652+
advisory_ids_by_set,
653+
)
654+
655+
fixed_patches_by_set = build_patch_set_map(
656+
fixed_patches_map,
657+
advisory_ids_by_set,
658+
)
659+
545660
ssvc_rows = (
546661
SSVC.objects.filter(
547662
related_advisories__id__in=all_advisory_ids,
@@ -613,7 +728,6 @@ def get_affected_advisories_bulk(packages, max_advisories, base_url):
613728

614729
for adv in groups:
615730
primary = adv.primary_advisory
616-
617731
fixed_by_packages = impact_by_package_and_advisory.get(
618732
(package.id, primary.id),
619733
[],
@@ -647,6 +761,14 @@ def get_affected_advisories_bulk(packages, max_advisories, base_url):
647761
"exploitability": exploitability,
648762
"risk_score": risk_score,
649763
"fixed_by_packages": fixed_by_packages,
764+
"introduced_in_patch": introduced_patches_by_set.get(
765+
adv.id,
766+
[],
767+
),
768+
"fixed_in_patch": fixed_patches_by_set.get(
769+
adv.id,
770+
[],
771+
),
650772
"ssvc_trees": adv.ssvc_trees,
651773
"resource_url": resource_url,
652774
}
@@ -742,6 +864,8 @@ def get_affected_advisories_bulk(packages, max_advisories, base_url):
742864
"exploitability": advisory.exploitability,
743865
"risk_score": advisory.risk_score,
744866
"fixed_by_packages": fixed_by_packages,
867+
"introduced_in_patch": introduced_patches_by_set.get(advisory_id, []),
868+
"fixed_in_patch": introduced_patches_by_set.get(advisory_id, []),
745869
"ssvc_trees": [
746870
{
747871
"vector": ssvc.vector,

vulnerabilities/templates/advisory_detail.html

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,16 @@
8080
</a>
8181
</li>
8282
{% endif %}
83-
83+
84+
<li data-tab="patch-url">
85+
<a>
86+
<span>
87+
{% with pcp_length=package_commit_patches|length %}
88+
Patches: ({{ advisory.patches.count|add:pcp_length }})
89+
{% endwith %}
90+
</span>
91+
</a>
92+
</li>
8493
<!-- <li data-tab="history">
8594
<a>
8695
<span>
@@ -184,6 +193,18 @@
184193
</a>
185194
</td>
186195
</tr>
196+
<tr>
197+
<td class="two-col-left"
198+
data-tooltip="Risk expressed as a number ranging from 0 to 10. It is calculated by multiplying
199+
the weighted severity and exploitability values, capped at a maximum of 10.
200+
"
201+
>Introduced and Fixed Package Commit Patches</td>
202+
<td class="two-col-right wrap-strings">
203+
<a href="/advisories/commits/{{ advisory.avid }}">
204+
Package Commit Patches Details
205+
</a>
206+
</td>
207+
</tr>
187208
</tbody>
188209
</table>
189210
<div class="has-text-weight-bold tab-nested-div ml-1 mb-1 mt-6">
@@ -436,7 +457,6 @@
436457
</tr>
437458
{% endfor %}
438459
</div>
439-
440460

441461
<div class="tab-div content" data-content="epss">
442462
{% if epss_data %}
@@ -503,6 +523,28 @@
503523
{% endif %}
504524
</div>
505525

526+
<div class="tab-div content" data-content="patch-url">
527+
<table class="table is-bordered is-striped is-narrow is-hoverable is-fullwidth">
528+
<thead>
529+
<tr>
530+
<th style="width: 250px;"> Patch URL </th>
531+
</tr>
532+
</thead>
533+
{% for patch in patches %}
534+
<tr>
535+
<td class="wrap-strings"><a href="{{ patch.patch_url }}" target="_blank">{{ patch.patch_url }}<i
536+
class="fa fa-external-link fa_link_custom"></i></a></td>
537+
</tr>
538+
{% empty %}
539+
<tr>
540+
<td colspan="2">
541+
There are no known patches.
542+
</td>
543+
</tr>
544+
{% endfor %}
545+
</table>
546+
</div>
547+
506548
<div class="tab-div content" data-content="severities-vectors">
507549
{% for severity_vector in severity_vectors %}
508550
{% if severity_vector.vector.version == '2.0' %}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
{% extends "base.html" %}
2+
{% load humanize %}
3+
{% load widget_tweaks %}
4+
{% load static %}
5+
{% load show_cvss %}
6+
{% load url_filters %}
7+
8+
{% block title %}
9+
VulnerableCode Advisory Package Commit Patch Details - {{ advisoryv2.advisory_id }}
10+
{% endblock %}
11+
12+
{% block content %}
13+
14+
{% if advisoryv2 %}
15+
<section class="section pt-4">
16+
<div class="details-container">
17+
<article class="panel is-info panel-header-only">
18+
<div class="panel-heading py-2 is-size-6">
19+
Introduce and Fixing Package Commit Patch details for Advisory:
20+
<span class="tag is-white custom">
21+
{{ advisoryv2.advisory_id }}
22+
</span>
23+
</div>
24+
</article>
25+
26+
<div id="tab-content">
27+
<table class="table vcio-table width-100-pct mt-2">
28+
<thead>
29+
<tr>
30+
<th style="width: 50%;">Introduced in</th>
31+
<th>Fixed by</th>
32+
</tr>
33+
</thead>
34+
<tbody>
35+
{% for impact in advisoryv2.impacted_packages.all %}
36+
{% for pkg_commit_patch in impact.introduced_by_package_commit_patches.all %}
37+
<tr>
38+
<td>
39+
<a href="{{ pkg_commit_patch.vcs_url }}" target="_self">
40+
{{ pkg_commit_patch.base_purl }}@{{ pkg_commit_patch.commit_hash }}
41+
</a>
42+
</td>
43+
<td></td>
44+
</tr>
45+
{% endfor %}
46+
47+
{% for pkg_commit_patch in impact.fixed_by_package_commit_patches.all %}
48+
<tr>
49+
<td></td>
50+
<td>
51+
<a href="{{ pkg_commit_patch.vcs_url }}" target="_self">
52+
{{ impact.base_purl }}@{{ pkg_commit_patch.commit_hash }}
53+
</a>
54+
</td>
55+
</tr>
56+
{% endfor %}
57+
58+
{% empty %}
59+
<tr>
60+
<td colspan="2">
61+
This vulnerability is not known to affect any package commits.
62+
</td>
63+
</tr>
64+
{% endfor %}
65+
</tbody>
66+
</table>
67+
</div>
68+
69+
</div>
70+
</section>
71+
{% endif %}
72+
73+
<script src="{% static 'js/main.js' %}" crossorigin="anonymous"></script>
74+
75+
{% endblock %}

0 commit comments

Comments
 (0)