Skip to content

Commit 17a5006

Browse files
authored
Store and display new Package.risk_score field in the UI (#194)
* Add a risk_score field and display the values in the UI #98 Signed-off-by: tdruez <[email protected]> * Generate random value for the risk_score #98 Signed-off-by: tdruez <[email protected]> * Remove temp data migration #98 Signed-off-by: tdruez <[email protected]> * CSS adjustments #98 Signed-off-by: tdruez <[email protected]> * Rename fetch_for_queryset to fetch_for_packages #98 Signed-off-by: tdruez <[email protected]> * Add exploitability, weighted_severity, risk_score on Vulnerability #98 Signed-off-by: tdruez <[email protected]> * Display new fields in Vulnerabilities tab #98 Signed-off-by: tdruez <[email protected]> * Display new fields in Vulnerabilities lists #98 Signed-off-by: tdruez <[email protected]> * Refine the Risk badge rendering #98 Signed-off-by: tdruez <[email protected]> * Add filter for all new fields #98 Signed-off-by: tdruez <[email protected]> * Sort the vulnerability by risk in listing #98 Signed-off-by: tdruez <[email protected]> * Remove the min_score and max_score attributes from model #98 Signed-off-by: tdruez <[email protected]> * Add help text in Inventory tab headers #98 Signed-off-by: tdruez <[email protected]> * Remove dead code #98 Signed-off-by: tdruez <[email protected]> * Set proper choices for the exploitability filter #98 Signed-off-by: tdruez <[email protected]> * Consolidate migrations #98 Signed-off-by: tdruez <[email protected]> * Fix part of the failing tests #98 Signed-off-by: tdruez <[email protected]> * Add migration files #98 Signed-off-by: tdruez <[email protected]> * Fix failing tests #98 Signed-off-by: tdruez <[email protected]> * Refine the DecimalField and exploitability label system #98 Signed-off-by: tdruez <[email protected]> * Refine the display of exploitability #98 Signed-off-by: tdruez <[email protected]> * Update risk_score help_text #98 Signed-off-by: tdruez <[email protected]> * Display the risk_score as badge in Package vulnerabilities tab #98 Signed-off-by: tdruez <[email protected]> * Update the risk_score on package in fetch_for_packages #98 Signed-off-by: tdruez <[email protected]> * Set proper max_digits to 2 for exploitability #98 Signed-off-by: tdruez <[email protected]> * Fix issue with the new filters #98 Signed-off-by: tdruez <[email protected]> * Render exploitability as a badge #98 Signed-off-by: tdruez <[email protected]> * Add unit test for the risk_score filter in Inventory tab #98 Signed-off-by: tdruez <[email protected]> * Consolidate migration files #98 Signed-off-by: tdruez <[email protected]> * Add changelog entry Signed-off-by: tdruez <[email protected]> * Add support for update in fetch_for_packages #98 Signed-off-by: tdruez <[email protected]> --------- Signed-off-by: tdruez <[email protected]>
1 parent 219b5e6 commit 17a5006

File tree

34 files changed

+468
-221
lines changed

34 files changed

+468
-221
lines changed

CHANGELOG.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ Release notes
66
- Rename ProductDependency is_resolved to is_pinned.
77
https://github.com/aboutcode-org/dejacode/issues/189
88

9+
- Add new fields on the Vulnerability model: `exploitability`, `weighted_severity`,
10+
`risk_score`. The field are displayed in all relevant part of the UI where
11+
vulnerability data is available.
12+
https://github.com/aboutcode-org/dejacode/issues/98
13+
914
### Version 5.2.1
1015

1116
- Fix the models documentation navigation.

component_catalog/importers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
from policy.models import UsagePolicy
4242
from product_portfolio.models import ProductComponent
4343
from product_portfolio.models import ProductPackage
44-
from vulnerabilities.fetch import fetch_for_queryset
44+
from vulnerabilities.fetch import fetch_for_packages
4545

4646
keywords_help = (
4747
get_help_text(Component, "keywords")
@@ -433,7 +433,7 @@ def save_all(self):
433433
if self.dataspace.enable_vulnerablecodedb_access:
434434
package_pks = [package.pk for package in self.results["added"]]
435435
package_qs = Package.objects.scope(dataspace=self.dataspace).filter(pk__in=package_pks)
436-
fetch_for_queryset(package_qs, self.dataspace)
436+
fetch_for_packages(package_qs, self.dataspace)
437437

438438

439439
class SubcomponentImportForm(
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Generated by Django 5.0.9 on 2024-11-18 10:01
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('component_catalog', '0009_componentaffectedbyvulnerability_and_more'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='component',
15+
name='risk_score',
16+
field=models.DecimalField(blank=True, decimal_places=1, help_text='Risk score between 0.0 and 10.0, where higher values indicate greater vulnerability risk for the package.', max_digits=3, null=True),
17+
),
18+
migrations.AddField(
19+
model_name='package',
20+
name='risk_score',
21+
field=models.DecimalField(blank=True, decimal_places=1, help_text='Risk score between 0.0 and 10.0, where higher values indicate greater vulnerability risk for the package.', max_digits=3, null=True),
22+
),
23+
]

component_catalog/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1675,6 +1675,7 @@ def only_rendering_fields(self):
16751675
*PACKAGE_URL_FIELDS,
16761676
"filename",
16771677
"license_expression",
1678+
"risk_score",
16781679
"dataspace__name",
16791680
"dataspace__show_usage_policy_in_user_views",
16801681
)

component_catalog/templates/component_catalog/tabs/tab_vulnerabilities.html

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
11
{% load i18n %}
2+
<dl class="row mb-3">
3+
<dt class="col-sm-1 text-end pe-0">
4+
<span class="help_text" data-bs-placement="right" data-bs-toggle="tooltip" data-bs-title="Risk score between 0.0 and 10.0, where higher values indicate greater vulnerability risk for the package.">
5+
Risk score
6+
</span>
7+
</dt>
8+
<dd class="col-sm-11 fs-110pct">
9+
{% include 'vulnerabilities/includes/risk_score_badge.html' with risk_score=package.risk_score only %}
10+
</dd>
11+
</dl>
212
<table class="table table-bordered table-hover table-md text-break">
313
<thead>
414
<tr>
@@ -7,19 +17,24 @@
717
{% trans 'Affected by' %}
818
</span>
919
</th>
10-
<th style="width: 210px;">
11-
<span class="help_text" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="A list of aliases for this vulnerability.">
12-
{% trans 'Aliases' %}
20+
<th style="width: 300px;">
21+
<span class="help_text" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Summary of the vulnerability.">
22+
{% trans 'Summary' %}
1323
</span>
1424
</th>
15-
<th style="width: 90px;">
16-
<span class="help_text" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Severity score range.">
17-
{% trans 'Score' %}
25+
<th style="min-width: 130px;">
26+
<span class="help_text" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Exploitability indicates the likelihood that a vulnerability in a software package could be used by malicious actors to compromise systems, applications, or networks. This metric is determined automatically based on the discovery of known exploits">
27+
{% trans 'Exploitability' %}
1828
</span>
1929
</th>
20-
<th>
21-
<span class="help_text" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Summary of the vulnerability.">
22-
{% trans 'Summary' %}
30+
<th style="min-width: 100px;">
31+
<span class="help_text" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Weighted severity is the highest value calculated by multiplying each severity by its corresponding weight, divided by 10.">
32+
{% trans 'Severity' %}
33+
</span>
34+
</th>
35+
<th style="min-width: 90px;">
36+
<span class="help_text" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Risk score from 0.0 to 10.0, with higher values indicating greater vulnerability risk. This score is the maximum of the weighted severity multiplied by exploitability, capped at 10.">
37+
{% trans 'Risk' %}
2338
</span>
2439
</th>
2540
<th style="min-width: 320px;">
@@ -43,19 +58,9 @@
4358
{{ vulnerability.vulnerability_id }}
4459
{% endif %}
4560
</strong>
46-
</td>
47-
<td>
48-
{% include 'component_catalog/includes/vulnerability_aliases.html' with aliases=vulnerability.aliases only %}
49-
</td>
50-
<td>
51-
{% if vulnerability.min_score %}
52-
{{ vulnerability.min_score }} -
53-
{% endif %}
54-
{% if vulnerability.max_score %}
55-
<strong>
56-
{{ vulnerability.max_score }}
57-
</strong>
58-
{% endif %}
61+
<div class="mt-2">
62+
{% include 'component_catalog/includes/vulnerability_aliases.html' with aliases=vulnerability.aliases only %}
63+
</div>
5964
</td>
6065
<td>
6166
{% if vulnerability.summary %}
@@ -69,6 +74,15 @@
6974
{% endif %}
7075
{% endif %}
7176
</td>
77+
<td>
78+
{% include 'vulnerabilities/includes/exploitability.html' with instance=vulnerability only %}
79+
</td>
80+
<td>
81+
{{ vulnerability.weighted_severity|default_if_none:"" }}
82+
</td>
83+
<td class="fs-110pct">
84+
{% include 'vulnerabilities/includes/risk_score_badge.html' with risk_score=vulnerability.risk_score only %}
85+
</td>
7286
<td>
7387
{% if vulnerability.fixed_packages_html %}
7488
{{ vulnerability.fixed_packages_html }}

component_catalog/tests/test_importers.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1397,17 +1397,17 @@ def test_package_import_add_to_product(self):
13971397
self.assertContains(response, expected3)
13981398
self.assertContains(response, expected4)
13991399

1400-
@mock.patch("component_catalog.importers.fetch_for_queryset")
1401-
def test_package_import_fetch_vulnerabilities(self, mock_fetch_for_queryset):
1402-
mock_fetch_for_queryset.return_value = None
1400+
@mock.patch("component_catalog.importers.fetch_for_packages")
1401+
def test_package_import_fetch_vulnerabilities(self, mock_fetch_for_packages):
1402+
mock_fetch_for_packages.return_value = None
14031403
self.dataspace.enable_vulnerablecodedb_access = True
14041404
self.dataspace.save()
14051405

14061406
file = os.path.join(TESTFILES_LOCATION, "package_from_scancode.json")
14071407
importer = PackageImporter(self.super_user, file)
14081408
importer.save_all()
14091409
self.assertEqual(2, len(importer.results["added"]))
1410-
mock_fetch_for_queryset.assert_called()
1410+
mock_fetch_for_packages.assert_called()
14111411

14121412

14131413
class SubcomponentImporterTestCase(TestCase):

component_catalog/tests/test_models.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1683,6 +1683,10 @@ def test_package_model_update_from_data(self):
16831683
package.refresh_from_db()
16841684
self.assertEqual(new_data["filename"], package.filename)
16851685

1686+
new_data = {"filename": "new_filename2"}
1687+
updated_fields = package.update_from_data(user=None, data=new_data, override=True)
1688+
self.assertEqual(["filename"], updated_fields)
1689+
16861690
@mock.patch("component_catalog.models.collect_package_data")
16871691
def test_package_model_create_from_url(self, mock_collect):
16881692
self.assertIsNone(Package.create_from_url(url=" ", user=self.user))

component_catalog/views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ class TabVulnerabilityMixin:
251251
template = "component_catalog/tabs/tab_vulnerabilities.html"
252252

253253
def tab_vulnerabilities(self):
254-
vulnerabilities_qs = self.object.affected_by_vulnerabilities.all()
254+
vulnerabilities_qs = self.object.affected_by_vulnerabilities.order_by_risk()
255255
if not vulnerabilities_qs:
256256
return
257257

dejacode/static/css/dejacode_bootstrap.css

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ a.dropdown-item:hover {
4040
.fs-85pct {
4141
font-size: 85%;
4242
}
43+
.fs-110pct {
44+
font-size: 110%;
45+
}
4346
.header {
4447
margin-bottom: 1rem;
4548
}
@@ -86,6 +89,9 @@ table.text-break thead {
8689
word-wrap: initial!important;
8790
word-break: initial!important;
8891
}
92+
.bg-warning-orange {
93+
background-color: var(--bs-orange);
94+
}
8995
/* -- Dark there fixes -- */
9096
[data-bs-theme=dark] .btn-outline-dark {
9197
--bs-btn-color: var(--bs-tertiary-color);
@@ -380,14 +386,20 @@ table.vulnerabilities-table .column-summary {
380386
#tab_vulnerabilities .column-vulnerability_id {
381387
width: 210px;
382388
}
383-
#tab_vulnerabilities .column-aliases {
384-
width: 210px;
389+
#tab_vulnerabilities .column-affected_packages {
390+
min-width: 300px;
385391
}
386-
#tab_vulnerabilities .column-max_score {
387-
width: 105px;
392+
#tab_vulnerabilities .column-exploitability {
393+
width: 150px;
388394
}
389-
#tab_vulnerabilities .column-column-affected_packages {
390-
width: 320px;
395+
#tab_vulnerabilities .column-weighted_severity {
396+
width: 115px;
397+
}
398+
#tab_vulnerabilities .column-risk_score {
399+
width: 95px;
400+
}
401+
#tab_vulnerabilities .column-summary {
402+
width: 300px;
391403
}
392404

393405
/* -- Dependency tab -- */

dje/copier.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
"project_uuid",
6767
"default_assignee",
6868
"affected_by_vulnerabilities",
69+
"risk_score",
6970
]
7071

7172

0 commit comments

Comments
 (0)