Skip to content

Commit e050dde

Browse files
committed
Add project vulnerability list view
Signed-off-by: tdruez <tdruez@aboutcode.org>
1 parent f21f18d commit e050dde

10 files changed

Lines changed: 129 additions & 37 deletions

File tree

scancodeio/static/main.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,12 @@ progress.file-upload::before {
391391
#message-list th#column-severity {
392392
min-width: 110px;
393393
}
394+
th#column-vulnerability_id {
395+
min-width: 220px;
396+
}
397+
th#column-summary {
398+
width: 40%;
399+
}
394400
.menu.is-info .is-active {
395401
background-color: #3e8ed0;
396402
}

scanpipe/models.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1495,6 +1495,11 @@ def vulnerable_dependency_count(self):
14951495
"""Return the number of vulnerable dependencies related to this project."""
14961496
return self.vulnerable_dependencies.count()
14971497

1498+
@cached_property
1499+
def vulnerability_count(self):
1500+
"""Return the number of vulnerabilities related to this project."""
1501+
return self.vulnerable_package_count + self.vulnerable_dependency_count
1502+
14981503
@cached_property
14991504
def dependency_count(self):
15001505
"""Return the number of dependencies related to this project."""

scanpipe/templates/scanpipe/includes/project_summary_level.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,8 @@
9494
{% endif %}
9595
{% url 'project_messages' project.slug as project_messages_url %}
9696
{% include "scanpipe/includes/project_summary_level_item.html" with label="Messages" count=project.message_count url=project_messages_url only %}
97+
{% if project.vulnerability_count %}
98+
{% url 'project_vulnerabilities' project.slug as project_vulnerabilities_url %}
99+
{% include "scanpipe/includes/project_summary_level_item.html" with label="Vulnerabilities" count=project.vulnerability_count url=project_vulnerabilities_url only %}
100+
{% endif %}
97101
</nav>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{% if vulnerability.vulnerability_id|slice:":4" == "VCID" %}
2+
<a href="{{ VULNERABLECODE_URL }}/vulnerabilities/{{ vulnerability.vulnerability_id }}" target="_blank">
3+
{{ vulnerability.vulnerability_id }}
4+
<i class="fa-solid fa-up-right-from-square is-small"></i>
5+
</a>
6+
{% else %}
7+
{{ vulnerability.vulnerability_id }}
8+
{% endif %}
9+
10+
<ul class="list-unstyled mb-0">
11+
{% for alias in aliases %}
12+
<li>
13+
{% if alias|slice:":3" == "CVE" %}
14+
<a href="https://nvd.nist.gov/vuln/detail/{{ alias }}" target="_blank">{{ alias }}
15+
<i class="fa-solid fa-up-right-from-square mini"></i>
16+
</a>
17+
{% elif alias|slice:":4" == "GHSA" %}
18+
<a href="https://github.com/advisories/{{ alias }}" target="_blank">{{ alias }}
19+
<i class="fa-solid fa-up-right-from-square mini"></i>
20+
</a>
21+
{% elif alias|slice:":3" == "NPM" %}
22+
<a href="https://github.com/nodejs/security-wg/blob/main/vuln/npm/{{ alias|slice:"4:" }}.json" target="_blank">{{ alias }}
23+
<i class="fa-solid fa-up-right-from-square mini"></i>
24+
</a>
25+
{% else %}
26+
{{ alias }}
27+
{% endif %}
28+
</li>
29+
{% endfor %}
30+
</ul>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{% if vulnerability.summary %}
2+
{% if vulnerability.summary|length > 150 %}
3+
<details>
4+
<summary>{{ vulnerability.summary|slice:":150" }}...</summary>
5+
{{ vulnerability.summary|slice:"150:" }}
6+
</details>
7+
{% else %}
8+
{{ vulnerability.summary }}
9+
{% endif %}
10+
{% endif %}

scanpipe/templates/scanpipe/tabset/tab_vulnerabilities.html

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -11,43 +11,10 @@
1111
{% for vulnerability in tab_data.fields.affected_by_vulnerabilities.value %}
1212
<tr>
1313
<td>
14-
<a href="{{ VULNERABLECODE_URL }}/vulnerabilities/{{ vulnerability.vulnerability_id }}" target="_blank">
15-
{{ vulnerability.vulnerability_id }}
16-
<i class="fa-solid fa-up-right-from-square is-small"></i>
17-
</a>
18-
<ul class="list-unstyled mb-0">
19-
{% for alias in aliases %}
20-
<li>
21-
{% if alias|slice:":3" == "CVE" %}
22-
<a href="https://nvd.nist.gov/vuln/detail/{{ alias }}" target="_blank">{{ alias }}
23-
<i class="fa-solid fa-up-right-from-square mini"></i>
24-
</a>
25-
{% elif alias|slice:":4" == "GHSA" %}
26-
<a href="https://github.com/advisories/{{ alias }}" target="_blank">{{ alias }}
27-
<i class="fa-solid fa-up-right-from-square mini"></i>
28-
</a>
29-
{% elif alias|slice:":3" == "NPM" %}
30-
<a href="https://github.com/nodejs/security-wg/blob/main/vuln/npm/{{ alias|slice:"4:" }}.json" target="_blank">{{ alias }}
31-
<i class="fa-solid fa-up-right-from-square mini"></i>
32-
</a>
33-
{% else %}
34-
{{ alias }}
35-
{% endif %}
36-
</li>
37-
{% endfor %}
38-
</ul>
14+
{% include 'scanpipe/includes/vulnerability_id.html' with vulnerability=vulnerability only %}
3915
</td>
4016
<td>
41-
{% if vulnerability.summary %}
42-
{% if vulnerability.summary|length > 150 %}
43-
<details>
44-
<summary>{{ vulnerability.summary|slice:":150" }}...</summary>
45-
{{ vulnerability.summary|slice:"150:" }}
46-
</details>
47-
{% else %}
48-
{{ vulnerability.summary }}
49-
{% endif %}
50-
{% endif %}
17+
{% include 'scanpipe/includes/vulnerability_summary.html' with vulnerability=vulnerability only %}
5118
</td>
5219
<td>
5320
{% for key, value in vulnerability.cdx_vulnerability.analysis.items %}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{% extends "scanpipe/base.html" %}
2+
{% load humanize %}
3+
4+
{% block title %}ScanCode.io: Vulnerabilities{% endblock %}
5+
6+
{% block content %}
7+
<div id="content-header" class="container is-max-widescreen mb-3">
8+
{% include 'scanpipe/includes/navbar_header.html' %}
9+
<section class="mx-5">
10+
{% include 'scanpipe/includes/breadcrumb.html' with linked_project=True current="Vulnerabilities" %}
11+
<div>{{ object_list|length|intcomma }} results</div>
12+
</section>
13+
</div>
14+
15+
<div class="container is-fluid mb-3">
16+
<table class="table is-bordered is-striped is-narrow is-hoverable is-fullwidth break-word">
17+
{% include 'scanpipe/includes/list_view_thead.html' %}
18+
<tbody>
19+
{% for vulnerability in object_list.values %}
20+
<tr>
21+
<td>
22+
{% include 'scanpipe/includes/vulnerability_id.html' with vulnerability=vulnerability only %}
23+
</td>
24+
<td>
25+
{% include 'scanpipe/includes/vulnerability_summary.html' with vulnerability=vulnerability only %}
26+
</td>
27+
<td>
28+
{% for obj in vulnerability.affects %}
29+
{{ obj }}<br>
30+
{% endfor %}
31+
</td>
32+
</tr>
33+
{% empty %}
34+
<tr>
35+
<td colspan="42" class="has-text-centered p-3">
36+
No Vulnerabilities found. <a href="?">Clear search and filters</a>
37+
</td>
38+
</tr>
39+
{% endfor %}
40+
</tbody>
41+
</table>
42+
</div>
43+
{% endblock %}

scanpipe/tests/test_views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -821,7 +821,7 @@ def test_scanpipe_views_project_views(self):
821821
with self.assertNumQueries(7):
822822
self.client.get(url)
823823

824-
with self.assertNumQueries(13):
824+
with self.assertNumQueries(15):
825825
self.client.get(self.project1.get_absolute_url())
826826

827827
@mock.patch("scanpipe.models.Run.execute_task_async")

scanpipe/urls.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@
9696
views.ProjectMessageListView.as_view(),
9797
name="project_messages",
9898
),
99+
path(
100+
"project/<slug:slug>/vulnerabilities/",
101+
views.VulnerabilityListView.as_view(),
102+
name="project_vulnerabilities",
103+
),
99104
path(
100105
"project/<slug:slug>/archive/",
101106
views.ProjectArchiveView.as_view(),

scanpipe/views.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1626,7 +1626,7 @@ def get_queryset(self):
16261626

16271627
def get_context_data(self, **kwargs):
16281628
context = super().get_context_data(**kwargs)
1629-
context["project"] = self.project
1629+
context["project"] = self.get_project()
16301630
context["model_label"] = self.model_label
16311631
return context
16321632

@@ -1951,6 +1951,28 @@ def get_filterset_kwargs(self, filterset_class):
19511951
return kwargs
19521952

19531953

1954+
class VulnerabilityListView(
1955+
ConditionalLoginRequired,
1956+
ProjectRelatedViewMixin,
1957+
TableColumnsMixin,
1958+
generic.ListView,
1959+
):
1960+
template_name = "scanpipe/vulnerability_list.html"
1961+
table_columns = [
1962+
"vulnerability_id",
1963+
"summary",
1964+
"affects",
1965+
]
1966+
1967+
def get_queryset(self):
1968+
return []
1969+
1970+
def get_context_data(self, **kwargs):
1971+
context = super().get_context_data(**kwargs)
1972+
context["object_list"] = self.project.vulnerabilities
1973+
return context
1974+
1975+
19541976
class CodebaseResourceDetailsView(
19551977
ConditionalLoginRequired,
19561978
ProjectRelatedViewMixin,

0 commit comments

Comments
 (0)