diff --git a/component_catalog/templates/component_catalog/includes/vulnerability_icon_link.html b/component_catalog/templates/component_catalog/includes/vulnerability_icon_link.html index 9ba7d739..bf556e1e 100644 --- a/component_catalog/templates/component_catalog/includes/vulnerability_icon_link.html +++ b/component_catalog/templates/component_catalog/includes/vulnerability_icon_link.html @@ -1,3 +1,3 @@ - + {% if count %}{{ count }}{% endif %} \ 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 new file mode 100644 index 00000000..9f29dea3 --- /dev/null +++ b/component_catalog/templates/component_catalog/tabs/tab_vulnerabilities.html @@ -0,0 +1,67 @@ +{% load i18n %} + + + + + + + + + + + {% for vulnerability in values.vulnerabilities %} + + + + + + + {% endfor %} + +
+ + {% trans 'Affected by' %} + + + + {% trans 'Summary' %} + + + + {% trans 'Aliases' %} + + + + {% trans 'Fixed packages' %} + +
+ + {{ vulnerability.vulnerability_id }} + + + + {{ 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 %} +
+ {% if vulnerability.fixed_packages_html %} + {{ vulnerability.fixed_packages_html }} + {% endif %} +
\ No newline at end of file diff --git a/component_catalog/views.py b/component_catalog/views.py index 5e25731f..659571c9 100644 --- a/component_catalog/views.py +++ b/component_catalog/views.py @@ -95,7 +95,6 @@ from dje.views import DataspacedCreateView from dje.views import DataspacedFilterView from dje.views import DataspacedUpdateView -from dje.views import FieldLastLoop from dje.views import FieldSeparator from dje.views import Header from dje.views import LicenseDataForBuilderMixin @@ -250,151 +249,94 @@ def include_type(view_instance): class TabVulnerabilityMixin: + template = "component_catalog/tabs/tab_vulnerabilities.html" + def tab_vulnerabilities(self): - vulnerabilities = self.object.affected_by_vulnerabilities.all() - if not vulnerabilities: + vulnerabilities_qs = self.object.affected_by_vulnerabilities.all() + if not vulnerabilities_qs: return vulnerablecode = VulnerableCode(self.object.dataspace) - if fields := self.get_vulnerabilities_tab_fields(vulnerabilities, vulnerablecode): - label = ( - f"Vulnerabilities" - f' {len(vulnerabilities)}' - ) - - return { - "fields": fields, - "label": format_html(label), - } - - def get_vulnerabilities_tab_fields(self, vulnerabilities, vulnerablecode): - dataspace = self.object.dataspace - fields = [] - - for vulnerability in vulnerabilities: - vulnerability_fields = self.get_vulnerability_fields( - vulnerability, dataspace, vulnerablecode - ) - fields.extend(vulnerability_fields) + label = ( + f"Vulnerabilities" + f' {len(vulnerabilities_qs)}' + ) - return fields + vulnerabilities = [] + for vulnerability in vulnerabilities_qs: + fixed_packages_html = self.get_fixed_packages_html(vulnerability, self.object.dataspace) + vulnerability.fixed_packages_html = fixed_packages_html + vulnerabilities.append(vulnerability) - @staticmethod - def get_vulnerability_fields(vulnerability, dataspace, vulnerablecode): - reference_urls = [] - reference_ids = [] - - for reference in vulnerability.references: - url = reference.get("reference_url") - reference_id = reference.get("reference_id") - if url and reference_id: - reference_ids.append(f'{reference_id}') - elif reference_id: - reference_ids.append(reference_id) - elif url: - reference_urls.append(url) - - reference_urls_joined = "\n".join(reference_urls) - reference_ids_joined = "\n".join(reference_ids) + context = { + "vulnerabilities": vulnerabilities, + "vulnerablecode_url": vulnerablecode.service_url, + } - tab_fields = [ - (_("Summary"), vulnerability.summary, "Summary of the vulnerability"), - ] + return { + "fields": [(None, context, None, self.template)], + "label": format_html(label), + } - if vulnerablecode_url := vulnerablecode.service_url: - vulnerability_url = f"{vulnerablecode_url}vulnerabilities/{vulnerability.vcid}" - vulnerability_url_help = "Link to the VulnerableCode app." - url_as_link = format_html( - '{vulnerability_url}', - vulnerability_url=vulnerability_url, - ) - tab_fields.append((_("VulnerableCode URL"), url_as_link, vulnerability_url_help)) - - if vulnerability.fixed_packages: - fixed_packages_sorted = natsorted(vulnerability.fixed_packages, key=itemgetter("purl")) - add_package_url = reverse("component_catalog:package_add") - vulnerability_icon = ( - '' - '' - "" - ) - no_vulnerabilities_icon = ( - '' - ' ' - ' ' - "" - ) + def get_fixed_packages_html(self, vulnerability, dataspace): + if not vulnerability.fixed_packages: + return - fixed_packages_values = [] - for fixed_package in fixed_packages_sorted: - purl = fixed_package.get("purl") - is_vulnerable = fixed_package.get("is_vulnerable") - package_instances = Package.objects.scope(dataspace).for_package_url(purl) - - for package in package_instances: - absolute_url = package.get_absolute_url() - display_value = package.get_html_link(href=absolute_url) - if is_vulnerable: - display_value += package.get_html_link( - href=f"{absolute_url}#vulnerabilities", - value=format_html(vulnerability_icon), - ) - else: - display_value += no_vulnerabilities_icon - fixed_packages_values.append(display_value) + fixed_packages_sorted = natsorted(vulnerability.fixed_packages, key=itemgetter("purl")) + add_package_url = reverse("component_catalog:package_add") + vulnerability_icon = ( + '' + '' + "" + ) + no_vulnerabilities_icon = ( + '' + ' ' + ' ' + "" + ) - if not package_instances: - display_value = purl.replace("pkg:", "") - if is_vulnerable: - display_value += vulnerability_icon - else: - display_value += no_vulnerabilities_icon - # Warning: do not add spaces between HTML elements as this content - # is displayed in a
-                    display_value += (
-                        f''
-                        f''
-                        f''
-                        f""
-                        f""
+        fixed_packages_values = []
+        for fixed_package in fixed_packages_sorted:
+            purl = fixed_package.get("purl")
+            is_vulnerable = fixed_package.get("is_vulnerable")
+            package_instances = Package.objects.scope(dataspace).for_package_url(purl)
+
+            for package in package_instances:
+                absolute_url = package.get_absolute_url()
+                display_value = package.get_html_link(href=absolute_url)
+                if is_vulnerable:
+                    display_value += package.get_html_link(
+                        href=f"{absolute_url}#vulnerabilities",
+                        value=format_html(vulnerability_icon),
                     )
-                    fixed_packages_values.append(display_value)
-
-            tab_fields.append(
-                (
-                    _("Fixed packages"),
-                    format_html("\n".join(fixed_packages_values)),
-                    "The identifiers of Package Versions that have been reported to fix a "
-                    "specific vulnerability and collected in VulnerableCodeDB.",
-                ),
-            )
+                else:
+                    display_value += no_vulnerabilities_icon
+                fixed_packages_values.append(display_value)
 
-        tab_fields.extend(
-            [
-                (
-                    _("Reference IDs"),
-                    format_html(reference_ids_joined),
-                    "Reference IDs to the reported vulnerability, such as a DSA "
-                    "(Debian Security Advisory) ID or a CVE (Common Vulnerabilities "
-                    "and Exposures) ID, when available.",
-                ),
-                (
-                    _("Reference URLs"),
-                    urlize_target_blank(reference_urls_joined),
-                    "The URLs collected in VulnerableCodeDB that give you quick "
-                    "access to public websites that provide details about a "
-                    "vulnerability.",
-                ),
-                FieldLastLoop,
-            ]
-        )
+            if not package_instances:
+                display_value = purl.replace("pkg:", "")
+                if is_vulnerable:
+                    display_value += vulnerability_icon
+                else:
+                    display_value += no_vulnerabilities_icon
+                # Warning: do not add spaces between HTML elements as this content
+                # is displayed in a 
+                display_value += (
+                    f''
+                    f''
+                    f''
+                    f""
+                    f""
+                )
+                fixed_packages_values.append(display_value)
 
-        return tab_fields
+        return format_html("\n".join(fixed_packages_values))
 
 
 class ComponentListView(
diff --git a/dejacode/static/css/dejacode_bootstrap.css b/dejacode/static/css/dejacode_bootstrap.css
index 07d374cc..1a36881e 100644
--- a/dejacode/static/css/dejacode_bootstrap.css
+++ b/dejacode/static/css/dejacode_bootstrap.css
@@ -64,6 +64,9 @@ a.dropdown-item:hover {
 .smaller {
   font-size: 90%;
 }
+.mini {
+  font-size: .675em;
+}
 code {
   color: #ac1459;
 }