From 1deee0e29a21825394fb2502805e5c1c58b131bb Mon Sep 17 00:00:00 2001 From: tdruez Date: Fri, 23 Aug 2024 14:08:34 +0400 Subject: [PATCH] Add new Vulnerability list #95 Signed-off-by: tdruez --- component_catalog/filters.py | 25 ++++++++++ .../includes/vulnerability_aliases.html | 21 +++++++++ .../includes/vulnerability_list_table.html | 34 ++++++++++++++ .../tabs/tab_vulnerabilities.html | 19 +------- .../component_catalog/vulnerability_list.html | 16 +++++++ component_catalog/urls.py | 11 ++++- component_catalog/views.py | 47 +++++++++++++++++++ dje/templates/includes/navbar_header.html | 1 + .../includes/navbar_header_tools_menu.html | 8 ++++ 9 files changed, 163 insertions(+), 19 deletions(-) create mode 100644 component_catalog/templates/component_catalog/includes/vulnerability_aliases.html create mode 100644 component_catalog/templates/component_catalog/includes/vulnerability_list_table.html create mode 100644 component_catalog/templates/component_catalog/vulnerability_list.html diff --git a/component_catalog/filters.py b/component_catalog/filters.py index 5efdefa5..7993e8f5 100644 --- a/component_catalog/filters.py +++ b/component_catalog/filters.py @@ -16,12 +16,14 @@ from component_catalog.models import Component from component_catalog.models import ComponentKeyword from component_catalog.models import Package +from component_catalog.models import Vulnerability from component_catalog.programming_languages import PROGRAMMING_LANGUAGES from dje.filters import DataspacedFilterSet from dje.filters import DefaultOrderingFilter from dje.filters import HasRelationFilter from dje.filters import MatchOrderedSearchFilter from dje.filters import RelatedLookupListFilter +from dje.filters import SearchFilter from dje.widgets import BootstrapSelectMultipleWidget from dje.widgets import DropDownRightWidget from dje.widgets import SortDropDownWidget @@ -272,3 +274,26 @@ def show_created_date(self): @cached_property def show_last_modified_date(self): return not self.sort_value or self.has_sort_by("last_modified_date") + + +class VulnerabilityFilterSet(DataspacedFilterSet): + q = SearchFilter( + label=_("Search"), + search_fields=["vulnerability_id", "aliases"], + ) + sort = DefaultOrderingFilter( + label=_("Sort"), + fields=[ + "vulnerability_id", + "affected_packages_count", + "created_date", + "last_modified_date", + ], + widget=SortDropDownWidget, + ) + + class Meta: + model = Vulnerability + fields = [ + "q", + ] diff --git a/component_catalog/templates/component_catalog/includes/vulnerability_aliases.html b/component_catalog/templates/component_catalog/includes/vulnerability_aliases.html new file mode 100644 index 00000000..e64fae7a --- /dev/null +++ b/component_catalog/templates/component_catalog/includes/vulnerability_aliases.html @@ -0,0 +1,21 @@ + \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/includes/vulnerability_list_table.html b/component_catalog/templates/component_catalog/includes/vulnerability_list_table.html new file mode 100644 index 00000000..0d6bf5d2 --- /dev/null +++ b/component_catalog/templates/component_catalog/includes/vulnerability_list_table.html @@ -0,0 +1,34 @@ +{% load i18n %} +{% load inject_preserved_filters from dje_tags %} +{% load urlize_target_blank from dje_tags %} +{% load naturaltime_short from dje_tags %} + + + {% include 'includes/object_list_table_header.html' %} + + {% for vulnerability in object_list %} + + + + + + + {% empty %} + + {% endfor %} + +
+ + {# TODO #} + + {{ vulnerability.vulnerability_id }} + + + + + {% include 'component_catalog/includes/vulnerability_aliases.html' with aliases=vulnerability.aliases only %} + + {{ vulnerability.affected_packages_count }} + + {{ vulnerability.fixed_packages_length }} +
No results.
\ No newline at end of file diff --git a/component_catalog/templates/component_catalog/tabs/tab_vulnerabilities.html b/component_catalog/templates/component_catalog/tabs/tab_vulnerabilities.html index 9f29dea3..dbca137d 100644 --- a/component_catalog/templates/component_catalog/tabs/tab_vulnerabilities.html +++ b/component_catalog/templates/component_catalog/tabs/tab_vulnerabilities.html @@ -37,24 +37,7 @@ {{ vulnerability.summary }} - {% for alias in vulnerability.aliases %} - {% if alias|slice:":3" == "CVE" %} - {{ alias }} - - - {% elif alias|slice:":4" == "GHSA" %} - {{ alias }} - - - {% elif alias|slice:":3" == "NPM" %} - {{ alias }} - - - {% else %} - {{ alias }} - {% endif %} -
- {% endfor %} + {% include 'component_catalog/includes/vulnerability_aliases.html' with aliases=vulnerability.aliases only %} {% if vulnerability.fixed_packages_html %} diff --git a/component_catalog/templates/component_catalog/vulnerability_list.html b/component_catalog/templates/component_catalog/vulnerability_list.html new file mode 100644 index 00000000..5ebdddc4 --- /dev/null +++ b/component_catalog/templates/component_catalog/vulnerability_list.html @@ -0,0 +1,16 @@ +{% extends 'object_list_base.html' %} +{% load i18n %} + +{% block page_title %}{% trans "Vulnerabilities" %}{% endblock %} + +{% block nav-list-head %} + {{ block.super }} + +{% endblock %} \ No newline at end of file diff --git a/component_catalog/urls.py b/component_catalog/urls.py index 53ff217a..50e87277 100644 --- a/component_catalog/urls.py +++ b/component_catalog/urls.py @@ -21,6 +21,7 @@ from component_catalog.views import PackageTabScanView from component_catalog.views import PackageUpdateView from component_catalog.views import ScanListView +from component_catalog.views import VulnerabilityListView from component_catalog.views import component_create_ajax_view from component_catalog.views import delete_scan_view from component_catalog.views import package_create_ajax_view @@ -148,6 +149,14 @@ ), ] +vulnerabilities_patterns = [ + path( + "vulnerabilities/", + VulnerabilityListView.as_view(), + name="vulnerability_list", + ), +] + # WARNING: we moved the components/ patterns from the include to the following # since we need the packages/ and scans/ to be register on the root "/" @@ -239,4 +248,4 @@ def component_path(path_segment, view): ] -urlpatterns = packages_patterns + component_patterns + scans_patterns +urlpatterns = packages_patterns + component_patterns + scans_patterns + vulnerabilities_patterns diff --git a/component_catalog/views.py b/component_catalog/views.py index 572296ca..52bb8ca8 100644 --- a/component_catalog/views.py +++ b/component_catalog/views.py @@ -52,6 +52,7 @@ from component_catalog.filters import ComponentFilterSet from component_catalog.filters import PackageFilterSet +from component_catalog.filters import VulnerabilityFilterSet from component_catalog.forms import AddMultipleToComponentForm from component_catalog.forms import AddToComponentForm from component_catalog.forms import AddToProductAdminForm @@ -71,6 +72,7 @@ from component_catalog.models import Package from component_catalog.models import PackageAlreadyExistsWarning from component_catalog.models import Subcomponent +from component_catalog.models import Vulnerability from dejacode_toolkit.download import DataCollectionException from dejacode_toolkit.purldb import PurlDB from dejacode_toolkit.scancodeio import ScanCodeIO @@ -2476,3 +2478,48 @@ def get_tab_fields(self): tab_fields.extend(get_purldb_tab_fields(purldb_entry, user.dataspace)) return {"fields": tab_fields} + + +class VulnerabilityListView( + LoginRequiredMixin, + DataspacedFilterView, +): + model = Vulnerability + filterset_class = VulnerabilityFilterSet + template_name = "component_catalog/vulnerability_list.html" + template_list_table = "component_catalog/includes/vulnerability_list_table.html" + table_headers = ( + Header("vulnerability_id", _("Vulnerability")), + Header("aliases", _("Aliases")), + # Header("score"), + Header("affected_packages_count", "Affected packages", help_text=" "), + Header("fixed_packages_length", "Fixed by packages", help_text=" "), + # Header("affected_product_count", "Affected products"), + ) + + def get_queryset(self): + from django.db.models import Func + from django.db.models import IntegerField + + return ( + super() + .get_queryset() + .only( + "uuid", + "vulnerability_id", + "aliases", + "created_date", + "last_modified_date", + "dataspace", + ) + .annotate( + affected_packages_count=Count("affected_packages"), + # TODO: Consider computing this on save() + fixed_packages_length=Func( + "fixed_packages", function="jsonb_array_length", output_field=IntegerField() + ), + ) + .order_by( + "-last_modified_date", + ) + ) diff --git a/dje/templates/includes/navbar_header.html b/dje/templates/includes/navbar_header.html index a4c283c6..44948a75 100644 --- a/dje/templates/includes/navbar_header.html +++ b/dje/templates/includes/navbar_header.html @@ -11,6 +11,7 @@ {% url 'workflow:request_list' as request_list_url %} {% url 'component_catalog:scan_list' as scan_list_url %} {% url 'purldb:purldb_list' as purldb_list_url %} +{% url 'component_catalog:vulnerability_list' as vulnerability_list_url %} {% url 'django_registration_register' as register_url %} {% url 'api_v2:api-root' as api_root_url %} {% url 'account_profile' as account_profile_url %} diff --git a/dje/templates/includes/navbar_header_tools_menu.html b/dje/templates/includes/navbar_header_tools_menu.html index 133cb0b6..28b4f707 100644 --- a/dje/templates/includes/navbar_header_tools_menu.html +++ b/dje/templates/includes/navbar_header_tools_menu.html @@ -32,6 +32,14 @@ {% endif %} {% endif %} + {% if user.dataspace.enable_vulnerablecodedb_access %} + + + + + {% trans 'Vulnerabilities' %} + + {% endif %}