diff --git a/dojo/db_migrations/0200_finding_epss_score_percentile_fields.py b/dojo/db_migrations/0200_finding_epss_score_percentile_fields.py new file mode 100644 index 00000000000..ceecef08306 --- /dev/null +++ b/dojo/db_migrations/0200_finding_epss_score_percentile_fields.py @@ -0,0 +1,32 @@ +# Generated by Django 4.1.13 on 2024-02-05 19:52 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dojo', '0199_whitesource_to_mend'), + ] + + operations = [ + migrations.AddField( + model_name='finding', + name='epss_percentile', + field=models.FloatField(blank=True, help_text='Percentile for the EPSS score: the proportion of all scored vulnerabilities with the same or a lower EPSS score.', null=True, validators=[django.core.validators.MinValueValidator(0.0), django.core.validators.MaxValueValidator(1.0)], verbose_name='EPSS percentile'), + ), + migrations.AddField( + model_name='finding', + name='epss_score', + field=models.FloatField(blank=True, help_text='EPSS score representing the probability [0-1] of exploitation in the wild in the 30 days following score publication.', null=True, validators=[django.core.validators.MinValueValidator(0.0), django.core.validators.MaxValueValidator(1.0)], verbose_name='EPSS value'), + ), + migrations.AddIndex( + model_name='finding', + index=models.Index(fields=['epss_score'], name='dojo_findin_epss_sc_e40540_idx'), + ), + migrations.AddIndex( + model_name='finding', + index=models.Index(fields=['epss_percentile'], name='dojo_findin_epss_pe_567499_idx'), + ), + ] diff --git a/dojo/models.py b/dojo/models.py index 64fc8137167..8d6b515cdc9 100755 --- a/dojo/models.py +++ b/dojo/models.py @@ -13,7 +13,7 @@ from django.contrib.auth.models import Group from django.db.models.expressions import Case, When from django.urls import reverse -from django.core.validators import RegexValidator, validate_ipv46_address +from django.core.validators import RegexValidator, validate_ipv46_address, MinValueValidator, MaxValueValidator from django.core.files.base import ContentFile from django.core.exceptions import ValidationError from django.db import models, connection @@ -2422,6 +2422,17 @@ class Finding(models.Model): tags = TagField(blank=True, force_lowercase=True, help_text=_("Add tags that help describe this finding. Choose from the list or add new tags. Press Enter key to add.")) inherited_tags = TagField(blank=True, force_lowercase=True, help_text=_("Internal use tags sepcifically for maintaining parity with product. This field will be present as a subset in the tags field")) + epss_score = models.FloatField(null = True, + blank = True, + verbose_name = _('EPSS value'), + help_text = _("EPSS score representing the probability [0-1] of exploitation in the wild in the 30 days following score publication."), + validators=[MinValueValidator(0.0), MaxValueValidator(1.0)],) + epss_percentile = models.FloatField(null = True, + blank = True, + verbose_name = _('EPSS percentile'), + help_text = _("Percentile for the EPSS score: the proportion of all scored vulnerabilities with the same or a lower EPSS score."), + validators=[MinValueValidator(0.0), MaxValueValidator(1.0)],) + SEVERITIES = {'Info': 4, 'Low': 3, 'Medium': 2, 'High': 1, 'Critical': 0} @@ -2458,6 +2469,8 @@ class Meta: models.Index(fields=['duplicate']), models.Index(fields=['is_mitigated']), models.Index(fields=['duplicate_finding', 'id']), + models.Index(fields=['epss_score']), + models.Index(fields=['epss_percentile']), ] def __init__(self, *args, **kwargs): diff --git a/dojo/templates/dojo/findings_list_snippet.html b/dojo/templates/dojo/findings_list_snippet.html index 248b7491942..14bf921a3bb 100644 --- a/dojo/templates/dojo/findings_list_snippet.html +++ b/dojo/templates/dojo/findings_list_snippet.html @@ -322,6 +322,12 @@

{% trans "Vulnerability Id" %} + + {% trans "EPSS Score" %} + + + {% trans "EPSS Percentile" %} + {% if filter_name == 'Closed' %} {% comment %} The display field is translated in the function. No need to translate here as well{% endcomment %} @@ -593,6 +599,20 @@

{% endif %} {% endwith %} + + {% if finding.epss_score is not None %} + {{ finding.epss_score|percentage:1.0 }} + {% else %} + N/A + {% endif %} + + + {% if finding.epss_percentile is not None %} + {{ finding.epss_percentile|percentage:1.0 }} + {% else %} + N/A + {% endif %} + {% if filter_name == 'Closed' %} {{ finding.mitigated|date }} @@ -721,6 +741,8 @@

}}, { "data": "cwe" }, { "data": "cve" }, + { "data": "epss_score" }, + { "data": "epss_percentile" }, { "data": "found_date" }, { "data": "finding_age" }, {% if system_settings.enable_finding_sla %} diff --git a/dojo/templates/dojo/view_finding.html b/dojo/templates/dojo/view_finding.html index 5a49cbd5bbf..cff057abba0 100755 --- a/dojo/templates/dojo/view_finding.html +++ b/dojo/templates/dojo/view_finding.html @@ -270,6 +270,7 @@

{% endif %} CWE Vulnerability Id + EPSS Score / Percentile Found by {% if finding.vuln_id_from_tool %} Vuln ID from tool @@ -421,6 +422,19 @@

{% endif %} {% endif %} + + {% if finding.epss_score is not None %} + {{ finding.epss_score|percentage:1.0 }} + {% else %} + N/A + {% endif %} + / + {% if finding.epss_percentile is not None %} + {{ finding.epss_percentile|percentage:1.0 }} + {% else %} + N/A + {% endif %} + {% if found_by %} {% for scanner in found_by %}