diff --git a/components/package.json b/components/package.json index 340fcde82b0..1859493eb90 100644 --- a/components/package.json +++ b/components/package.json @@ -1,6 +1,6 @@ { "name": "defectdojo", - "version": "2.42.1", + "version": "2.42.2", "license" : "BSD-3-Clause", "private": true, "dependencies": { diff --git a/docs/content/en/open_source/upgrading/2.42.md b/docs/content/en/open_source/upgrading/2.42.md index c815a7794d3..918ffdb901c 100644 --- a/docs/content/en/open_source/upgrading/2.42.md +++ b/docs/content/en/open_source/upgrading/2.42.md @@ -1,7 +1,7 @@ --- title: 'Upgrading to DefectDojo Version 2.42.x' toc_hide: true -weight: -20241104 +weight: -20241202 description: No special instructions. --- There are no special instructions for upgrading to 2.42.x. Check the [Release Notes](https://github.com/DefectDojo/django-DefectDojo/releases/tag/2.42.0) for the contents of the release. diff --git a/docs/content/en/share_your_findings/pro_reports/working_with_generated_reports.md b/docs/content/en/share_your_findings/pro_reports/working_with_generated_reports.md index 112f1538066..a63f07cf01f 100644 --- a/docs/content/en/share_your_findings/pro_reports/working_with_generated_reports.md +++ b/docs/content/en/share_your_findings/pro_reports/working_with_generated_reports.md @@ -7,7 +7,9 @@ weight: 2 Once you have created one or more **Reports** in DefectDojo you can take further actions, including: * Using a report as a template for subsequent reports -* Re\-running a report with updated data + +* Re-running a report with updated data + * Deleting an old or unused report ![image](images/Working_with_Generated_Reports.png) diff --git a/dojo/__init__.py b/dojo/__init__.py index c5a06c6f17c..5eec1f14dd1 100644 --- a/dojo/__init__.py +++ b/dojo/__init__.py @@ -4,6 +4,6 @@ # Django starts so that shared_task will use this app. from .celery import app as celery_app # noqa: F401 -__version__ = "2.42.1" +__version__ = "2.42.2" __url__ = "https://github.com/DefectDojo/django-DefectDojo" __docs__ = "https://documentation.defectdojo.com" diff --git a/dojo/api_v2/serializers.py b/dojo/api_v2/serializers.py index df700e6bf82..0f0a89906c3 100644 --- a/dojo/api_v2/serializers.py +++ b/dojo/api_v2/serializers.py @@ -2286,7 +2286,7 @@ def setup_common_context(self, data: dict) -> dict: """ context = dict(data) # update some vars - context["scan"] = data.pop("file") + context["scan"] = data.pop("file", None) if context.get("auto_create_context"): environment = Development_Environment.objects.get_or_create(name=data.get("environment", "Development"))[0] diff --git a/dojo/db_migrations/0219_system_settings_enforce_verified_status_jira_and_more.py b/dojo/db_migrations/0219_system_settings_enforce_verified_status_jira_and_more.py new file mode 100644 index 00000000000..839ce662885 --- /dev/null +++ b/dojo/db_migrations/0219_system_settings_enforce_verified_status_jira_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 5.1.4 on 2025-01-10 16:01 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dojo', '0218_system_settings_enforce_verified_status_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='system_settings', + name='enforce_verified_status_jira', + field=models.BooleanField(default=True, help_text='When enabled, findings must have a verified status to be pushed to jira.', verbose_name='Enforce Verified Status - Jira'), + ), + migrations.AddField( + model_name='system_settings', + name='enforce_verified_status_metrics', + field=models.BooleanField(default=True, help_text='When enabled, findings must have a verified status to be counted in metric calculations, be included in reports, and filters.', verbose_name='Enforce Verified Status - Metrics'), + ), + migrations.AddField( + model_name='system_settings', + name='enforce_verified_status_product_grading', + field=models.BooleanField(default=True, help_text="When enabled, findings must have a verified status to be considered as part of a product's grading.", verbose_name='Enforce Verified Status - Product Grading'), + ), + migrations.AlterField( + model_name='system_settings', + name='enforce_verified_status', + field=models.BooleanField(default=True, help_text='When enabled, features such as product grading, jira integration, metrics, and reports will only interact with verified findings. This setting will override individually scoped verified toggles.', verbose_name='Enforce Verified Status - Globally'), + ), + ] diff --git a/dojo/filters.py b/dojo/filters.py index b4c452c2fea..daa73375945 100644 --- a/dojo/filters.py +++ b/dojo/filters.py @@ -21,6 +21,7 @@ CharFilter, DateFilter, DateFromToRangeFilter, + DateTimeFilter, FilterSet, ModelChoiceFilter, ModelMultipleChoiceFilter, @@ -1411,6 +1412,15 @@ class ApiProductFilter(DojoFilter): ) +class PercentageRangeFilter(RangeFilter): + def filter(self, qs, value): + if value is not None: + start = value.start / decimal.Decimal("100.0") if value.start else None + stop = value.stop / decimal.Decimal("100.0") if value.stop else None + value = slice(start, stop) + return super().filter(qs, value) + + class ApiFindingFilter(DojoFilter): # BooleanFilter active = BooleanFilter(field_name="active") @@ -1456,13 +1466,30 @@ class ApiFindingFilter(DojoFilter): jira_change = DateRangeFilter(field_name="jira_issue__jira_change") last_reviewed = DateRangeFilter() mitigated = DateRangeFilter() - mitigated_on = DateFilter(field_name="mitigated", lookup_expr="exact") - mitigated_before = DateFilter(field_name="mitigated", lookup_expr="lt") - mitigated_after = DateFilter(field_name="mitigated", lookup_expr="gt") + mitigated_on = DateTimeFilter(field_name="mitigated", lookup_expr="exact", method="filter_mitigated_on") + mitigated_before = DateTimeFilter(field_name="mitigated", lookup_expr="lt") + mitigated_after = DateTimeFilter(field_name="mitigated", lookup_expr="gt", label="Mitigated After", method="filter_mitigated_after") # NumberInFilter cwe = NumberInFilter(field_name="cwe", lookup_expr="in") defect_review_requested_by = NumberInFilter(field_name="defect_review_requested_by", lookup_expr="in") endpoints = NumberInFilter(field_name="endpoints", lookup_expr="in") + epss_score = PercentageRangeFilter( + field_name="epss_score", + label="EPSS score range", + help_text=( + "The range of EPSS score percentages to filter on; the min input is a lower bound, " + "the max is an upper bound. Leaving one empty will skip that bound (e.g., leaving " + "the min bound input empty will filter only on the max bound -- filtering on " + '"less than or equal"). Leading 0 required.' + )) + epss_percentile = PercentageRangeFilter( + field_name="epss_percentile", + label="EPSS percentile range", + help_text=( + "The range of EPSS percentiles to filter on; the min input is a lower bound, the max " + "is an upper bound. Leaving one empty will skip that bound (e.g., leaving the min bound " + 'input empty will filter only on the max bound -- filtering on "less than or equal"). Leading 0 required.' + )) found_by = NumberInFilter(field_name="found_by", lookup_expr="in") id = NumberInFilter(field_name="id", lookup_expr="in") last_reviewed_by = NumberInFilter(field_name="last_reviewed_by", lookup_expr="in") @@ -1544,6 +1571,20 @@ class Meta: exclude = ["url", "thread_id", "notes", "files", "line", "cve"] + def filter_mitigated_after(self, queryset, name, value): + if value.hour == 0 and value.minute == 0 and value.second == 0: + value = value.replace(hour=23, minute=59, second=59) + + return queryset.filter(mitigated__gt=value) + + def filter_mitigated_on(self, queryset, name, value): + if value.hour == 0 and value.minute == 0 and value.second == 0: + # we have a simple date without a time, lets get a range from this morning to tonight at 23:59:59:999 + nextday = value + timedelta(days=1) + return queryset.filter(mitigated__gte=value, mitigated__lt=nextday) + + return queryset.filter(mitigated=value) + class PercentageFilter(NumberFilter): def __init__(self, *args, **kwargs): @@ -1564,15 +1605,6 @@ def filter_percentage(self, queryset, name, value): return queryset.filter(**lookup_kwargs) -class PercentageRangeFilter(RangeFilter): - def filter(self, qs, value): - if value is not None: - start = value.start / decimal.Decimal("100.0") if value.start else None - stop = value.stop / decimal.Decimal("100.0") if value.stop else None - value = slice(start, stop) - return super().filter(qs, value) - - class FindingFilterHelper(FilterSet): title = CharFilter(lookup_expr="icontains") date = DateRangeFilter(field_name="date", label="Date Discovered") @@ -1587,9 +1619,9 @@ class FindingFilterHelper(FilterSet): duplicate = ReportBooleanFilter() is_mitigated = ReportBooleanFilter() mitigated = DateRangeFilter(field_name="mitigated", label="Mitigated Date") - mitigated_on = DateFilter(field_name="mitigated", lookup_expr="exact", label="Mitigated On") - mitigated_before = DateFilter(field_name="mitigated", lookup_expr="lt", label="Mitigated Before") - mitigated_after = DateFilter(field_name="mitigated", lookup_expr="gt", label="Mitigated After") + mitigated_on = DateTimeFilter(field_name="mitigated", lookup_expr="exact", label="Mitigated On", method="filter_mitigated_on") + mitigated_before = DateTimeFilter(field_name="mitigated", lookup_expr="lt", label="Mitigated Before") + mitigated_after = DateTimeFilter(field_name="mitigated", lookup_expr="gt", label="Mitigated After", method="filter_mitigated_after") planned_remediation_date = DateRangeOmniFilter() planned_remediation_version = CharFilter(lookup_expr="icontains", label=_("Planned remediation version")) file_path = CharFilter(lookup_expr="icontains") @@ -1705,6 +1737,20 @@ def set_date_fields(self, *args: list, **kwargs: dict): self.form.fields["mitigated_after"].widget = date_input_widget self.form.fields["cwe"].choices = cwe_options(self.queryset) + def filter_mitigated_after(self, queryset, name, value): + if value.hour == 0 and value.minute == 0 and value.second == 0: + value = value.replace(hour=23, minute=59, second=59) + + return queryset.filter(mitigated__gt=value) + + def filter_mitigated_on(self, queryset, name, value): + if value.hour == 0 and value.minute == 0 and value.second == 0: + # we have a simple date without a time, lets get a range from this morning to tonight at 23:59:59:999 + nextday = value + timedelta(days=1) + return queryset.filter(mitigated__gte=value, mitigated__lt=nextday) + + return queryset.filter(mitigated=value) + class FindingFilterWithoutObjectLookups(FindingFilterHelper, FindingTagStringFilter): test__engagement__product__prod_type = NumberFilter(widget=HiddenInput()) diff --git a/dojo/forms.py b/dojo/forms.py index 334a958e93f..1080f19a73a 100644 --- a/dojo/forms.py +++ b/dojo/forms.py @@ -3076,7 +3076,7 @@ def clean(self): elif self.cleaned_data.get("push_to_jira", None): active = self.finding_form["active"].value() verified = self.finding_form["verified"].value() - if not active or (not verified and get_system_setting("enforce_verified_status", True)): + if not active or (not verified and (get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_jira", True))): logger.debug("Findings must be active and verified to be pushed to JIRA") error_message = "Findings must be active and verified to be pushed to JIRA" self.add_error("push_to_jira", ValidationError(error_message, code="not_active_or_verified")) diff --git a/dojo/jira_link/helper.py b/dojo/jira_link/helper.py index 8aff8972401..a557a05a3c1 100644 --- a/dojo/jira_link/helper.py +++ b/dojo/jira_link/helper.py @@ -146,7 +146,7 @@ def can_be_pushed_to_jira(obj, form=None): logger.debug("can_be_pushed_to_jira: %s, %s, %s", active, verified, severity) - isenforced = get_system_setting("enforce_verified_status", True) + isenforced = get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_jira", True) if not active or (not verified and isenforced): logger.debug("Findings must be active and verified, if enforced by system settings, to be pushed to JIRA") @@ -1116,7 +1116,6 @@ def get_issuetype_fields( except JIRAError as e: e.text = f"Jira API call 'createmeta' failed with status: {e.status_code} and message: {e.text}" raise - project = None try: project = meta["projects"][0] diff --git a/dojo/management/commands/jira_async_updates.py b/dojo/management/commands/jira_async_updates.py index a49ae04050a..222671daa75 100644 --- a/dojo/management/commands/jira_async_updates.py +++ b/dojo/management/commands/jira_async_updates.py @@ -22,7 +22,7 @@ class Command(BaseCommand): def handle(self, *args, **options): findings = Finding.objects.exclude(jira_issue__isnull=True) - if get_system_setting("enforce_verified_status", True): + if get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_jira", True): findings = findings.filter(verified=True, active=True) else: findings = findings.filter(active=True) diff --git a/dojo/management/commands/push_to_jira_update.py b/dojo/management/commands/push_to_jira_update.py index 164bc8e9704..d70a4b96b21 100644 --- a/dojo/management/commands/push_to_jira_update.py +++ b/dojo/management/commands/push_to_jira_update.py @@ -23,7 +23,7 @@ class Command(BaseCommand): def handle(self, *args, **options): findings = Finding.objects.exclude(jira_issue__isnull=True) - if get_system_setting("enforce_verified_status", True): + if get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_jira", True): findings = findings.filter(verified=True, active=True) else: findings = findings.filter(active=True) diff --git a/dojo/metrics/utils.py b/dojo/metrics/utils.py index 9a4b9f6bb04..cbe40b85bcf 100644 --- a/dojo/metrics/utils.py +++ b/dojo/metrics/utils.py @@ -109,7 +109,7 @@ def finding_queries( weekly_counts = query_counts_for_period(MetricsPeriod.WEEK, weeks_between) top_ten = get_authorized_products(Permissions.Product_View) - if get_system_setting("enforce_verified_status", True): + if get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_metrics", True): top_ten = top_ten.filter(engagement__test__finding__verified=True) top_ten = top_ten.filter(engagement__test__finding__false_p=False, diff --git a/dojo/metrics/views.py b/dojo/metrics/views.py index 592899bcdc9..4b037bcbd75 100644 --- a/dojo/metrics/views.py +++ b/dojo/metrics/views.py @@ -197,7 +197,7 @@ def simple_metrics(request): date__year=now.year, ) - if get_system_setting("enforce_verified_status", True): + if get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_metrics", True): total = total.filter(verified=True) total = total.distinct() @@ -308,7 +308,7 @@ def product_type_counts(request): then=Value(1)), output_field=IntegerField())))["total"] - if get_system_setting("enforce_verified_status", True): + if get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_metrics", True): overall_in_pt = Finding.objects.filter(date__lt=end_date, verified=True, false_p=False, @@ -509,7 +509,7 @@ def product_tag_counts(request): then=Value(1)), output_field=IntegerField())))["total"] - if get_system_setting("enforce_verified_status", True): + if get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_metrics", True): overall_in_pt = Finding.objects.filter(date__lt=end_date, verified=True, false_p=False, @@ -685,7 +685,7 @@ def view_engineer(request, eid): raise PermissionDenied now = timezone.now() - if get_system_setting("enforce_verified_status", True): + if get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_metrics", True): findings = Finding.objects.filter(reporter=user, verified=True) else: findings = Finding.objects.filter(reporter=user) diff --git a/dojo/models.py b/dojo/models.py index 99074a9cf3b..ff34cde034a 100644 --- a/dojo/models.py +++ b/dojo/models.py @@ -5,6 +5,7 @@ import os import re import warnings +from contextlib import suppress from datetime import datetime from pathlib import Path from uuid import uuid4 @@ -363,10 +364,32 @@ class System_Settings(models.Model): enforce_verified_status = models.BooleanField( default=True, - verbose_name=_("Enforce Verified Status"), - help_text=_("When enabled, features such as product grading, jira " - "integration, metrics, and reports will only interact " - "with verified findings.", + verbose_name=_("Enforce Verified Status - Globally"), + help_text=_( + "When enabled, features such as product grading, jira " + "integration, metrics, and reports will only interact " + "with verified findings. This setting will override " + "individually scoped verified toggles.", + ), + ) + enforce_verified_status_jira = models.BooleanField( + default=True, + verbose_name=_("Enforce Verified Status - Jira"), + help_text=_("When enabled, findings must have a verified status to be pushed to jira."), + ) + enforce_verified_status_product_grading = models.BooleanField( + default=True, + verbose_name=_("Enforce Verified Status - Product Grading"), + help_text=_( + "When enabled, findings must have a verified status to be considered as part of a product's grading.", + ), + ) + enforce_verified_status_metrics = models.BooleanField( + default=True, + verbose_name=_("Enforce Verified Status - Metrics"), + help_text=_( + "When enabled, findings must have a verified status to be counted in metric calculations, " + "be included in reports, and filters.", ), ) @@ -1236,7 +1259,7 @@ def open_findings(self, start_date=None, end_date=None): date__range=[start_date, end_date]) - if get_system_setting("enforce_verified_status", True): + if get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_metrics", True): findings = findings.filter(verified=True) critical = findings.filter(severity="Critical").count() @@ -1553,7 +1576,7 @@ def unaccepted_open_findings(self): from dojo.utils import get_system_setting findings = Finding.objects.filter(risk_accepted=False, active=True, duplicate=False, test__engagement=self) - if get_system_setting("enforce_verified_status", True): + if get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_metrics", True): findings = findings.filter(verified=True) return findings @@ -1575,7 +1598,10 @@ def delete(self, *args, **kwargs): import dojo.finding.helper as helper helper.prepare_duplicates_for_delete(engagement=self) super().delete(*args, **kwargs) - calculate_grade(self.product) + with suppress(Product.DoesNotExist): + # Suppressing a potential issue created from async delete removing + # related objects in a separate task + calculate_grade(self.product) def inherit_tags(self, potentially_existing_tags): # get a copy of the tags to be inherited @@ -2123,7 +2149,7 @@ def copy(self, engagement=None): def unaccepted_open_findings(self): from dojo.utils import get_system_setting findings = Finding.objects.filter(risk_accepted=False, active=True, duplicate=False, test=self) - if get_system_setting("enforce_verified_status", True): + if get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_metrics", True): findings = findings.filter(verified=True) return findings @@ -2185,7 +2211,10 @@ def hash_code_allows_null_cwe(self): def delete(self, *args, **kwargs): logger.debug("%d test delete", self.id) super().delete(*args, **kwargs) - calculate_grade(self.engagement.product) + with suppress(Engagement.DoesNotExist, Product.DoesNotExist): + # Suppressing a potential issue created from async delete removing + # related objects in a separate task + calculate_grade(self.engagement.product) @property def statistics(self): @@ -2745,14 +2774,17 @@ def delete(self, *args, **kwargs): import dojo.finding.helper as helper helper.finding_delete(self) super().delete(*args, **kwargs) - calculate_grade(self.test.engagement.product) + with suppress(Test.DoesNotExist, Engagement.DoesNotExist, Product.DoesNotExist): + # Suppressing a potential issue created from async delete removing + # related objects in a separate task + calculate_grade(self.test.engagement.product) # only used by bulk risk acceptance api @classmethod def unaccepted_open_findings(cls): from dojo.utils import get_system_setting results = cls.objects.filter(active=True, duplicate=False, risk_accepted=False) - if get_system_setting("enforce_verified_status", True): + if get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_metrics", True): results = results.filter(verified=True) return results diff --git a/dojo/reports/views.py b/dojo/reports/views.py index 061476efe1b..4bcd7386769 100644 --- a/dojo/reports/views.py +++ b/dojo/reports/views.py @@ -88,7 +88,7 @@ def get_endpoints(self, request: HttpRequest): finding__duplicate=False, finding__out_of_scope=False, ) - if get_system_setting("enforce_verified_status", True): + if get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_metrics", True): endpoints = endpoints.filter(finding__active=True) endpoints = endpoints.distinct() @@ -194,7 +194,7 @@ def report_endpoints(request): finding__duplicate=False, finding__out_of_scope=False, ) - if get_system_setting("enforce_verified_status", True): + if get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_metrics", True): endpoints = endpoints.filter(finding__active=True) endpoints = endpoints.distinct() @@ -271,7 +271,7 @@ def product_endpoint_report(request, pid): finding__duplicate=False, finding__out_of_scope=False) - if get_system_setting("enforce_verified_status", True): + if get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_metrics", True): endpoint_ids = endpoints.filter(finding__active=True).values_list("id", flat=True) endpoint_ids = endpoints.values_list("id", flat=True) diff --git a/dojo/reports/widgets.py b/dojo/reports/widgets.py index 7439a4bb8f3..f0559d23098 100644 --- a/dojo/reports/widgets.py +++ b/dojo/reports/widgets.py @@ -378,7 +378,7 @@ def report_widget_factory(json_data=None, request=None, user=None, finding_notes finding__duplicate=False, finding__out_of_scope=False, ) - if get_system_setting("enforce_verified_status", True): + if get_system_setting("enforce_verified_status", True) or get_system_setting("enforce_verified_status_metrics", True): endpoints = endpoints.filter(finding__verified=True) endpoints = endpoints.distinct() diff --git a/dojo/settings/settings.dist.py b/dojo/settings/settings.dist.py index 4f72fa171ce..3815942ea1b 100644 --- a/dojo/settings/settings.dist.py +++ b/dojo/settings/settings.dist.py @@ -1761,6 +1761,7 @@ def saml2_attrib_map_format(dict): "RHEA": "https://access.redhat.com/errata/", "FEDORA": "https://bodhi.fedoraproject.org/updates/", "ALSA": "https://osv.dev/vulnerability/", # e.g. https://osv.dev/vulnerability/ALSA-2024:0827 + "ALBA": "https://osv.dev/vulnerability/", # e.g. https://osv.dev/vulnerability/ALBA-2019:3411 "USN": "https://ubuntu.com/security/notices/", # e.g. https://ubuntu.com/security/notices/USN-6642-1 "DLA": "https://security-tracker.debian.org/tracker/", # e.g. https://security-tracker.debian.org/tracker/DLA-3917-1 "DSA": "https://security-tracker.debian.org/tracker/", # e.g. https://security-tracker.debian.org/tracker/DSA-5791-1 diff --git a/dojo/templates/dojo/notifications.html b/dojo/templates/dojo/notifications.html index 81fac49d5cc..9a87197c35e 100644 --- a/dojo/templates/dojo/notifications.html +++ b/dojo/templates/dojo/notifications.html @@ -162,7 +162,7 @@

$(".chosen-select").chosen({disable_search_threshold: 10}); $('#notification-scope').val('{{ scope }}'); $('.chosen-select').trigger('chosen:updated'); - $('#notification-scope').change(function() { window.location = '/notifications/' + $(this).val(); }); + $('#notification-scope').change(function() { window.location = '{% url 'notifications' %}/' + $(this).val(); }); }); {% endblock postscript %} diff --git a/dojo/tools/aws_inspector2/parser.py b/dojo/tools/aws_inspector2/parser.py index 5350044d10d..2281992c81e 100644 --- a/dojo/tools/aws_inspector2/parser.py +++ b/dojo/tools/aws_inspector2/parser.py @@ -221,6 +221,7 @@ def process_endpoints(self, finding: Finding, raw_finding: dict) -> Finding: f"Hash: {ecr_image_details.get('imageHash', 'N/A')}", f"Author: {ecr_image_details.get('author', 'N/A')}", f"Pushed at: {ecr_image_details.get('pushedAt', 'N/A')}", + f"Image tags: {','.join(ecr_image_details.get('imageTags', []))}", "---", ), ) diff --git a/dojo/tools/awssecurityhub/compliance.py b/dojo/tools/awssecurityhub/compliance.py index 563a4995b99..6ad8e05ec2c 100644 --- a/dojo/tools/awssecurityhub/compliance.py +++ b/dojo/tools/awssecurityhub/compliance.py @@ -52,6 +52,7 @@ def get_item(self, finding: dict, test): f"Registry: {details.get('RegistryId')}", f"Repository: {details.get('RepositoryName')}", f"Image digest: {details.get('ImageDigest')}", + f"Image tags: {','.join(details.get('ImageTags', []))}", )) title_suffix = f" - Image: {arn.split('/', 1)[1]}" # repo-name/sha256:digest else: # generic implementation diff --git a/dojo/tools/awssecurityhub/guardduty.py b/dojo/tools/awssecurityhub/guardduty.py index 9f1cfb63aab..f97d8527838 100644 --- a/dojo/tools/awssecurityhub/guardduty.py +++ b/dojo/tools/awssecurityhub/guardduty.py @@ -53,6 +53,7 @@ def get_item(self, finding: dict, test): f"Registry: {details.get('RegistryId')}", f"Repository: {details.get('RepositoryName')}", f"Image digest: {details.get('ImageDigest')}", + f"Image tags: {','.join(details.get('ImageTags', []))}", )) title_suffix = f" - Image: {arn.split('/', 1)[1]}" # repo-name/sha256:digest else: # generic implementation diff --git a/dojo/tools/awssecurityhub/inspector.py b/dojo/tools/awssecurityhub/inspector.py index 5235bf70a05..83ecd37a1a3 100644 --- a/dojo/tools/awssecurityhub/inspector.py +++ b/dojo/tools/awssecurityhub/inspector.py @@ -64,6 +64,7 @@ def get_item(self, finding: dict, test): f"Registry: {details.get('RegistryId')}", f"Repository: {details.get('RepositoryName')}", f"Image digest: {details.get('ImageDigest')}", + f"Image tags: {','.join(details.get('ImageTags', []))}", )) title_suffix = f" - Image: {arn.split('/', 1)[1]}" # repo-name/sha256:digest else: # generic implementation diff --git a/dojo/tools/openvas/xml_parser.py b/dojo/tools/openvas/xml_parser.py index 32b7d001ca9..b50350348c9 100644 --- a/dojo/tools/openvas/xml_parser.py +++ b/dojo/tools/openvas/xml_parser.py @@ -38,6 +38,7 @@ def get_findings(self, filename, test): finding = Finding( title=str(title), + test=test, description="\n".join(description), severity=severity, dynamic_finding=True, diff --git a/dojo/tools/semgrep/parser.py b/dojo/tools/semgrep/parser.py index 39f72f8b431..83eb200a368 100644 --- a/dojo/tools/semgrep/parser.py +++ b/dojo/tools/semgrep/parser.py @@ -35,6 +35,10 @@ def get_findings(self, filename, test): # fingerprint detection unique_id_from_tool = item.get("extra", {}).get("fingerprint") + # treat "requires login" as if the fingerprint is absent + if unique_id_from_tool == "requires login": + unique_id_from_tool = None + if unique_id_from_tool: finding.unique_id_from_tool = unique_id_from_tool @@ -99,6 +103,10 @@ def get_findings(self, filename, test): # fingerprint detection unique_id_from_tool = item.get("extra", {}).get("fingerprint") + # treat "requires login" as if the fingerprint is absent + if unique_id_from_tool == "requires login": + unique_id_from_tool = None + if unique_id_from_tool: finding.unique_id_from_tool = unique_id_from_tool @@ -149,6 +157,9 @@ def get_description(self, item): description += f"**Result message:** {message}\n" snippet = item["extra"].get("lines") + if snippet == "requires login": + snippet = None # Treat "requires login" as no snippet + if snippet is not None: if "