From 74c6563f35299aa4cd4201f48a00b8a04d177f23 Mon Sep 17 00:00:00 2001 From: Valentijn Scholten Date: Tue, 7 Oct 2025 19:54:07 +0200 Subject: [PATCH 01/50] initial batching code --- dojo/finding/helper.py | 261 ++++++++++++++++++++++++ dojo/importers/default_importer.py | 59 +++--- dojo/importers/default_reimporter.py | 58 +++--- unittests/test_importers_performance.py | 16 ++ 4 files changed, 338 insertions(+), 56 deletions(-) diff --git a/dojo/finding/helper.py b/dojo/finding/helper.py index dd78ccbce69..a74de9c5e0c 100644 --- a/dojo/finding/helper.py +++ b/dojo/finding/helper.py @@ -457,6 +457,267 @@ def post_process_finding_save_internal(finding, dedupe_option=True, rules_option jira_helper.push_to_jira(finding.finding_group) +@dojo_async_task(signature=True) +@app.task +def post_process_findings_batch_signature(finding_ids, dedupe_option=True, rules_option=True, product_grading_option=True, # noqa: FBT002 + issue_updater_option=True, push_to_jira=False, user=None, *args, **kwargs): # noqa: FBT002 + return post_process_findings_batch(finding_ids, dedupe_option, rules_option, product_grading_option, + issue_updater_option, push_to_jira, user, *args, **kwargs) + + +@dojo_async_task +@app.task +def post_process_findings_batch(finding_ids, dedupe_option=True, rules_option=True, product_grading_option=True, # noqa: FBT002 + issue_updater_option=True, push_to_jira=False, user=None, *args, **kwargs): # noqa: FBT002 + # if True: + # msg = "WTF" + # raise ValueError(msg) + + if not finding_ids: + return + + system_settings = System_Settings.objects.get() + + # Fetch findings in one go; they should all belong to the same test/import + findings = list( + Finding.objects.filter(id__in=finding_ids) + .select_related("test", "test__engagement", "test__engagement__product") + .prefetch_related("endpoints"), + ) + if not findings: + return + + # Batch dedupe with single queries per algorithm; fallback to per-finding for anything else + if dedupe_option and system_settings.enable_deduplication: + first_test = findings[0].test + dedup_alg = first_test.deduplication_algorithm + scope_on_engagement = first_test.engagement.deduplication_on_engagement + if scope_on_engagement: + scope_filter = {"test__engagement": first_test.engagement} + else: + scope_filter = {"test__engagement__product": first_test.engagement.product} + + # Helper functions imported lazily to avoid circulars at import time + from dojo.utils import ( # noqa: PLC0415 + are_endpoints_duplicates, + is_deduplication_on_engagement_mismatch, + set_duplicate, + ) + + if dedup_alg == settings.DEDUPE_ALGO_HASH_CODE: + logger.debug("deduplicating finding batch with DEDUPE_ALGO_HASH_CODE") + hash_codes = {f.hash_code for f in findings if f.hash_code is not None} + if hash_codes: + existing_qs = ( + Finding.objects.filter(**scope_filter, hash_code__in=hash_codes) + .exclude(id__in=finding_ids) + .exclude(hash_code=None) + .exclude(duplicate=True) + .order_by("id") + ) + existing_by_hash = {} + for ef in existing_qs: + existing_by_hash.setdefault(ef.hash_code, []).append(ef) + + for new_finding in findings: + if new_finding.hash_code is None: + continue + for candidate in existing_by_hash.get(new_finding.hash_code, []): + if is_deduplication_on_engagement_mismatch(new_finding, candidate): + continue + try: + if are_endpoints_duplicates(new_finding, candidate): + set_duplicate(new_finding, candidate) + break + except Exception as e: + deduplicationLogger.debug(str(e)) + continue + + elif dedup_alg == settings.DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL: + logger.debug("deduplicating finding batch with DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL") + unique_ids = {f.unique_id_from_tool for f in findings if f.unique_id_from_tool is not None} + if unique_ids: + base_filter = { + **scope_filter, + "unique_id_from_tool__in": unique_ids, + } + # For product scope, limit to same test_type like the single-finding logic + if not scope_on_engagement: + base_filter["test__test_type"] = first_test.test_type + existing_qs = ( + Finding.objects.filter(**base_filter) + .exclude(id__in=finding_ids) + .exclude(unique_id_from_tool=None) + .exclude(duplicate=True) + .order_by("id") + ) + existing_by_uid = {} + for ef in existing_qs: + existing_by_uid.setdefault(ef.unique_id_from_tool, []).append(ef) + + for new_finding in findings: + if new_finding.unique_id_from_tool is None: + continue + for candidate in existing_by_uid.get(new_finding.unique_id_from_tool, []): + # unique_id path sets duplicate without endpoint comparison in utils + try: + set_duplicate(new_finding, candidate) + break + except Exception as e: + deduplicationLogger.debug(str(e)) + continue + + elif dedup_alg == settings.DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL_OR_HASH_CODE: + logger.debug("deduplicating finding batch with DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL_OR_HASH_CODE") + hash_codes = {f.hash_code for f in findings if f.hash_code is not None} + unique_ids = {f.unique_id_from_tool for f in findings if f.unique_id_from_tool is not None} + if hash_codes or unique_ids: + from django.db.models import Q # noqa: PLC0415 + base_q = Q(**scope_filter) + cond = Q() + if hash_codes: + cond |= Q(hash_code__isnull=False, hash_code__in=hash_codes) + if unique_ids: + uid_q = Q(unique_id_from_tool__isnull=False, unique_id_from_tool__in=unique_ids) + # same test_type constraint for product scope + if not scope_on_engagement: + uid_q &= Q(test__test_type=first_test.test_type) + cond |= uid_q + existing_qs = ( + Finding.objects.filter(base_q & cond) + .exclude(id__in=finding_ids) + .exclude(duplicate=True) + .order_by("id") + ) + existing_by_hash = {} + existing_by_uid = {} + for ef in existing_qs: + if ef.hash_code is not None: + existing_by_hash.setdefault(ef.hash_code, []).append(ef) + if ef.unique_id_from_tool is not None: + existing_by_uid.setdefault(ef.unique_id_from_tool, []).append(ef) + + for new_finding in findings: + # Try unique_id first like utils; then hash_code + if new_finding.unique_id_from_tool is not None: + duplicate_found = False + for candidate in existing_by_uid.get(new_finding.unique_id_from_tool, []): + try: + if are_endpoints_duplicates(new_finding, candidate): + set_duplicate(new_finding, candidate) + duplicate_found = True + break + except Exception as e: + deduplicationLogger.debug(str(e)) + continue + if not duplicate_found and new_finding.hash_code is not None: + for candidate in existing_by_hash.get(new_finding.hash_code, []): + if is_deduplication_on_engagement_mismatch(new_finding, candidate): + continue + try: + if are_endpoints_duplicates(new_finding, candidate): + set_duplicate(new_finding, candidate) + break + except Exception as e: + deduplicationLogger.debug(str(e)) + continue + elif new_finding.hash_code is not None: + for candidate in existing_by_hash.get(new_finding.hash_code, []): + if is_deduplication_on_engagement_mismatch(new_finding, candidate): + continue + try: + if are_endpoints_duplicates(new_finding, candidate): + set_duplicate(new_finding, candidate) + break + except Exception as e: + deduplicationLogger.debug(str(e)) + continue + else: + logger.debug("deduplicating finding batch with LEGACY") + # Legacy algorithm in batch: candidates share title or CWE, then apply legacy checks + from django.db.models import Q # noqa: PLC0415 + + titles = {f.title for f in findings if f.title} + cwes = {f.cwe for f in findings if getattr(f, "cwe", 0)} + cwes.discard(0) + + if titles or cwes: + existing_qs = ( + Finding.objects.filter( + Q(**scope_filter) & (Q(title__in=titles) | Q(cwe__in=cwes)), + ) + .exclude(id__in=finding_ids) + .exclude(duplicate=True) + .prefetch_related( + "endpoints", + "test", + "test__engagement", + "found_by", + "original_finding", + "test__test_type", + ) + .order_by("id") + ) + + by_title = {} + by_cwe = {} + for ef in existing_qs: + if ef.title: + by_title.setdefault(ef.title, []).append(ef) + if ef.cwe and ef.cwe != 0: + by_cwe.setdefault(ef.cwe, []).append(ef) + + for new_finding in findings: + # union of candidates with same title or same CWE + candidates = [] + if new_finding.title: + candidates.extend(by_title.get(new_finding.title, [])) + if getattr(new_finding, "cwe", 0): + candidates.extend(by_cwe.get(new_finding.cwe, [])) + + # apply legacy checks + for candidate in candidates: + if is_deduplication_on_engagement_mismatch(new_finding, candidate): + continue + + flag_endpoints = False + flag_line_path = False + + if candidate.endpoints.count() != 0 and new_finding.endpoints.count() != 0: + list1 = [str(e) for e in new_finding.endpoints.all()] + list2 = [str(e) for e in candidate.endpoints.all()] + if all(x in list1 for x in list2): + flag_endpoints = True + elif new_finding.static_finding and new_finding.file_path and len(new_finding.file_path) > 0: + if str(candidate.line) == str(new_finding.line) and candidate.file_path == new_finding.file_path: + flag_line_path = True + + flag_hash = candidate.hash_code == new_finding.hash_code + + if (flag_endpoints or flag_line_path) and flag_hash: + try: + set_duplicate(new_finding, candidate) + break + except Exception as e: + deduplicationLogger.debug(str(e)) + continue + + # Non-status changing tasks + if issue_updater_option: + for finding in findings: + tool_issue_updater.async_tool_issue_update(finding) + + if product_grading_option and system_settings.enable_product_grade: + calculate_grade(findings[0].test.engagement.product) + + if push_to_jira: + for finding in findings: + if finding.has_jira_issue or not finding.finding_group: + jira_helper.push_to_jira(finding) + else: + jira_helper.push_to_jira(finding.finding_group) + + @receiver(pre_delete, sender=Finding) def finding_pre_delete(sender, instance, **kwargs): logger.debug("finding pre_delete: %d", instance.id) diff --git a/dojo/importers/default_importer.py b/dojo/importers/default_importer.py index 726e55717eb..7268e21c279 100644 --- a/dojo/importers/default_importer.py +++ b/dojo/importers/default_importer.py @@ -157,10 +157,9 @@ def process_findings( parsed_findings: list[Finding], **kwargs: dict, ) -> list[Finding]: - # Progressive batching for chord execution - post_processing_task_signatures = [] - current_batch_number = 1 - max_batch_size = 1024 + # Batched post-processing (no chord): dispatch a task per 1000 findings or on final finding + batch_finding_ids: list[int] = [] + batch_max_size = 1000 """ Saves findings in memory that were parsed from the scan report into the database. @@ -233,32 +232,34 @@ def process_findings( finding = self.process_vulnerability_ids(finding) # Categorize this finding as a new one new_findings.append(finding) - # all data is already saved on the finding, we only need to trigger post processing - - # We create a signature for the post processing task so we can decide to apply it async or sync + # all data is already saved on the finding, we only need to trigger post processing in batches push_to_jira = self.push_to_jira and (not self.findings_groups_enabled or not self.group_by) - post_processing_task_signature = finding_helper.post_process_finding_save_signature( - finding, - dedupe_option=True, - rules_option=True, - product_grading_option=False, - issue_updater_option=True, - push_to_jira=push_to_jira, - ) - - post_processing_task_signatures.append(post_processing_task_signature) - - # Check if we should launch a chord (batch full or end of findings) - if we_want_async(async_user=self.user) and post_processing_task_signatures: - post_processing_task_signatures, current_batch_number, _ = self.maybe_launch_post_processing_chord( - post_processing_task_signatures, - current_batch_number, - max_batch_size, - is_final_finding, - ) - else: - # Execute task immediately for synchronous processing - post_processing_task_signature() + batch_finding_ids.append(finding.id) + + # If batch is full or we're at the end, dispatch one batched task + if len(batch_finding_ids) >= batch_max_size or is_final_finding: + finding_ids_batch = list(batch_finding_ids) + batch_finding_ids.clear() + if we_want_async(async_user=self.user): + finding_helper.post_process_findings_batch_signature( + finding_ids_batch, + dedupe_option=True, + rules_option=True, + product_grading_option=True, + issue_updater_option=True, + push_to_jira=push_to_jira, + )() + else: + finding_helper.post_process_findings_batch( + finding_ids_batch, + dedupe_option=True, + rules_option=True, + product_grading_option=True, + issue_updater_option=True, + push_to_jira=push_to_jira, + ) + + # No chord: tasks are dispatched immediately above per batch for (group_name, findings) in group_names_to_findings_dict.items(): finding_helper.add_findings_to_auto_group( diff --git a/dojo/importers/default_reimporter.py b/dojo/importers/default_reimporter.py index 17775eb22ae..7feff4db53c 100644 --- a/dojo/importers/default_reimporter.py +++ b/dojo/importers/default_reimporter.py @@ -179,9 +179,7 @@ def process_findings( self.unchanged_items = [] self.group_names_to_findings_dict = {} # Progressive batching for chord execution - post_processing_task_signatures = [] - current_batch_number = 1 - max_batch_size = 1024 + # No chord: we dispatch per 1000 findings or on the final finding logger.debug(f"starting reimport of {len(parsed_findings) if parsed_findings else 0} items.") logger.debug("STEP 1: looping over findings from the reimported report and trying to match them to existing findings") @@ -201,6 +199,9 @@ def process_findings( continue cleaned_findings.append(sanitized) + batch_finding_ids: list[int] = [] + batch_max_size = 1000 + for idx, unsaved_finding in enumerate(cleaned_findings): is_final = idx == len(cleaned_findings) - 1 # Some parsers provide "mitigated" field but do not set timezone (because they are probably not available in the report) @@ -251,31 +252,34 @@ def process_findings( finding, unsaved_finding, ) - # all data is already saved on the finding, we only need to trigger post processing - - # Execute post-processing task immediately if async, otherwise execute synchronously + # all data is already saved on the finding, we only need to trigger post processing in batches push_to_jira = self.push_to_jira and (not self.findings_groups_enabled or not self.group_by) - - post_processing_task_signature = finding_helper.post_process_finding_save_signature( - finding, - dedupe_option=True, - rules_option=True, - product_grading_option=False, - issue_updater_option=True, - push_to_jira=push_to_jira, - ) - post_processing_task_signatures.append(post_processing_task_signature) - - # Check if we should launch a chord (batch full or end of findings) - if we_want_async(async_user=self.user) and post_processing_task_signatures: - post_processing_task_signatures, current_batch_number, _ = self.maybe_launch_post_processing_chord( - post_processing_task_signatures, - current_batch_number, - max_batch_size, - is_final, - ) - else: - post_processing_task_signature() + batch_finding_ids.append(finding.id) + + # If batch is full or we're at the end, dispatch one batched task + if len(batch_finding_ids) >= batch_max_size or is_final: + finding_ids_batch = list(batch_finding_ids) + batch_finding_ids.clear() + if we_want_async(async_user=self.user): + finding_helper.post_process_findings_batch_signature( + finding_ids_batch, + dedupe_option=True, + rules_option=True, + product_grading_option=True, + issue_updater_option=True, + push_to_jira=push_to_jira, + )() + else: + finding_helper.post_process_findings_batch( + finding_ids_batch, + dedupe_option=True, + rules_option=True, + product_grading_option=True, + issue_updater_option=True, + push_to_jira=push_to_jira, + ) + + # No chord: tasks are dispatched immediately above per batch self.to_mitigate = (set(self.original_items) - set(self.reactivated_items) - set(self.unchanged_items)) # due to #3958 we can have duplicates inside the same report diff --git a/unittests/test_importers_performance.py b/unittests/test_importers_performance.py index 3b4ce357c85..48cd167edab 100644 --- a/unittests/test_importers_performance.py +++ b/unittests/test_importers_performance.py @@ -220,7 +220,11 @@ def test_import_reimport_reimport_performance_no_async(self): testuser.usercontactinfo.block_execution = True testuser.usercontactinfo.save() self._import_reimport_performance( +<<<<<<< HEAD expected_num_queries1=603, +======= + expected_num_queries1=593, +>>>>>>> c10543f124 (initial batching code) expected_num_async_tasks1=10, expected_num_queries2=515, expected_num_async_tasks2=22, @@ -242,7 +246,11 @@ def test_import_reimport_reimport_performance_pghistory_no_async(self): testuser.usercontactinfo.save() self._import_reimport_performance( +<<<<<<< HEAD expected_num_queries1=569, +======= + expected_num_queries1=559, +>>>>>>> c10543f124 (initial batching code) expected_num_async_tasks1=10, expected_num_queries2=508, expected_num_async_tasks2=22, @@ -268,7 +276,11 @@ def test_import_reimport_reimport_performance_no_async_with_product_grading(self self.system_settings(enable_product_grade=True) self._import_reimport_performance( +<<<<<<< HEAD expected_num_queries1=604, +======= + expected_num_queries1=594, +>>>>>>> c10543f124 (initial batching code) expected_num_async_tasks1=11, expected_num_queries2=516, expected_num_async_tasks2=23, @@ -291,7 +303,11 @@ def test_import_reimport_reimport_performance_pghistory_no_async_with_product_gr self.system_settings(enable_product_grade=True) self._import_reimport_performance( +<<<<<<< HEAD expected_num_queries1=570, +======= + expected_num_queries1=560, +>>>>>>> c10543f124 (initial batching code) expected_num_async_tasks1=11, expected_num_queries2=509, expected_num_async_tasks2=23, From 01842eb3f80582521e6d7a360f250be2289780b5 Mon Sep 17 00:00:00 2001 From: Valentijn Scholten Date: Mon, 13 Oct 2025 19:53:08 +0200 Subject: [PATCH 02/50] fix dedupe_inside_engagement --- dojo/finding/helper.py | 7 ++++++- dojo/utils.py | 6 +++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/dojo/finding/helper.py b/dojo/finding/helper.py index a74de9c5e0c..225d7962d0f 100644 --- a/dojo/finding/helper.py +++ b/dojo/finding/helper.py @@ -495,7 +495,12 @@ def post_process_findings_batch(finding_ids, dedupe_option=True, rules_option=Tr if scope_on_engagement: scope_filter = {"test__engagement": first_test.engagement} else: - scope_filter = {"test__engagement__product": first_test.engagement.product} + # When deduplicating across the product, exclude findings from engagements + # that have deduplication_on_engagement=True + scope_filter = { + "test__engagement__product": first_test.engagement.product, + "test__engagement__deduplication_on_engagement": False, + } # Helper functions imported lazily to avoid circulars at import time from dojo.utils import ( # noqa: PLC0415 diff --git a/dojo/utils.py b/dojo/utils.py index 07709c4bbbf..b5a3edbc3c3 100644 --- a/dojo/utils.py +++ b/dojo/utils.py @@ -239,7 +239,11 @@ def match_finding_to_existing_findings(finding, product=None, engagement=None, t # true if both findings are on an engagement that have a different "deduplication on engagement" configuration def is_deduplication_on_engagement_mismatch(new_finding, to_duplicate_finding): - return not new_finding.test.engagement.deduplication_on_engagement and to_duplicate_finding.test.engagement.deduplication_on_engagement + # Return True if findings are from different engagements and at least one has deduplication_on_engagement=True + if new_finding.test.engagement != to_duplicate_finding.test.engagement: + return (new_finding.test.engagement.deduplication_on_engagement or + to_duplicate_finding.test.engagement.deduplication_on_engagement) + return False def get_endpoints_as_url(finding): From ccc5ad16ab40d55ec2c6b80d3e67b0fb9d922c13 Mon Sep 17 00:00:00 2001 From: Valentijn Scholten Date: Mon, 13 Oct 2025 21:19:03 +0200 Subject: [PATCH 03/50] all tests working incl sarif with internal dupes --- dojo/finding/helper.py | 152 +++++++++++++++++++++++++++-------------- 1 file changed, 101 insertions(+), 51 deletions(-) diff --git a/dojo/finding/helper.py b/dojo/finding/helper.py index 225d7962d0f..ca60771b24e 100644 --- a/dojo/finding/helper.py +++ b/dojo/finding/helper.py @@ -479,11 +479,13 @@ def post_process_findings_batch(finding_ids, dedupe_option=True, rules_option=Tr system_settings = System_Settings.objects.get() # Fetch findings in one go; they should all belong to the same test/import + logger.debug(f"SARIF Debug: finding_ids parameter contains {len(finding_ids)} IDs: {finding_ids}") findings = list( Finding.objects.filter(id__in=finding_ids) .select_related("test", "test__engagement", "test__engagement__product") .prefetch_related("endpoints"), ) + logger.debug(f"SARIF Debug: Database query returned {len(findings)} findings: {[f.id for f in findings]}") if not findings: return @@ -493,15 +495,23 @@ def post_process_findings_batch(finding_ids, dedupe_option=True, rules_option=Tr dedup_alg = first_test.deduplication_algorithm scope_on_engagement = first_test.engagement.deduplication_on_engagement if scope_on_engagement: + # When deduplicating within engagement, only look at the current engagement scope_filter = {"test__engagement": first_test.engagement} else: - # When deduplicating across the product, exclude findings from engagements - # that have deduplication_on_engagement=True scope_filter = { "test__engagement__product": first_test.engagement.product, - "test__engagement__deduplication_on_engagement": False, } + # Create base queryset with proper scope filtering + base_queryset = Finding.objects.filter(**scope_filter) + + # Add Q object filter for product scope to exclude other engagements with deduplication_on_engagement=True + if not scope_on_engagement: + base_queryset = base_queryset.filter( + Q(test__engagement=first_test.engagement) | # Include current engagement always + Q(test__engagement__deduplication_on_engagement=False), # Include other engagements without deduplication_on_engagement + ) + # Helper functions imported lazily to avoid circulars at import time from dojo.utils import ( # noqa: PLC0415 are_endpoints_duplicates, @@ -510,12 +520,11 @@ def post_process_findings_batch(finding_ids, dedupe_option=True, rules_option=Tr ) if dedup_alg == settings.DEDUPE_ALGO_HASH_CODE: - logger.debug("deduplicating finding batch with DEDUPE_ALGO_HASH_CODE") + logger.debug(f"deduplicating finding batch with DEDUPE_ALGO_HASH_CODE - {len(finding_ids)} findings") hash_codes = {f.hash_code for f in findings if f.hash_code is not None} if hash_codes: existing_qs = ( Finding.objects.filter(**scope_filter, hash_code__in=hash_codes) - .exclude(id__in=finding_ids) .exclude(hash_code=None) .exclude(duplicate=True) .order_by("id") @@ -539,23 +548,21 @@ def post_process_findings_batch(finding_ids, dedupe_option=True, rules_option=Tr continue elif dedup_alg == settings.DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL: - logger.debug("deduplicating finding batch with DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL") + logger.debug(f"deduplicating finding batch with DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL - {len(finding_ids)} findings") unique_ids = {f.unique_id_from_tool for f in findings if f.unique_id_from_tool is not None} if unique_ids: - base_filter = { - **scope_filter, - "unique_id_from_tool__in": unique_ids, - } + # Use base queryset and add specific filters + existing_qs = base_queryset.filter( + unique_id_from_tool__in=unique_ids, + ).exclude( + unique_id_from_tool=None, + ).exclude( + duplicate=True, + ).order_by("id") + # For product scope, limit to same test_type like the single-finding logic if not scope_on_engagement: - base_filter["test__test_type"] = first_test.test_type - existing_qs = ( - Finding.objects.filter(**base_filter) - .exclude(id__in=finding_ids) - .exclude(unique_id_from_tool=None) - .exclude(duplicate=True) - .order_by("id") - ) + existing_qs = existing_qs.filter(test__test_type=first_test.test_type) existing_by_uid = {} for ef in existing_qs: existing_by_uid.setdefault(ef.unique_id_from_tool, []).append(ef) @@ -573,12 +580,11 @@ def post_process_findings_batch(finding_ids, dedupe_option=True, rules_option=Tr continue elif dedup_alg == settings.DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL_OR_HASH_CODE: - logger.debug("deduplicating finding batch with DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL_OR_HASH_CODE") + logger.debug(f"deduplicating finding batch with DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL_OR_HASH_CODE - {len(finding_ids)} findings") hash_codes = {f.hash_code for f in findings if f.hash_code is not None} unique_ids = {f.unique_id_from_tool for f in findings if f.unique_id_from_tool is not None} if hash_codes or unique_ids: - from django.db.models import Q # noqa: PLC0415 - base_q = Q(**scope_filter) + # noqa: PLC0415 cond = Q() if hash_codes: cond |= Q(hash_code__isnull=False, hash_code__in=hash_codes) @@ -588,12 +594,13 @@ def post_process_findings_batch(finding_ids, dedupe_option=True, rules_option=Tr if not scope_on_engagement: uid_q &= Q(test__test_type=first_test.test_type) cond |= uid_q - existing_qs = ( - Finding.objects.filter(base_q & cond) - .exclude(id__in=finding_ids) - .exclude(duplicate=True) - .order_by("id") - ) + + # Use base queryset and add specific filters + existing_qs = base_queryset.filter(cond).exclude( + duplicate=True, + ).order_by("id") + + # Build dictionaries for existing findings existing_by_hash = {} existing_by_uid = {} for ef in existing_qs: @@ -602,67 +609,110 @@ def post_process_findings_batch(finding_ids, dedupe_option=True, rules_option=Tr if ef.unique_id_from_tool is not None: existing_by_uid.setdefault(ef.unique_id_from_tool, []).append(ef) + + # Debug logging for SARIF + logger.debug(f"SARIF Debug: Found {len(existing_by_hash)} unique hash codes in existing findings") + logger.debug(f"SARIF Debug: Found {len(existing_by_uid)} unique unique_ids in existing findings") + for unique_id, existing_findings in existing_by_uid.items(): + if len(existing_findings) > 1: + logger.debug(f"SARIF Debug: Unique ID {unique_id} has {len(existing_findings)} findings: {[f.id for f in existing_findings]}") + + for hash_code, existing_findings in existing_by_hash.items(): + if len(existing_findings) > 1: + logger.debug(f"SARIF Debug: Hash {hash_code} has {len(existing_findings)} findings: {[f.id for f in existing_findings]}") + + logger.debug(f"SARIF Debug: Processing {len(findings)} findings in batch") for new_finding in findings: + logger.debug(f"SARIF Debug: Start dedupe of finding {new_finding.id} with unique_id {new_finding.unique_id_from_tool} and hash {new_finding.hash_code}") + # Skip if already marked as duplicate + if new_finding.duplicate: + continue + + duplicate_found = False + # Try unique_id first like utils; then hash_code if new_finding.unique_id_from_tool is not None: - duplicate_found = False + # Check against existing findings in database for candidate in existing_by_uid.get(new_finding.unique_id_from_tool, []): + if candidate.id == new_finding.id: + continue # Skip self-comparison + # Prefer anchoring on the older finding (lower id) + if candidate.id >= new_finding.id: + continue + if is_deduplication_on_engagement_mismatch(new_finding, candidate): + continue try: - if are_endpoints_duplicates(new_finding, candidate): - set_duplicate(new_finding, candidate) - duplicate_found = True - break + + # For unique_id matches, do not gate on endpoint equality. + # Aligns with one-by-one dedupe behavior. + set_duplicate(new_finding, candidate) + duplicate_found = True + break except Exception as e: deduplicationLogger.debug(str(e)) continue + + # If no duplicate found by unique_id, try hash_code if not duplicate_found and new_finding.hash_code is not None: + # Check against existing findings in database for candidate in existing_by_hash.get(new_finding.hash_code, []): + if candidate.id == new_finding.id: + continue # Skip self-comparison + # Prefer anchoring on the older finding (lower id) + if candidate.id >= new_finding.id: + continue if is_deduplication_on_engagement_mismatch(new_finding, candidate): continue try: if are_endpoints_duplicates(new_finding, candidate): set_duplicate(new_finding, candidate) + duplicate_found = True break except Exception as e: deduplicationLogger.debug(str(e)) continue + elif new_finding.hash_code is not None: + # Check against existing findings in database for candidate in existing_by_hash.get(new_finding.hash_code, []): + if candidate.id == new_finding.id: + continue # Skip self-comparison if is_deduplication_on_engagement_mismatch(new_finding, candidate): continue try: if are_endpoints_duplicates(new_finding, candidate): set_duplicate(new_finding, candidate) - break + duplicate_found = True except Exception as e: deduplicationLogger.debug(str(e)) continue + + if not duplicate_found: + logger.debug(f"SARIF Debug: No duplicate found for finding {new_finding.id} with unique_id {new_finding.unique_id_from_tool} and hash {new_finding.hash_code} ") + else: - logger.debug("deduplicating finding batch with LEGACY") + logger.debug(f"deduplicating finding batch with LEGACY - {len(finding_ids)} findings") # Legacy algorithm in batch: candidates share title or CWE, then apply legacy checks - from django.db.models import Q # noqa: PLC0415 + # noqa: PLC0415 titles = {f.title for f in findings if f.title} cwes = {f.cwe for f in findings if getattr(f, "cwe", 0)} cwes.discard(0) if titles or cwes: - existing_qs = ( - Finding.objects.filter( - Q(**scope_filter) & (Q(title__in=titles) | Q(cwe__in=cwes)), - ) - .exclude(id__in=finding_ids) - .exclude(duplicate=True) - .prefetch_related( - "endpoints", - "test", - "test__engagement", - "found_by", - "original_finding", - "test__test_type", - ) - .order_by("id") - ) + # Use base queryset and add specific filters + existing_qs = base_queryset.filter( + Q(title__in=titles) | Q(cwe__in=cwes), + ).exclude( + duplicate=True, + ).prefetch_related( + "endpoints", + "test", + "test__engagement", + "found_by", + "original_finding", + "test__test_type", + ).order_by("id") by_title = {} by_cwe = {} From 01c4911dea65933e5d0cc3760b583618c96a7bfd Mon Sep 17 00:00:00 2001 From: Valentijn Scholten Date: Wed, 15 Oct 2025 17:20:26 +0200 Subject: [PATCH 04/50] cleanup --- dojo/finding/helper.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/dojo/finding/helper.py b/dojo/finding/helper.py index ca60771b24e..6841a5d1563 100644 --- a/dojo/finding/helper.py +++ b/dojo/finding/helper.py @@ -584,7 +584,6 @@ def post_process_findings_batch(finding_ids, dedupe_option=True, rules_option=Tr hash_codes = {f.hash_code for f in findings if f.hash_code is not None} unique_ids = {f.unique_id_from_tool for f in findings if f.unique_id_from_tool is not None} if hash_codes or unique_ids: - # noqa: PLC0415 cond = Q() if hash_codes: cond |= Q(hash_code__isnull=False, hash_code__in=hash_codes) @@ -609,7 +608,6 @@ def post_process_findings_batch(finding_ids, dedupe_option=True, rules_option=Tr if ef.unique_id_from_tool is not None: existing_by_uid.setdefault(ef.unique_id_from_tool, []).append(ef) - # Debug logging for SARIF logger.debug(f"SARIF Debug: Found {len(existing_by_hash)} unique hash codes in existing findings") logger.debug(f"SARIF Debug: Found {len(existing_by_uid)} unique unique_ids in existing findings") @@ -693,7 +691,6 @@ def post_process_findings_batch(finding_ids, dedupe_option=True, rules_option=Tr else: logger.debug(f"deduplicating finding batch with LEGACY - {len(finding_ids)} findings") # Legacy algorithm in batch: candidates share title or CWE, then apply legacy checks - # noqa: PLC0415 titles = {f.title for f in findings if f.title} cwes = {f.cwe for f in findings if getattr(f, "cwe", 0)} From 53b22589f52817650fc5d260212bbbf97ed05f77 Mon Sep 17 00:00:00 2001 From: Valentijn Scholten Date: Sun, 19 Oct 2025 11:42:10 +0200 Subject: [PATCH 05/50] deduplication: add more importer unit tests --- ...ndings_fabricated_internal_duplicates.json | 577 +++++++++ ...fabricated_internal_duplicates_subset.json | 391 ++++++ .../multiple_findings_fabricated_subset.json | 370 ++++++ ...-fabricated-no-internal-dupes-subset.sarif | 405 ++++++ ...-report-fabricated-no-internal-dupes.sarif | 446 +++++++ ...rnetes_fabricated_internal_duplicates.json | 1141 +++++++++++++++++ ...fabricated_internal_duplicates_subset.json | 388 ++++++ .../trivy/kubernetes_fabricated_subset.json | 1141 +++++++++++++++++ ...ne_dojo_fabricated_internal_duplicates.xml | 833 ++++++++++++ ..._fabricated_internal_duplicates_subset.xml | 462 +++++++ unittests/test_importers_deduplication.py | 409 +++--- 11 files changed, 6412 insertions(+), 151 deletions(-) create mode 100644 unittests/scans/checkmarx/multiple_findings_fabricated_internal_duplicates.json create mode 100644 unittests/scans/checkmarx/multiple_findings_fabricated_internal_duplicates_subset.json create mode 100644 unittests/scans/checkmarx/multiple_findings_fabricated_subset.json create mode 100644 unittests/scans/sarif/bash-report-fabricated-no-internal-dupes-subset.sarif create mode 100644 unittests/scans/sarif/bash-report-fabricated-no-internal-dupes.sarif create mode 100644 unittests/scans/trivy/kubernetes_fabricated_internal_duplicates.json create mode 100644 unittests/scans/trivy/kubernetes_fabricated_internal_duplicates_subset.json create mode 100644 unittests/scans/trivy/kubernetes_fabricated_subset.json create mode 100644 unittests/scans/zap/dvwa_baseline_dojo_fabricated_internal_duplicates.xml create mode 100644 unittests/scans/zap/dvwa_baseline_dojo_fabricated_internal_duplicates_subset.xml diff --git a/unittests/scans/checkmarx/multiple_findings_fabricated_internal_duplicates.json b/unittests/scans/checkmarx/multiple_findings_fabricated_internal_duplicates.json new file mode 100644 index 00000000000..f86c0f7134e --- /dev/null +++ b/unittests/scans/checkmarx/multiple_findings_fabricated_internal_duplicates.json @@ -0,0 +1,577 @@ +{ + "reportId": "hidden", + "reportHeader": { + "projectName": "DIVA", + "createdDate": "2022-02-25T21:56:10.318Z", + "tenantId": "hidden" + }, + "executiveSummary": { + "branchName": "", + "projectName": "DIVA", + "engines": [ + "SAST" + ], + "riskLevel": "No Risk", + "totalVulnerabilities": 11, + "newVulnerabilities": 10, + "recurrentVulnerabilities": 0, + "vulnerabilitiesPerEngine": { + "SAST": 10 + }, + "resultsTriage": { + "SAST": { + "Confirmed": { + "name": "Confirmed", + "amount": 0, + "percentage": 0 + }, + "Not exploitable": { + "name": "Not exploitable", + "amount": 0, + "percentage": 0 + }, + "To verify": { + "name": "To verify", + "amount": 10, + "percentage": 100 + }, + "Urgent": { + "name": "Urgent", + "amount": 0, + "percentage": 0 + } + } + } + }, + "scanSummary": { + "scanId": "hidden", + "languages": [ + "Java" + ], + "enginesCount": 1, + "scanCompletedDate": "2022-02-25 21:55:16.281169 +0000 UTC", + "engineTypes": [ + "SAST" + ] + }, + "scanResults": { + "sast": { + "languages": [ + { + "languageName": "Java", + "queries": [ + { + "queryName": "SQL_Injection", + "queryId": "", + "description": "", + "vulnerabilitiesTotal": 3, + "vulnerabilities": [ + { + "id": "YmC0We6hAbZWhIrrniEWGot4AHQ=", + "similarityId": -665784454, + "status": "NEW", + "state": "To verify", + "severity": "HIGH", + "groupName": "Java_High_Risk", + "cweId": 89, + "confidenceLevel": 0, + "compliance": [ + "OWASP Top 10 2013", + "OWASP Top 10 API", + "OWASP Mobile Top 10 2016", + "OWASP Top 10 2021", + "FISMA 2014", + "PCI DSS v3.2.1", + "ASD STIG 4.10", + "NIST SP 800-53", + "OWASP Top 10 2017" + ], + "firstScanId": "hidden", + "nodes": [ + { + "column": 88, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/SQLInjectionActivity.java", + "fullName": "jakhar.aseem.diva.SQLInjectionActivity.search.srchtxt.getText", + "length": 1, + "line": 70, + "methodLine": 66, + "name": "getText", + "domType": "MethodInvokeExpr", + "method": "search" + }, + { + "column": 99, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/SQLInjectionActivity.java", + "fullName": "jakhar.aseem.diva.SQLInjectionActivity.search.toString", + "length": 1, + "line": 70, + "methodLine": 66, + "name": "toString", + "domType": "MethodInvokeExpr", + "method": "search" + }, + { + "column": 30, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/SQLInjectionActivity.java", + "fullName": "jakhar.aseem.diva.SQLInjectionActivity.mDB.rawQuery", + "length": 1, + "line": 70, + "methodLine": 66, + "name": "rawQuery", + "domType": "MethodInvokeExpr", + "method": "search" + } + ], + "foundDate": "2022-02-25T21:55:08Z", + "firstFoundDate": "2022-02-25T21:55:07Z" + }, + { + "id": "YmC0We6hAbZWhIrrniEWGot4AHQ=", + "similarityId": 816060275, + "status": "NEW", + "state": "To verify", + "severity": "HIGH", + "groupName": "Java_High_Risk", + "cweId": 89, + "confidenceLevel": 0, + "compliance": [ + "OWASP Top 10 2013", + "OWASP Top 10 API", + "OWASP Mobile Top 10 2016", + "OWASP Top 10 2021", + "FISMA 2014", + "PCI DSS v3.2.1", + "ASD STIG 4.10", + "NIST SP 800-53", + "OWASP Top 10 2017" + ], + "firstScanId": "hidden", + "nodes": [ + { + "column": 68, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.saveCredentials.usr.getText", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "getText", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + }, + { + "column": 79, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.saveCredentials.toString", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "toString", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + }, + { + "column": 24, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.mDB.execSQL", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "execSQL", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + } + ], + "foundDate": "2022-02-25T21:55:08Z", + "firstFoundDate": "2022-02-25T21:55:07Z" + }, + { + "id": "YmC0We6hAbZWhIrrniEWGot4AHQ=", + "similarityId": -722600838, + "status": "NEW", + "state": "To verify", + "severity": "HIGH", + "groupName": "Java_High_Risk", + "cweId": 89, + "confidenceLevel": 0, + "compliance": [ + "OWASP Top 10 2013", + "OWASP Top 10 API", + "OWASP Mobile Top 10 2016", + "OWASP Top 10 2021", + "FISMA 2014", + "PCI DSS v3.2.1", + "ASD STIG 4.10", + "NIST SP 800-53", + "OWASP Top 10 2017" + ], + "firstScanId": "hidden", + "nodes": [ + { + "column": 102, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.saveCredentials.pwd.getText", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "getText", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + }, + { + "column": 113, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.saveCredentials.toString", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "toString", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + }, + { + "column": 24, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.mDB.execSQL", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "execSQL", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + } + ], + "foundDate": "2022-02-25T21:55:08Z", + "firstFoundDate": "2022-02-25T21:55:07Z" + } + ] + }, + { + "queryName": "CSRF", + "queryId": "", + "description": "", + "vulnerabilitiesTotal": 2, + "vulnerabilities": [ + { + "id": "mJHdLIgE2fx10ehNLPytxzPTVCo=", + "similarityId": 1268494559, + "status": "NEW", + "state": "To verify", + "severity": "MEDIUM", + "groupName": "Java_Medium_Threat", + "cweId": 352, + "confidenceLevel": 0, + "compliance": [ + "OWASP Top 10 2013", + "OWASP Top 10 2021", + "PCI DSS v3.2.1", + "ASD STIG 4.10", + "NIST SP 800-53" + ], + "firstScanId": "hidden", + "nodes": [ + { + "column": 68, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.saveCredentials.usr.getText", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "getText", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + }, + { + "column": 79, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.saveCredentials.toString", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "toString", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + }, + { + "column": 24, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.mDB.execSQL", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "execSQL", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + } + ], + "foundDate": "2022-02-25T21:55:08Z", + "firstFoundDate": "2022-02-25T21:55:06Z" + }, + { + "id": "mJHdLIgE2fx10ehNLPytxzPTVCo=", + "similarityId": -443707656, + "status": "NEW", + "state": "To verify", + "severity": "MEDIUM", + "groupName": "Java_Medium_Threat", + "cweId": 352, + "confidenceLevel": 0, + "compliance": [ + "OWASP Top 10 2013", + "OWASP Top 10 2021", + "PCI DSS v3.2.1", + "ASD STIG 4.10", + "NIST SP 800-53" + ], + "firstScanId": "hidden", + "nodes": [ + { + "column": 102, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.saveCredentials.pwd.getText", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "getText", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + }, + { + "column": 113, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.saveCredentials.toString", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "toString", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + }, + { + "column": 24, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.mDB.execSQL", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "execSQL", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + } + ], + "foundDate": "2022-02-25T21:55:08Z", + "firstFoundDate": "2022-02-25T21:55:06Z" + } + ] + }, + { + "queryName": "Log_Forging", + "queryId": "", + "description": "", + "vulnerabilitiesTotal": 1, + "vulnerabilities": [ + { + "id": "Zxmd7VE19ZSJwJVDs00YugwMhwo=", + "similarityId": 2017000979, + "status": "NEW", + "state": "To verify", + "severity": "LOW", + "groupName": "Java_Low_Visibility", + "cweId": 117, + "confidenceLevel": 0, + "compliance": [ + "OWASP Mobile Top 10 2016", + "OWASP Top 10 2017", + "OWASP Top 10 2021", + "PCI DSS v3.2.1", + "ASD STIG 4.10", + "FISMA 2014", + "NIST SP 800-53" + ], + "firstScanId": "hidden", + "nodes": [ + { + "column": 102, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/LogActivity.java", + "fullName": "jakhar.aseem.diva.LogActivity.checkout.cctxt.getText", + "length": 1, + "line": 56, + "methodLine": 49, + "name": "getText", + "domType": "MethodInvokeExpr", + "method": "checkout" + }, + { + "column": 113, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/LogActivity.java", + "fullName": "jakhar.aseem.diva.LogActivity.checkout.toString", + "length": 1, + "line": 56, + "methodLine": 49, + "name": "toString", + "domType": "MethodInvokeExpr", + "method": "checkout" + }, + { + "column": 18, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/LogActivity.java", + "fullName": "Log.e", + "length": 1, + "line": 56, + "methodLine": 49, + "name": "e", + "domType": "MethodInvokeExpr", + "method": "checkout" + } + ], + "foundDate": "2022-02-25T21:55:08Z", + "firstFoundDate": "2022-02-25T21:55:06Z" + } + ] + }, + { + "queryName": "Heap_Inspection", + "queryId": "", + "description": "", + "vulnerabilitiesTotal": 4, + "vulnerabilities": [ + { + "id": "b7tGSONiaSObAFTnmF18NcRIuA4=", + "similarityId": -1853810714, + "status": "NEW", + "state": "To verify", + "severity": "LOW", + "groupName": "Java_Low_Visibility", + "cweId": 244, + "confidenceLevel": 0, + "compliance": [ + "ASD STIG 4.10", + "OWASP Top 10 2013", + "OWASP Top 10 2021" + ], + "firstScanId": "hidden", + "nodes": [ + { + "column": 18, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage4Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage4Activity.saveCredentials.pwd", + "length": 3, + "line": 55, + "methodLine": 53, + "name": "pwd", + "domType": "Declarator", + "method": "saveCredentials" + } + ], + "foundDate": "2022-02-25T21:55:08Z", + "firstFoundDate": "2022-02-25T21:55:06Z" + }, + { + "id": "b7tGSONiaSObAFTnmF18NcRIuA4=", + "similarityId": 1375153830, + "status": "NEW", + "state": "To verify", + "severity": "LOW", + "groupName": "Java_Low_Visibility", + "cweId": 244, + "confidenceLevel": 0, + "compliance": [ + "ASD STIG 4.10", + "OWASP Top 10 2013", + "OWASP Top 10 2021" + ], + "firstScanId": "hidden", + "nodes": [ + { + "column": 18, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage3Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage3Activity.saveCredentials.pwd", + "length": 3, + "line": 56, + "methodLine": 54, + "name": "pwd", + "domType": "Declarator", + "method": "saveCredentials" + } + ], + "foundDate": "2022-02-25T21:55:08Z", + "firstFoundDate": "2022-02-25T21:55:06Z" + }, + { + "id": "b7tGSONiaSObAFTnmF18NcRIuA4=", + "similarityId": 309151078, + "status": "NEW", + "state": "To verify", + "severity": "LOW", + "groupName": "Java_Low_Visibility", + "cweId": 244, + "confidenceLevel": 0, + "compliance": [ + "ASD STIG 4.10", + "OWASP Top 10 2013", + "OWASP Top 10 2021" + ], + "firstScanId": "hidden", + "nodes": [ + { + "column": 18, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.saveCredentials.pwd", + "length": 3, + "line": 65, + "methodLine": 63, + "name": "pwd", + "domType": "Declarator", + "method": "saveCredentials" + } + ], + "foundDate": "2022-02-25T21:55:08Z", + "firstFoundDate": "2022-02-25T21:55:06Z" + }, + { + "id": "b7tGSONiaSObAFTnmF18NcRIuA4=", + "similarityId": -756851674, + "status": "NEW", + "state": "To verify", + "severity": "LOW", + "groupName": "Java_Low_Visibility", + "cweId": 244, + "confidenceLevel": 0, + "compliance": [ + "ASD STIG 4.10", + "OWASP Top 10 2013", + "OWASP Top 10 2021" + ], + "firstScanId": "hidden", + "nodes": [ + { + "column": 18, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage1Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage1Activity.saveCredentials.pwd", + "length": 3, + "line": 54, + "methodLine": 50, + "name": "pwd", + "domType": "Declarator", + "method": "saveCredentials" + } + ], + "foundDate": "2022-02-25T21:55:08Z", + "firstFoundDate": "2022-02-25T21:55:06Z" + } + ] + } + ] + } + ], + "vulnerabilities": { + "total": 10, + "high": 3, + "medium": 2, + "low": 5, + "info": 0 + } + }, + "sca": null, + "kics": null + } +} diff --git a/unittests/scans/checkmarx/multiple_findings_fabricated_internal_duplicates_subset.json b/unittests/scans/checkmarx/multiple_findings_fabricated_internal_duplicates_subset.json new file mode 100644 index 00000000000..d1e17cd2934 --- /dev/null +++ b/unittests/scans/checkmarx/multiple_findings_fabricated_internal_duplicates_subset.json @@ -0,0 +1,391 @@ +{ + "reportId": "hidden", + "reportHeader": { + "projectName": "DIVA", + "createdDate": "2022-02-25T21:56:10.318Z", + "tenantId": "hidden" + }, + "executiveSummary": { + "branchName": "", + "projectName": "DIVA", + "engines": [ + "SAST" + ], + "riskLevel": "No Risk", + "totalVulnerabilities": 11, + "newVulnerabilities": 10, + "recurrentVulnerabilities": 0, + "vulnerabilitiesPerEngine": { + "SAST": 10 + }, + "resultsTriage": { + "SAST": { + "Confirmed": { + "name": "Confirmed", + "amount": 0, + "percentage": 0 + }, + "Not exploitable": { + "name": "Not exploitable", + "amount": 0, + "percentage": 0 + }, + "To verify": { + "name": "To verify", + "amount": 10, + "percentage": 100 + }, + "Urgent": { + "name": "Urgent", + "amount": 0, + "percentage": 0 + } + } + } + }, + "scanSummary": { + "scanId": "hidden", + "languages": [ + "Java" + ], + "enginesCount": 1, + "scanCompletedDate": "2022-02-25 21:55:16.281169 +0000 UTC", + "engineTypes": [ + "SAST" + ] + }, + "scanResults": { + "sast": { + "languages": [ + { + "languageName": "Java", + "queries": [ + { + "queryName": "SQL_Injection", + "queryId": "", + "description": "", + "vulnerabilitiesTotal": 3, + "vulnerabilities": [ + { + "id": "YmC0We6hAbZWhIrrniEWGot4AHQ=", + "similarityId": -665784454, + "status": "NEW", + "state": "To verify", + "severity": "HIGH", + "groupName": "Java_High_Risk", + "cweId": 89, + "confidenceLevel": 0, + "compliance": [ + "OWASP Top 10 2013", + "OWASP Top 10 API", + "OWASP Mobile Top 10 2016", + "OWASP Top 10 2021", + "FISMA 2014", + "PCI DSS v3.2.1", + "ASD STIG 4.10", + "NIST SP 800-53", + "OWASP Top 10 2017" + ], + "firstScanId": "hidden", + "nodes": [ + { + "column": 88, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/SQLInjectionActivity.java", + "fullName": "jakhar.aseem.diva.SQLInjectionActivity.search.srchtxt.getText", + "length": 1, + "line": 70, + "methodLine": 66, + "name": "getText", + "domType": "MethodInvokeExpr", + "method": "search" + }, + { + "column": 99, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/SQLInjectionActivity.java", + "fullName": "jakhar.aseem.diva.SQLInjectionActivity.search.toString", + "length": 1, + "line": 70, + "methodLine": 66, + "name": "toString", + "domType": "MethodInvokeExpr", + "method": "search" + }, + { + "column": 30, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/SQLInjectionActivity.java", + "fullName": "jakhar.aseem.diva.SQLInjectionActivity.mDB.rawQuery", + "length": 1, + "line": 70, + "methodLine": 66, + "name": "rawQuery", + "domType": "MethodInvokeExpr", + "method": "search" + } + ], + "foundDate": "2022-02-25T21:55:08Z", + "firstFoundDate": "2022-02-25T21:55:07Z" + }, + { + "id": "YmC0We6hAbZWhIrrniEWGot4AHQ=", + "similarityId": 816060275, + "status": "NEW", + "state": "To verify", + "severity": "HIGH", + "groupName": "Java_High_Risk", + "cweId": 89, + "confidenceLevel": 0, + "compliance": [ + "OWASP Top 10 2013", + "OWASP Top 10 API", + "OWASP Mobile Top 10 2016", + "OWASP Top 10 2021", + "FISMA 2014", + "PCI DSS v3.2.1", + "ASD STIG 4.10", + "NIST SP 800-53", + "OWASP Top 10 2017" + ], + "firstScanId": "hidden", + "nodes": [ + { + "column": 68, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.saveCredentials.usr.getText", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "getText", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + }, + { + "column": 79, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.saveCredentials.toString", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "toString", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + }, + { + "column": 24, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.mDB.execSQL", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "execSQL", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + } + ], + "foundDate": "2022-02-25T21:55:08Z", + "firstFoundDate": "2022-02-25T21:55:07Z" + } + ] + }, + { + "queryName": "CSRF", + "queryId": "", + "description": "", + "vulnerabilitiesTotal": 2, + "vulnerabilities": [ + { + "id": "mJHdLIgE2fx10ehNLPytxzPTVCo=", + "similarityId": 1268494559, + "status": "NEW", + "state": "To verify", + "severity": "MEDIUM", + "groupName": "Java_Medium_Threat", + "cweId": 352, + "confidenceLevel": 0, + "compliance": [ + "OWASP Top 10 2013", + "OWASP Top 10 2021", + "PCI DSS v3.2.1", + "ASD STIG 4.10", + "NIST SP 800-53" + ], + "firstScanId": "hidden", + "nodes": [ + { + "column": 68, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.saveCredentials.usr.getText", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "getText", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + }, + { + "column": 79, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.saveCredentials.toString", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "toString", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + }, + { + "column": 24, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.mDB.execSQL", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "execSQL", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + } + ], + "foundDate": "2022-02-25T21:55:08Z", + "firstFoundDate": "2022-02-25T21:55:06Z" + }, + { + "id": "mJHdLIgE2fx10ehNLPytxzPTVCo=", + "similarityId": -443707656, + "status": "NEW", + "state": "To verify", + "severity": "MEDIUM", + "groupName": "Java_Medium_Threat", + "cweId": 352, + "confidenceLevel": 0, + "compliance": [ + "OWASP Top 10 2013", + "OWASP Top 10 2021", + "PCI DSS v3.2.1", + "ASD STIG 4.10", + "NIST SP 800-53" + ], + "firstScanId": "hidden", + "nodes": [ + { + "column": 102, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.saveCredentials.pwd.getText", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "getText", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + }, + { + "column": 113, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.saveCredentials.toString", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "toString", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + }, + { + "column": 24, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.mDB.execSQL", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "execSQL", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + } + ], + "foundDate": "2022-02-25T21:55:08Z", + "firstFoundDate": "2022-02-25T21:55:06Z" + } + ] + }, + { + "queryName": "Heap_Inspection", + "queryId": "", + "description": "", + "vulnerabilitiesTotal": 4, + "vulnerabilities": [ + { + "id": "b7tGSONiaSObAFTnmF18NcRIuA4=", + "similarityId": -1853810714, + "status": "NEW", + "state": "To verify", + "severity": "LOW", + "groupName": "Java_Low_Visibility", + "cweId": 244, + "confidenceLevel": 0, + "compliance": [ + "ASD STIG 4.10", + "OWASP Top 10 2013", + "OWASP Top 10 2021" + ], + "firstScanId": "hidden", + "nodes": [ + { + "column": 18, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage4Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage4Activity.saveCredentials.pwd", + "length": 3, + "line": 55, + "methodLine": 53, + "name": "pwd", + "domType": "Declarator", + "method": "saveCredentials" + } + ], + "foundDate": "2022-02-25T21:55:08Z", + "firstFoundDate": "2022-02-25T21:55:06Z" + }, + { + "id": "b7tGSONiaSObAFTnmF18NcRIuA4=", + "similarityId": 1375153830, + "status": "NEW", + "state": "To verify", + "severity": "LOW", + "groupName": "Java_Low_Visibility", + "cweId": 244, + "confidenceLevel": 0, + "compliance": [ + "ASD STIG 4.10", + "OWASP Top 10 2013", + "OWASP Top 10 2021" + ], + "firstScanId": "hidden", + "nodes": [ + { + "column": 18, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage3Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage3Activity.saveCredentials.pwd", + "length": 3, + "line": 56, + "methodLine": 54, + "name": "pwd", + "domType": "Declarator", + "method": "saveCredentials" + } + ], + "foundDate": "2022-02-25T21:55:08Z", + "firstFoundDate": "2022-02-25T21:55:06Z" + } + ] + } + ] + } + ], + "vulnerabilities": { + "total": 10, + "high": 3, + "medium": 2, + "low": 5, + "info": 0 + } + }, + "sca": null, + "kics": null + } +} diff --git a/unittests/scans/checkmarx/multiple_findings_fabricated_subset.json b/unittests/scans/checkmarx/multiple_findings_fabricated_subset.json new file mode 100644 index 00000000000..e276e5cb93d --- /dev/null +++ b/unittests/scans/checkmarx/multiple_findings_fabricated_subset.json @@ -0,0 +1,370 @@ +{ + "reportId": "hidden", + "reportHeader": { + "projectName": "DIVA", + "createdDate": "2022-02-25T21:56:10.318Z", + "tenantId": "hidden" + }, + "executiveSummary": { + "branchName": "", + "projectName": "DIVA", + "engines": [ + "SAST" + ], + "riskLevel": "No Risk", + "totalVulnerabilities": 11, + "newVulnerabilities": 10, + "recurrentVulnerabilities": 0, + "vulnerabilitiesPerEngine": { + "SAST": 10 + }, + "resultsTriage": { + "SAST": { + "Confirmed": { + "name": "Confirmed", + "amount": 0, + "percentage": 0 + }, + "Not exploitable": { + "name": "Not exploitable", + "amount": 0, + "percentage": 0 + }, + "To verify": { + "name": "To verify", + "amount": 10, + "percentage": 100 + }, + "Urgent": { + "name": "Urgent", + "amount": 0, + "percentage": 0 + } + } + } + }, + "scanSummary": { + "scanId": "hidden", + "languages": [ + "Java" + ], + "enginesCount": 1, + "scanCompletedDate": "2022-02-25 21:55:16.281169 +0000 UTC", + "engineTypes": [ + "SAST" + ] + }, + "scanResults": { + "sast": { + "languages": [ + { + "languageName": "Java", + "queries": [ + { + "queryName": "SQL_Injection", + "queryId": "", + "description": "", + "vulnerabilitiesTotal": 2, + "vulnerabilities": [ + { + "id": "/oiUUpBjigtUpTb1+haL9nypVaQ=", + "similarityId": -665784454, + "status": "NEW", + "state": "To verify", + "severity": "HIGH", + "groupName": "Java_High_Risk", + "cweId": 89, + "confidenceLevel": 0, + "compliance": [ + "OWASP Top 10 2013", + "OWASP Top 10 API", + "OWASP Mobile Top 10 2016", + "OWASP Top 10 2021", + "FISMA 2014", + "PCI DSS v3.2.1", + "ASD STIG 4.10", + "NIST SP 800-53", + "OWASP Top 10 2017" + ], + "firstScanId": "hidden", + "nodes": [ + { + "column": 88, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/SQLInjectionActivity.java", + "fullName": "jakhar.aseem.diva.SQLInjectionActivity.search.srchtxt.getText", + "length": 1, + "line": 70, + "methodLine": 66, + "name": "getText", + "domType": "MethodInvokeExpr", + "method": "search" + }, + { + "column": 99, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/SQLInjectionActivity.java", + "fullName": "jakhar.aseem.diva.SQLInjectionActivity.search.toString", + "length": 1, + "line": 70, + "methodLine": 66, + "name": "toString", + "domType": "MethodInvokeExpr", + "method": "search" + }, + { + "column": 30, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/SQLInjectionActivity.java", + "fullName": "jakhar.aseem.diva.SQLInjectionActivity.mDB.rawQuery", + "length": 1, + "line": 70, + "methodLine": 66, + "name": "rawQuery", + "domType": "MethodInvokeExpr", + "method": "search" + } + ], + "foundDate": "2022-02-25T21:55:08Z", + "firstFoundDate": "2022-02-25T21:55:07Z" + }, + { + "id": "RjtOspmU6+BMRIMlftOJo6ATMuA=", + "similarityId": 816060275, + "status": "NEW", + "state": "To verify", + "severity": "HIGH", + "groupName": "Java_High_Risk", + "cweId": 89, + "confidenceLevel": 0, + "compliance": [ + "OWASP Top 10 2013", + "OWASP Top 10 API", + "OWASP Mobile Top 10 2016", + "OWASP Top 10 2021", + "FISMA 2014", + "PCI DSS v3.2.1", + "ASD STIG 4.10", + "NIST SP 800-53", + "OWASP Top 10 2017" + ], + "firstScanId": "hidden", + "nodes": [ + { + "column": 68, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.saveCredentials.usr.getText", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "getText", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + }, + { + "column": 79, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.saveCredentials.toString", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "toString", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + }, + { + "column": 24, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.mDB.execSQL", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "execSQL", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + } + ], + "foundDate": "2022-02-25T21:55:08Z", + "firstFoundDate": "2022-02-25T21:55:07Z" + } + ] + }, + { + "queryName": "CSRF", + "queryId": "", + "description": "", + "vulnerabilitiesTotal": 1, + "vulnerabilities": [ + { + "id": "mJHdLIgE2fx10ehNLPytxzPTVCo=", + "similarityId": 1268494559, + "status": "NEW", + "state": "To verify", + "severity": "MEDIUM", + "groupName": "Java_Medium_Threat", + "cweId": 352, + "confidenceLevel": 0, + "compliance": [ + "OWASP Top 10 2013", + "OWASP Top 10 2021", + "PCI DSS v3.2.1", + "ASD STIG 4.10", + "NIST SP 800-53" + ], + "firstScanId": "hidden", + "nodes": [ + { + "column": 68, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.saveCredentials.usr.getText", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "getText", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + }, + { + "column": 79, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.saveCredentials.toString", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "toString", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + }, + { + "column": 24, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage2Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage2Activity.mDB.execSQL", + "length": 1, + "line": 67, + "methodLine": 63, + "name": "execSQL", + "domType": "MethodInvokeExpr", + "method": "saveCredentials" + } + ], + "foundDate": "2022-02-25T21:55:08Z", + "firstFoundDate": "2022-02-25T21:55:06Z" + } + ] + }, + { + "queryName": "Log_Forging", + "queryId": "", + "description": "", + "vulnerabilitiesTotal": 1, + "vulnerabilities": [ + { + "id": "Zxmd7VE19ZSJwJVDs00YugwMhwo=", + "similarityId": 2017000979, + "status": "NEW", + "state": "To verify", + "severity": "LOW", + "groupName": "Java_Low_Visibility", + "cweId": 117, + "confidenceLevel": 0, + "compliance": [ + "OWASP Mobile Top 10 2016", + "OWASP Top 10 2017", + "OWASP Top 10 2021", + "PCI DSS v3.2.1", + "ASD STIG 4.10", + "FISMA 2014", + "NIST SP 800-53" + ], + "firstScanId": "hidden", + "nodes": [ + { + "column": 102, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/LogActivity.java", + "fullName": "jakhar.aseem.diva.LogActivity.checkout.cctxt.getText", + "length": 1, + "line": 56, + "methodLine": 49, + "name": "getText", + "domType": "MethodInvokeExpr", + "method": "checkout" + }, + { + "column": 113, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/LogActivity.java", + "fullName": "jakhar.aseem.diva.LogActivity.checkout.toString", + "length": 1, + "line": 56, + "methodLine": 49, + "name": "toString", + "domType": "MethodInvokeExpr", + "method": "checkout" + }, + { + "column": 18, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/LogActivity.java", + "fullName": "Log.e", + "length": 1, + "line": 56, + "methodLine": 49, + "name": "e", + "domType": "MethodInvokeExpr", + "method": "checkout" + } + ], + "foundDate": "2022-02-25T21:55:08Z", + "firstFoundDate": "2022-02-25T21:55:06Z" + } + ] + }, + { + "queryName": "Heap_Inspection", + "queryId": "", + "description": "", + "vulnerabilitiesTotal": 1, + "vulnerabilities": [ + { + "id": "udB1urKobWKTYYlRQbAAub1yRAc=", + "similarityId": -756851674, + "status": "NEW", + "state": "To verify", + "severity": "LOW", + "groupName": "Java_Low_Visibility", + "cweId": 244, + "confidenceLevel": 0, + "compliance": [ + "ASD STIG 4.10", + "OWASP Top 10 2013", + "OWASP Top 10 2021" + ], + "firstScanId": "hidden", + "nodes": [ + { + "column": 18, + "fileName": "/diva-android-master/app/src/main/java/jakhar/aseem/diva/InsecureDataStorage1Activity.java", + "fullName": "jakhar.aseem.diva.InsecureDataStorage1Activity.saveCredentials.pwd", + "length": 3, + "line": 54, + "methodLine": 50, + "name": "pwd", + "domType": "Declarator", + "method": "saveCredentials" + } + ], + "foundDate": "2022-02-25T21:55:08Z", + "firstFoundDate": "2022-02-25T21:55:06Z" + } + ] + } + ] + } + ], + "vulnerabilities": { + "total": 10, + "high": 3, + "medium": 2, + "low": 5, + "info": 0 + } + }, + "sca": null, + "kics": null + } +} diff --git a/unittests/scans/sarif/bash-report-fabricated-no-internal-dupes-subset.sarif b/unittests/scans/sarif/bash-report-fabricated-no-internal-dupes-subset.sarif new file mode 100644 index 00000000000..f49ae665e40 --- /dev/null +++ b/unittests/scans/sarif/bash-report-fabricated-no-internal-dupes-subset.sarif @@ -0,0 +1,405 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "Shell Script Analysis", + "rules": [ + { + "id": "2076", + "help": { + "text": "Don't quote right-hand side of =~, it'll match literally rather than as a regex.", + "markdown": "Don't quote right-hand side of =~, it'll match literally rather than as a regex." + }, + "name": "", + "properties": { + "tags": [ + "Scan" + ], + "precision": "high" + }, + "defaultConfiguration": { + "level": "note" + }, + "fullDescription": { + "text": "Don't quote right-hand side of =~, it'll match literally rather than as a regex." + }, + "helpUri": "https://github.com/koalaman/shellcheck/wiki/SC2076", + "shortDescription": { + "text": "Don't quote right-hand side of =~, it'll match literally rather than as a regex." + } + }, + { + "id": "2071", + "help": { + "text": "> is for string comparisons. Use -gt instead.", + "markdown": "> is for string comparisons. Use -gt instead." + }, + "name": "", + "properties": { + "tags": [ + "Scan" + ], + "precision": "high" + }, + "defaultConfiguration": { + "level": "note" + }, + "fullDescription": { + "text": "> is for string comparisons. Use -gt instead." + }, + "helpUri": "https://github.com/koalaman/shellcheck/wiki/SC2071", + "shortDescription": { + "text": "> is for string comparisons" + } + }, + { + "id": "2072", + "help": { + "text": "Decimals are not supported. Either use integers only, or use bc or awk to compare.", + "markdown": "Decimals are not supported. Either use integers only, or use bc or awk to compare." + }, + "name": "", + "properties": { + "tags": [ + "Scan" + ], + "precision": "high" + }, + "defaultConfiguration": { + "level": "note" + }, + "fullDescription": { + "text": "Decimals are not supported. Either use integers only, or use bc or awk to compare." + }, + "helpUri": "https://github.com/koalaman/shellcheck/wiki/SC2072", + "shortDescription": { + "text": "Decimals are not supported" + } + }, + { + "id": "2077", + "help": { + "text": "You need spaces around the comparison operator.", + "markdown": "You need spaces around the comparison operator." + }, + "name": "", + "properties": { + "tags": [ + "Scan" + ], + "precision": "high" + }, + "defaultConfiguration": { + "level": "note" + }, + "fullDescription": { + "text": "You need spaces around the comparison operator." + }, + "helpUri": "https://github.com/koalaman/shellcheck/wiki/SC2077", + "shortDescription": { + "text": "You need spaces around the comparison operator." + } + }, + { + "id": "1035", + "help": { + "text": "You are missing a required space here.", + "markdown": "You are missing a required space here." + }, + "name": "", + "properties": { + "tags": [ + "Scan" + ], + "precision": "high" + }, + "defaultConfiguration": { + "level": "note" + }, + "fullDescription": { + "text": "You are missing a required space here." + }, + "helpUri": "https://github.com/koalaman/shellcheck/wiki/SC1035", + "shortDescription": { + "text": "You are missing a required space here." + } + } + ], + "version": "1.0.0-scan", + "fullName": "Shell Script Analysis" + } + }, + "conversion": { + "tool": { + "driver": { + "name": "@ShiftLeft/sast-scan" + } + }, + "invocation": { + "arguments": [ + "-a", + "--shell=bash", + "-f", + "json", + "-S", + "error", + "--color=never", + "/app/legacy-setup.bash", + "/app/test.sh", + "/app/upgrade.bash", + "/app/entrypoint_scripts/os/ubuntu.sh", + "/app/entrypoint_scripts/os/linux.sh", + "/app/entrypoint_scripts/common/config-vars.sh", + "/app/entrypoint_scripts/common/install-dojo.sh", + "/app/entrypoint_scripts/common/common-os.sh", + "/app/entrypoint_scripts/common/dojo-shared-resources.sh", + "/app/entrypoint_scripts/common/cmd-args.sh", + "/app/entrypoint_scripts/common/prompt.sh", + "/app/entrypoint_scripts/run/startup-docker.bash", + "/app/entrypoint_scripts/run/run-local-dojo.bash", + "/app/setup/setup.bash", + "/app/setup/upgrade.bash", + "/app/setup/scripts/os/ubuntu.sh", + "/app/setup/scripts/os/linux.sh", + "/app/setup/scripts/common/config-vars.sh", + "/app/setup/scripts/common/install-dojo.sh", + "/app/setup/scripts/common/common-os.sh", + "/app/setup/scripts/common/dojo-shared-resources.sh", + "/app/setup/scripts/common/cmd-args.sh", + "/app/setup/scripts/common/prompt.sh", + "/app/setup/scripts/run/startup-docker.bash", + "/app/setup/scripts/run/run-local-dojo.bash", + "/app/docker/entrypoint-uwsgi-dev.sh", + "/app/docker/entrypoint.sh", + "/app/docker/entrypoint-uwsgi.sh", + "/app/docker/entrypoint-uwsgi-ptvsd.sh", + "/app/docker/wait-for-it.sh", + "/app/docker/entrypoint-celery.sh", + "/app/docker/entrypoint-unit-tests.sh", + "/app/docker/entrypoint-nginx.sh", + "/app/docker/dojo-data.bash", + "/app/docker/entrypoint-unit-tests-devDocker.sh", + "/app/docker/setEnv.sh", + "/app/docker/entrypoint-celery-worker.sh", + "/app/docker/entrypoint-initializer.sh", + "/app/docker/entrypoint-celery-beat.sh", + "/app/docker/entrypoint-integration-tests.sh", + "/app/docker/unit-tests.sh" + ], + "executionSuccessful": true, + "commandLine": "-a --shell=bash -f json -S error --color=never /app/legacy-setup.bash /app/test.sh /app/upgrade.bash /app/entrypoint_scripts/os/ubuntu.sh /app/entrypoint_scripts/os/linux.sh /app/entrypoint_scripts/common/config-vars.sh /app/entrypoint_scripts/common/install-dojo.sh /app/entrypoint_scripts/common/common-os.sh /app/entrypoint_scripts/common/dojo-shared-resources.sh /app/entrypoint_scripts/common/cmd-args.sh /app/entrypoint_scripts/common/prompt.sh /app/entrypoint_scripts/run/startup-docker.bash /app/entrypoint_scripts/run/run-local-dojo.bash /app/setup/setup.bash /app/setup/upgrade.bash /app/setup/scripts/os/ubuntu.sh /app/setup/scripts/os/linux.sh /app/setup/scripts/common/config-vars.sh /app/setup/scripts/common/install-dojo.sh /app/setup/scripts/common/common-os.sh /app/setup/scripts/common/dojo-shared-resources.sh /app/setup/scripts/common/cmd-args.sh /app/setup/scripts/common/prompt.sh /app/setup/scripts/run/startup-docker.bash /app/setup/scripts/run/run-local-dojo.bash /app/docker/entrypoint-uwsgi-dev.sh /app/docker/entrypoint.sh /app/docker/entrypoint-uwsgi.sh /app/docker/entrypoint-uwsgi-ptvsd.sh /app/docker/wait-for-it.sh /app/docker/entrypoint-celery.sh /app/docker/entrypoint-unit-tests.sh /app/docker/entrypoint-nginx.sh /app/docker/dojo-data.bash /app/docker/entrypoint-unit-tests-devDocker.sh /app/docker/setEnv.sh /app/docker/entrypoint-celery-worker.sh /app/docker/entrypoint-initializer.sh /app/docker/entrypoint-celery-beat.sh /app/docker/entrypoint-integration-tests.sh /app/docker/unit-tests.sh", + "endTimeUtc": "2021-03-08T15:39:40Z", + "workingDirectory": { + "uri": "file:///home/damien/dd" + } + } + }, + "invocations": [ + { + "executionSuccessful": true, + "endTimeUtc": "2021-03-08T15:39:40Z", + "workingDirectory": { + "uri": "file:///home/damien/dd" + } + } + ], + "properties": { + "metrics": { + "total": 27, + "critical": 0, + "high": 0, + "medium": 0, + "low": 27 + } + }, + "results": [ + { + "message": { + "markdown": "", + "text": "Don't quote right-hand side of =~, it'll match literally rather than as a regex." + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": "if [ ${#} -eq 1 ] && [[ 'dev unit_tests unit_tests_cicd integration_tests release ptvsd' =~ \"${1}\" ]]\n" + }, + "startLine": 134 + }, + "artifactLocation": { + "uri": "file:///home/damien/dd/docker/setEnv.sh" + }, + "contextRegion": { + "snippet": { + "text": "\nif [ ${#} -eq 1 ] && [[ 'dev unit_tests unit_tests_cicd integration_tests release ptvsd' =~ \"${1}\" ]]\n" + }, + "endLine": 134, + "startLine": 133 + } + } + } + ], + "properties": { + "issue_confidence": "MEDIUM", + "issue_severity": "LOW", + "issue_tags": {} + }, + "baselineState": "new", + "partialFingerprints": { + "scanPrimaryLocationHash": "47f57f9669991906", + "scanFileHash": "f196dfe70ba8d8dace" + }, + "ruleId": "2076", + "ruleIndex": 0 + }, + { + "message": { + "markdown": "", + "text": "> is for string comparisons. Use -gt instead." + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": "if [ $# > 1 ]\n" + }, + "startLine": 4 + }, + "artifactLocation": { + "uri": "file:///home/damien/dd/docker/dojo-data.bash" + }, + "contextRegion": { + "snippet": { + "text": "\nif [ $# > 1 ]\n" + }, + "endLine": 4, + "startLine": 3 + } + } + } + ], + "properties": { + "issue_confidence": "MEDIUM", + "issue_severity": "LOW", + "issue_tags": {} + }, + "baselineState": "new", + "partialFingerprints": { + "scanPrimaryLocationHash": "6960f23d58e5c029", + "scanFileHash": "4ff774ffasdb6997d0eef" + }, + "ruleId": "2071", + "ruleIndex": 1 + }, + { + "message": { + "markdown": "", + "text": "Decimals are not supported. Either use integers only, or use bc or awk to compare." + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " if [[ \"$PYV\"<\"2.7\" ]]; then\n" + }, + "startLine": 143 + }, + "artifactLocation": { + "uri": "file:///home/damien/dd/entrypoint_scripts/common/dojo-shared-resources.sh" + }, + "contextRegion": { + "snippet": { + "text": " PYV=`python -c \"import sys;t='{v[0]}.{v[1]}'.format(v=list(sys.version_info[:2]));sys.stdout.write(t)\";`\n if [[ \"$PYV\"<\"2.7\" ]]; then\n" + }, + "endLine": 143, + "startLine": 142 + } + } + } + ], + "properties": { + "issue_confidence": "MEDIUM", + "issue_severity": "LOW", + "issue_tags": {} + }, + "baselineState": "new", + "partialFingerprints": { + "scanPrimaryLocationHash": "4d655189c485c086", + "scanFileHash": "4ee28649c6fasdf5c392d" + }, + "ruleId": "2072", + "ruleIndex": 2 + }, + { + "message": { + "markdown": "", + "text": "You need spaces around the comparison operator." + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " if [ $DBTYPE==\"mysql\" ]; then\n" + }, + "startLine": 410 + }, + "artifactLocation": { + "uri": "file:///home/damien/dd/entrypoint_scripts/common/dojo-shared-resources.sh" + }, + "contextRegion": { + "snippet": { + "text": " read DBTYPE DBNAME SQLUSER SQLPWD SQLHOST SQLPORT<<<\"$PARSE_DB_URL\"\n if [ $DBTYPE==\"mysql\" ]; then\n" + }, + "endLine": 410, + "startLine": 409 + } + } + } + ], + "properties": { + "issue_confidence": "MEDIUM", + "issue_severity": "LOW", + "issue_tags": {} + }, + "baselineState": "new", + "partialFingerprints": { + "scanPrimaryLocationHash": "e65e9fa644d89079", + "scanFileHash": "4ee28asdf649c65c392d" + }, + "ruleId": "2077", + "ruleIndex": 3 + } + ], + "automationDetails": { + "description": { + "text": "Static Analysis Security Test results using @ShiftLeft/sast-scan" + }, + "guid": "70d0f865-f0e4-406c-8837-40852afccaeb" + }, + "versionControlProvenance": [ + { + "branch": "dev", + "repositoryUri": "https://github.com/damiencarol/django-DefectDojo", + "revisionId": "288c68d1ba1f35ebeff1d1bdb032186a23f0ea5b" + } + ] + } + ], + "version": "2.1.0", + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "inlineExternalProperties": [ + { + "guid": "70d0f865-f0e4-406c-8837-40852afccaeb", + "runGuid": "fbb1392e-e657-4572-ac07-0e107d1ff3f1" + } + ] +} \ No newline at end of file diff --git a/unittests/scans/sarif/bash-report-fabricated-no-internal-dupes.sarif b/unittests/scans/sarif/bash-report-fabricated-no-internal-dupes.sarif new file mode 100644 index 00000000000..ef147e03b96 --- /dev/null +++ b/unittests/scans/sarif/bash-report-fabricated-no-internal-dupes.sarif @@ -0,0 +1,446 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "Shell Script Analysis", + "rules": [ + { + "id": "2076", + "help": { + "text": "Don't quote right-hand side of =~, it'll match literally rather than as a regex.", + "markdown": "Don't quote right-hand side of =~, it'll match literally rather than as a regex." + }, + "name": "", + "properties": { + "tags": [ + "Scan" + ], + "precision": "high" + }, + "defaultConfiguration": { + "level": "note" + }, + "fullDescription": { + "text": "Don't quote right-hand side of =~, it'll match literally rather than as a regex." + }, + "helpUri": "https://github.com/koalaman/shellcheck/wiki/SC2076", + "shortDescription": { + "text": "Don't quote right-hand side of =~, it'll match literally rather than as a regex." + } + }, + { + "id": "2071", + "help": { + "text": "> is for string comparisons. Use -gt instead.", + "markdown": "> is for string comparisons. Use -gt instead." + }, + "name": "", + "properties": { + "tags": [ + "Scan" + ], + "precision": "high" + }, + "defaultConfiguration": { + "level": "note" + }, + "fullDescription": { + "text": "> is for string comparisons. Use -gt instead." + }, + "helpUri": "https://github.com/koalaman/shellcheck/wiki/SC2071", + "shortDescription": { + "text": "> is for string comparisons" + } + }, + { + "id": "2072", + "help": { + "text": "Decimals are not supported. Either use integers only, or use bc or awk to compare.", + "markdown": "Decimals are not supported. Either use integers only, or use bc or awk to compare." + }, + "name": "", + "properties": { + "tags": [ + "Scan" + ], + "precision": "high" + }, + "defaultConfiguration": { + "level": "note" + }, + "fullDescription": { + "text": "Decimals are not supported. Either use integers only, or use bc or awk to compare." + }, + "helpUri": "https://github.com/koalaman/shellcheck/wiki/SC2072", + "shortDescription": { + "text": "Decimals are not supported" + } + }, + { + "id": "2077", + "help": { + "text": "You need spaces around the comparison operator.", + "markdown": "You need spaces around the comparison operator." + }, + "name": "", + "properties": { + "tags": [ + "Scan" + ], + "precision": "high" + }, + "defaultConfiguration": { + "level": "note" + }, + "fullDescription": { + "text": "You need spaces around the comparison operator." + }, + "helpUri": "https://github.com/koalaman/shellcheck/wiki/SC2077", + "shortDescription": { + "text": "You need spaces around the comparison operator." + } + }, + { + "id": "1035", + "help": { + "text": "You are missing a required space here.", + "markdown": "You are missing a required space here." + }, + "name": "", + "properties": { + "tags": [ + "Scan" + ], + "precision": "high" + }, + "defaultConfiguration": { + "level": "note" + }, + "fullDescription": { + "text": "You are missing a required space here." + }, + "helpUri": "https://github.com/koalaman/shellcheck/wiki/SC1035", + "shortDescription": { + "text": "You are missing a required space here." + } + } + ], + "version": "1.0.0-scan", + "fullName": "Shell Script Analysis" + } + }, + "conversion": { + "tool": { + "driver": { + "name": "@ShiftLeft/sast-scan" + } + }, + "invocation": { + "arguments": [ + "-a", + "--shell=bash", + "-f", + "json", + "-S", + "error", + "--color=never", + "/app/legacy-setup.bash", + "/app/test.sh", + "/app/upgrade.bash", + "/app/entrypoint_scripts/os/ubuntu.sh", + "/app/entrypoint_scripts/os/linux.sh", + "/app/entrypoint_scripts/common/config-vars.sh", + "/app/entrypoint_scripts/common/install-dojo.sh", + "/app/entrypoint_scripts/common/common-os.sh", + "/app/entrypoint_scripts/common/dojo-shared-resources.sh", + "/app/entrypoint_scripts/common/cmd-args.sh", + "/app/entrypoint_scripts/common/prompt.sh", + "/app/entrypoint_scripts/run/startup-docker.bash", + "/app/entrypoint_scripts/run/run-local-dojo.bash", + "/app/setup/setup.bash", + "/app/setup/upgrade.bash", + "/app/setup/scripts/os/ubuntu.sh", + "/app/setup/scripts/os/linux.sh", + "/app/setup/scripts/common/config-vars.sh", + "/app/setup/scripts/common/install-dojo.sh", + "/app/setup/scripts/common/common-os.sh", + "/app/setup/scripts/common/dojo-shared-resources.sh", + "/app/setup/scripts/common/cmd-args.sh", + "/app/setup/scripts/common/prompt.sh", + "/app/setup/scripts/run/startup-docker.bash", + "/app/setup/scripts/run/run-local-dojo.bash", + "/app/docker/entrypoint-uwsgi-dev.sh", + "/app/docker/entrypoint.sh", + "/app/docker/entrypoint-uwsgi.sh", + "/app/docker/entrypoint-uwsgi-ptvsd.sh", + "/app/docker/wait-for-it.sh", + "/app/docker/entrypoint-celery.sh", + "/app/docker/entrypoint-unit-tests.sh", + "/app/docker/entrypoint-nginx.sh", + "/app/docker/dojo-data.bash", + "/app/docker/entrypoint-unit-tests-devDocker.sh", + "/app/docker/setEnv.sh", + "/app/docker/entrypoint-celery-worker.sh", + "/app/docker/entrypoint-initializer.sh", + "/app/docker/entrypoint-celery-beat.sh", + "/app/docker/entrypoint-integration-tests.sh", + "/app/docker/unit-tests.sh" + ], + "executionSuccessful": true, + "commandLine": "-a --shell=bash -f json -S error --color=never /app/legacy-setup.bash /app/test.sh /app/upgrade.bash /app/entrypoint_scripts/os/ubuntu.sh /app/entrypoint_scripts/os/linux.sh /app/entrypoint_scripts/common/config-vars.sh /app/entrypoint_scripts/common/install-dojo.sh /app/entrypoint_scripts/common/common-os.sh /app/entrypoint_scripts/common/dojo-shared-resources.sh /app/entrypoint_scripts/common/cmd-args.sh /app/entrypoint_scripts/common/prompt.sh /app/entrypoint_scripts/run/startup-docker.bash /app/entrypoint_scripts/run/run-local-dojo.bash /app/setup/setup.bash /app/setup/upgrade.bash /app/setup/scripts/os/ubuntu.sh /app/setup/scripts/os/linux.sh /app/setup/scripts/common/config-vars.sh /app/setup/scripts/common/install-dojo.sh /app/setup/scripts/common/common-os.sh /app/setup/scripts/common/dojo-shared-resources.sh /app/setup/scripts/common/cmd-args.sh /app/setup/scripts/common/prompt.sh /app/setup/scripts/run/startup-docker.bash /app/setup/scripts/run/run-local-dojo.bash /app/docker/entrypoint-uwsgi-dev.sh /app/docker/entrypoint.sh /app/docker/entrypoint-uwsgi.sh /app/docker/entrypoint-uwsgi-ptvsd.sh /app/docker/wait-for-it.sh /app/docker/entrypoint-celery.sh /app/docker/entrypoint-unit-tests.sh /app/docker/entrypoint-nginx.sh /app/docker/dojo-data.bash /app/docker/entrypoint-unit-tests-devDocker.sh /app/docker/setEnv.sh /app/docker/entrypoint-celery-worker.sh /app/docker/entrypoint-initializer.sh /app/docker/entrypoint-celery-beat.sh /app/docker/entrypoint-integration-tests.sh /app/docker/unit-tests.sh", + "endTimeUtc": "2021-03-08T15:39:40Z", + "workingDirectory": { + "uri": "file:///home/damien/dd" + } + } + }, + "invocations": [ + { + "executionSuccessful": true, + "endTimeUtc": "2021-03-08T15:39:40Z", + "workingDirectory": { + "uri": "file:///home/damien/dd" + } + } + ], + "properties": { + "metrics": { + "total": 27, + "critical": 0, + "high": 0, + "medium": 0, + "low": 27 + } + }, + "results": [ + { + "message": { + "markdown": "", + "text": "Don't quote right-hand side of =~, it'll match literally rather than as a regex." + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": "if [ ${#} -eq 1 ] && [[ 'dev unit_tests unit_tests_cicd integration_tests release ptvsd' =~ \"${1}\" ]]\n" + }, + "startLine": 134 + }, + "artifactLocation": { + "uri": "file:///home/damien/dd/docker/setEnv.sh" + }, + "contextRegion": { + "snippet": { + "text": "\nif [ ${#} -eq 1 ] && [[ 'dev unit_tests unit_tests_cicd integration_tests release ptvsd' =~ \"${1}\" ]]\n" + }, + "endLine": 134, + "startLine": 133 + } + } + } + ], + "properties": { + "issue_confidence": "MEDIUM", + "issue_severity": "LOW", + "issue_tags": {} + }, + "baselineState": "new", + "partialFingerprints": { + "scanPrimaryLocationHash": "47f57f9669991906", + "scanFileHash": "f196dfe70ba8d8dace" + }, + "ruleId": "2076", + "ruleIndex": 0 + }, + { + "message": { + "markdown": "", + "text": "> is for string comparisons. Use -gt instead." + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": "if [ $# > 1 ]\n" + }, + "startLine": 4 + }, + "artifactLocation": { + "uri": "file:///home/damien/dd/docker/dojo-data.bash" + }, + "contextRegion": { + "snippet": { + "text": "\nif [ $# > 1 ]\n" + }, + "endLine": 4, + "startLine": 3 + } + } + } + ], + "properties": { + "issue_confidence": "MEDIUM", + "issue_severity": "LOW", + "issue_tags": {} + }, + "baselineState": "new", + "partialFingerprints": { + "scanPrimaryLocationHash": "6960f23d58e5c029", + "scanFileHash": "4ff774ffasdb6997d0eef" + }, + "ruleId": "2071", + "ruleIndex": 1 + }, + { + "message": { + "markdown": "", + "text": "Decimals are not supported. Either use integers only, or use bc or awk to compare." + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " if [[ \"$PYV\"<\"2.7\" ]]; then\n" + }, + "startLine": 143 + }, + "artifactLocation": { + "uri": "file:///home/damien/dd/entrypoint_scripts/common/dojo-shared-resources.sh" + }, + "contextRegion": { + "snippet": { + "text": " PYV=`python -c \"import sys;t='{v[0]}.{v[1]}'.format(v=list(sys.version_info[:2]));sys.stdout.write(t)\";`\n if [[ \"$PYV\"<\"2.7\" ]]; then\n" + }, + "endLine": 143, + "startLine": 142 + } + } + } + ], + "properties": { + "issue_confidence": "MEDIUM", + "issue_severity": "LOW", + "issue_tags": {} + }, + "baselineState": "new", + "partialFingerprints": { + "scanPrimaryLocationHash": "4d655189c485c086", + "scanFileHash": "4ee28649c6fasdf5c392d" + }, + "ruleId": "2072", + "ruleIndex": 2 + }, + { + "message": { + "markdown": "", + "text": "You need spaces around the comparison operator." + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " if [ $DBTYPE==\"mysql\" ]; then\n" + }, + "startLine": 410 + }, + "artifactLocation": { + "uri": "file:///home/damien/dd/entrypoint_scripts/common/dojo-shared-resources.sh" + }, + "contextRegion": { + "snippet": { + "text": " read DBTYPE DBNAME SQLUSER SQLPWD SQLHOST SQLPORT<<<\"$PARSE_DB_URL\"\n if [ $DBTYPE==\"mysql\" ]; then\n" + }, + "endLine": 410, + "startLine": 409 + } + } + } + ], + "properties": { + "issue_confidence": "MEDIUM", + "issue_severity": "LOW", + "issue_tags": {} + }, + "baselineState": "new", + "partialFingerprints": { + "scanPrimaryLocationHash": "e65e9fa644d89079", + "scanFileHash": "4ee28asdf649c65c392d" + }, + "ruleId": "2077", + "ruleIndex": 3 + }, + { + "message": { + "markdown": "", + "text": "You are missing a required space here." + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " if [[ \"$PYV\"<\"2.7\" ]]; then\n" + }, + "startLine": 142 + }, + "artifactLocation": { + "uri": "file:///home/damien/dd/setup/scripts/common/dojo-shared-resources.sh" + }, + "contextRegion": { + "snippet": { + "text": " PYV=`python -c \"import sys;t='{v[0]}.{v[1]}'.format(v=list(sys.version_info[:2]));sys.stdout.write(t)\";`\n if [[ \"$PYV\"<\"2.7\" ]]; then\n" + }, + "endLine": 142, + "startLine": 141 + } + } + } + ], + "properties": { + "issue_confidence": "MEDIUM", + "issue_severity": "LOW", + "issue_tags": {} + }, + "baselineState": "new", + "partialFingerprints": { + "scanPrimaryLocationHash": "4d655189c485c086", + "scanFileHash": "5b05533asdf780915bfc" + }, + "ruleId": "1035", + "ruleIndex": 4 + } + ], + "automationDetails": { + "description": { + "text": "Static Analysis Security Test results using @ShiftLeft/sast-scan" + }, + "guid": "70d0f865-f0e4-406c-8837-40852afccaeb" + }, + "versionControlProvenance": [ + { + "branch": "dev", + "repositoryUri": "https://github.com/damiencarol/django-DefectDojo", + "revisionId": "288c68d1ba1f35ebeff1d1bdb032186a23f0ea5b" + } + ] + } + ], + "version": "2.1.0", + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "inlineExternalProperties": [ + { + "guid": "70d0f865-f0e4-406c-8837-40852afccaeb", + "runGuid": "fbb1392e-e657-4572-ac07-0e107d1ff3f1" + } + ] +} \ No newline at end of file diff --git a/unittests/scans/trivy/kubernetes_fabricated_internal_duplicates.json b/unittests/scans/trivy/kubernetes_fabricated_internal_duplicates.json new file mode 100644 index 00000000000..672365fa898 --- /dev/null +++ b/unittests/scans/trivy/kubernetes_fabricated_internal_duplicates.json @@ -0,0 +1,1141 @@ +{ + "ClusterName": "arn:aws:eks:us-east-1:576036489467:cluster/monitoring-test-cluster", + "Vulnerabilities": [ + { + "Namespace": "default", + "Kind": "Deployment", + "Name": "redis-follower", + "Results": [ + { + "Target": "docker.io/redis:6.0.5 (debian 10.4)", + "Class": "os-pkgs", + "Type": "debian", + "Vulnerabilities": [ + { + "VulnerabilityID": "CVE-2020-27350", + "VendorIDs": [ + "DSA-4808-1" + ], + "PkgName": "apt", + "InstalledVersion": "1.8.2.1", + "FixedVersion": "1.8.2.2", + "Layer": { + "Digest": "sha256:8559a31e96f442f2c7b6da49d6c84705f98a39d8be10b3f5f14821d0ee8417df", + "DiffID": "sha256:13cb14c2acd34e45446a50af25cb05095a17624678dbafbcc9e26086547c1d74" + }, + "SeveritySource": "nvd", + "PrimaryURL": "https://avd.aquasec.com/nvd/cve-2020-27350", + "DataSource": { + "ID": "debian", + "Name": "Debian Security Tracker", + "URL": "https://salsa.debian.org/security-tracker-team/security-tracker" + }, + "Title": "apt: integer overflows and underflows while parsing .deb packages", + "Description": "APT had several integer overflows and underflows while parsing .deb packages, aka GHSL-2020-168 GHSL-2020-169, in files apt-pkg/contrib/extracttar.cc, apt-pkg/deb/debfile.cc, and apt-pkg/contrib/arfile.cc. This issue affects: apt 1.2.32ubuntu0 versions prior to 1.2.32ubuntu0.2; 1.6.12ubuntu0 versions prior to 1.6.12ubuntu0.2; 2.0.2ubuntu0 versions prior to 2.0.2ubuntu0.2; 2.1.10ubuntu0 versions prior to 2.1.10ubuntu0.1;", + "Severity": "MEDIUM", + "CweIDs": [ + "CWE-190" + ], + "CVSS": { + "nvd": { + "V2Vector": "AV:L/AC:L/Au:N/C:P/I:P/A:P", + "V3Vector": "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:L", + "V2Score": 4.6, + "V3Score": 5.7 + }, + "redhat": { + "V3Vector": "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:L", + "V3Score": 5.7 + } + }, + "References": [ + "https://access.redhat.com/security/cve/CVE-2020-27350", + "https://bugs.launchpad.net/bugs/1899193", + "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-27350", + "https://security.netapp.com/advisory/ntap-20210108-0005/", + "https://ubuntu.com/security/notices/USN-4667-1", + "https://ubuntu.com/security/notices/USN-4667-2", + "https://usn.ubuntu.com/usn/usn-4667-1", + "https://www.debian.org/security/2020/dsa-4808" + ], + "PublishedDate": "2020-12-10T04:15:00Z", + "LastModifiedDate": "2021-01-08T12:15:00Z" + }, + { + "VulnerabilityID": "CVE-2011-3374", + "PkgName": "apt", + "InstalledVersion": "1.8.2.1", + "Layer": { + "Digest": "sha256:8559a31e96f442f2c7b6da49d6c84705f98a39d8be10b3f5f14821d0ee8417df", + "DiffID": "sha256:13cb14c2acd34e45446a50af25cb05095a17624678dbafbcc9e26086547c1d74" + }, + "SeveritySource": "debian", + "PrimaryURL": "https://avd.aquasec.com/nvd/cve-2011-3374", + "DataSource": { + "ID": "debian", + "Name": "Debian Security Tracker", + "URL": "https://salsa.debian.org/security-tracker-team/security-tracker" + }, + "Title": "It was found that apt-key in apt, all versions, do not correctly valid ...", + "Description": "It was found that apt-key in apt, all versions, do not correctly validate gpg keys with the master keyring, leading to a potential man-in-the-middle attack.", + "Severity": "LOW", + "CweIDs": [ + "CWE-347" + ], + "CVSS": { + "nvd": { + "V2Vector": "AV:N/AC:M/Au:N/C:N/I:P/A:N", + "V3Vector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N", + "V2Score": 4.3, + "V3Score": 3.7 + } + }, + "References": [ + "https://access.redhat.com/security/cve/cve-2011-3374", + "https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=642480", + "https://people.canonical.com/~ubuntu-security/cve/2011/CVE-2011-3374.html", + "https://seclists.org/fulldisclosure/2011/Sep/221", + "https://security-tracker.debian.org/tracker/CVE-2011-3374", + "https://snyk.io/vuln/SNYK-LINUX-APT-116518", + "https://ubuntu.com/security/CVE-2011-3374" + ], + "PublishedDate": "2019-11-26T00:15:00Z", + "LastModifiedDate": "2021-02-09T16:08:00Z" + }, + { + "VulnerabilityID": "CVE-2019-18276", + "PkgName": "bash", + "InstalledVersion": "5.0-4", + "Layer": { + "Digest": "sha256:8559a31e96f442f2c7b6da49d6c84705f98a39d8be10b3f5f14821d0ee8417df", + "DiffID": "sha256:13cb14c2acd34e45446a50af25cb05095a17624678dbafbcc9e26086547c1d74" + }, + "SeveritySource": "debian", + "PrimaryURL": "https://avd.aquasec.com/nvd/cve-2019-18276", + "DataSource": { + "ID": "debian", + "Name": "Debian Security Tracker", + "URL": "https://salsa.debian.org/security-tracker-team/security-tracker" + }, + "Title": "bash: when effective UID is not equal to its real UID the saved UID is not dropped", + "Description": "An issue was discovered in disable_priv_mode in shell.c in GNU Bash through 5.0 patch 11. By default, if Bash is run with its effective UID not equal to its real UID, it will drop privileges by setting its effective UID to its real UID. However, it does so incorrectly. On Linux and other systems that support \"saved UID\" functionality, the saved UID is not dropped. An attacker with command execution in the shell can use \"enable -f\" for runtime loading of a new builtin, which can be a shared object that calls setuid() and therefore regains privileges. However, binaries running with an effective UID of 0 are unaffected.", + "Severity": "LOW", + "CweIDs": [ + "CWE-273" + ], + "CVSS": { + "nvd": { + "V2Vector": "AV:L/AC:L/Au:N/C:C/I:C/A:C", + "V3Vector": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", + "V2Score": 7.2, + "V3Score": 7.8 + }, + "redhat": { + "V3Vector": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", + "V3Score": 7.8 + } + }, + "References": [ + "http://packetstormsecurity.com/files/155498/Bash-5.0-Patch-11-Privilege-Escalation.html", + "https://access.redhat.com/security/cve/CVE-2019-18276", + "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-18276", + "https://github.com/bminor/bash/commit/951bdaad7a18cc0dc1036bba86b18b90874d39ff", + "https://linux.oracle.com/cve/CVE-2019-18276.html", + "https://linux.oracle.com/errata/ELSA-2021-1679.html", + "https://lists.apache.org/thread.html/rf9fa47ab66495c78bb4120b0754dd9531ca2ff0430f6685ac9b07772@%3Cdev.mina.apache.org%3E", + "https://nvd.nist.gov/vuln/detail/CVE-2019-18276", + "https://security.gentoo.org/glsa/202105-34", + "https://security.netapp.com/advisory/ntap-20200430-0003/", + "https://ubuntu.com/security/notices/USN-5380-1", + "https://www.oracle.com/security-alerts/cpuapr2022.html", + "https://www.youtube.com/watch?v=-wGtxJ8opa8" + ], + "PublishedDate": "2019-11-28T01:15:00Z", + "LastModifiedDate": "2022-06-07T18:41:00Z" + } + ] + } + ] + }, + { + "Namespace": "default", + "Kind": "Deployment", + "Name": "redis-follower", + "Results": [ + { + "Target": "docker.io/redis:6.0.5 (debian 10.4)", + "Class": "os-pkgs", + "Type": "debian", + "Vulnerabilities": [ + { + "VulnerabilityID": "CVE-2020-27350", + "VendorIDs": [ + "DSA-4808-1" + ], + "PkgName": "apt", + "InstalledVersion": "1.8.2.1", + "FixedVersion": "1.8.2.2", + "Layer": { + "Digest": "sha256:8559a31e96f442f2c7b6da49d6c84705f98a39d8be10b3f5f14821d0ee8417df", + "DiffID": "sha256:13cb14c2acd34e45446a50af25cb05095a17624678dbafbcc9e26086547c1d74" + }, + "SeveritySource": "nvd", + "PrimaryURL": "https://avd.aquasec.com/nvd/cve-2020-27350", + "DataSource": { + "ID": "debian", + "Name": "Debian Security Tracker", + "URL": "https://salsa.debian.org/security-tracker-team/security-tracker" + }, + "Title": "apt: integer overflows and underflows while parsing .deb packages", + "Description": "APT had several integer overflows and underflows while parsing .deb packages, aka GHSL-2020-168 GHSL-2020-169, in files apt-pkg/contrib/extracttar.cc, apt-pkg/deb/debfile.cc, and apt-pkg/contrib/arfile.cc. This issue affects: apt 1.2.32ubuntu0 versions prior to 1.2.32ubuntu0.2; 1.6.12ubuntu0 versions prior to 1.6.12ubuntu0.2; 2.0.2ubuntu0 versions prior to 2.0.2ubuntu0.2; 2.1.10ubuntu0 versions prior to 2.1.10ubuntu0.1;", + "Severity": "MEDIUM", + "CweIDs": [ + "CWE-190" + ], + "CVSS": { + "nvd": { + "V2Vector": "AV:L/AC:L/Au:N/C:P/I:P/A:P", + "V3Vector": "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:L", + "V2Score": 4.6, + "V3Score": 5.7 + }, + "redhat": { + "V3Vector": "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:L", + "V3Score": 5.7 + } + }, + "References": [ + "https://access.redhat.com/security/cve/CVE-2020-27350", + "https://bugs.launchpad.net/bugs/1899193", + "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-27350", + "https://security.netapp.com/advisory/ntap-20210108-0005/", + "https://ubuntu.com/security/notices/USN-4667-1", + "https://ubuntu.com/security/notices/USN-4667-2", + "https://usn.ubuntu.com/usn/usn-4667-1", + "https://www.debian.org/security/2020/dsa-4808" + ], + "PublishedDate": "2020-12-10T04:15:00Z", + "LastModifiedDate": "2021-01-08T12:15:00Z" + }, + { + "VulnerabilityID": "CVE-2011-3374", + "PkgName": "apt", + "InstalledVersion": "1.8.2.1", + "Layer": { + "Digest": "sha256:8559a31e96f442f2c7b6da49d6c84705f98a39d8be10b3f5f14821d0ee8417df", + "DiffID": "sha256:13cb14c2acd34e45446a50af25cb05095a17624678dbafbcc9e26086547c1d74" + }, + "SeveritySource": "debian", + "PrimaryURL": "https://avd.aquasec.com/nvd/cve-2011-3374", + "DataSource": { + "ID": "debian", + "Name": "Debian Security Tracker", + "URL": "https://salsa.debian.org/security-tracker-team/security-tracker" + }, + "Title": "It was found that apt-key in apt, all versions, do not correctly valid ...", + "Description": "It was found that apt-key in apt, all versions, do not correctly validate gpg keys with the master keyring, leading to a potential man-in-the-middle attack.", + "Severity": "LOW", + "CweIDs": [ + "CWE-347" + ], + "CVSS": { + "nvd": { + "V2Vector": "AV:N/AC:M/Au:N/C:N/I:P/A:N", + "V3Vector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N", + "V2Score": 4.3, + "V3Score": 3.7 + } + }, + "References": [ + "https://access.redhat.com/security/cve/cve-2011-3374", + "https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=642480", + "https://people.canonical.com/~ubuntu-security/cve/2011/CVE-2011-3374.html", + "https://seclists.org/fulldisclosure/2011/Sep/221", + "https://security-tracker.debian.org/tracker/CVE-2011-3374", + "https://snyk.io/vuln/SNYK-LINUX-APT-116518", + "https://ubuntu.com/security/CVE-2011-3374" + ], + "PublishedDate": "2019-11-26T00:15:00Z", + "LastModifiedDate": "2021-02-09T16:08:00Z" + }, + { + "VulnerabilityID": "CVE-2019-18276", + "PkgName": "bash", + "InstalledVersion": "5.0-4", + "Layer": { + "Digest": "sha256:8559a31e96f442f2c7b6da49d6c84705f98a39d8be10b3f5f14821d0ee8417df", + "DiffID": "sha256:13cb14c2acd34e45446a50af25cb05095a17624678dbafbcc9e26086547c1d74" + }, + "SeveritySource": "debian", + "PrimaryURL": "https://avd.aquasec.com/nvd/cve-2019-18276", + "DataSource": { + "ID": "debian", + "Name": "Debian Security Tracker", + "URL": "https://salsa.debian.org/security-tracker-team/security-tracker" + }, + "Title": "bash: when effective UID is not equal to its real UID the saved UID is not dropped", + "Description": "An issue was discovered in disable_priv_mode in shell.c in GNU Bash through 5.0 patch 11. By default, if Bash is run with its effective UID not equal to its real UID, it will drop privileges by setting its effective UID to its real UID. However, it does so incorrectly. On Linux and other systems that support \"saved UID\" functionality, the saved UID is not dropped. An attacker with command execution in the shell can use \"enable -f\" for runtime loading of a new builtin, which can be a shared object that calls setuid() and therefore regains privileges. However, binaries running with an effective UID of 0 are unaffected.", + "Severity": "LOW", + "CweIDs": [ + "CWE-273" + ], + "CVSS": { + "nvd": { + "V2Vector": "AV:L/AC:L/Au:N/C:C/I:C/A:C", + "V3Vector": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", + "V2Score": 7.2, + "V3Score": 7.8 + }, + "redhat": { + "V3Vector": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", + "V3Score": 7.8 + } + }, + "References": [ + "http://packetstormsecurity.com/files/155498/Bash-5.0-Patch-11-Privilege-Escalation.html", + "https://access.redhat.com/security/cve/CVE-2019-18276", + "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-18276", + "https://github.com/bminor/bash/commit/951bdaad7a18cc0dc1036bba86b18b90874d39ff", + "https://linux.oracle.com/cve/CVE-2019-18276.html", + "https://linux.oracle.com/errata/ELSA-2021-1679.html", + "https://lists.apache.org/thread.html/rf9fa47ab66495c78bb4120b0754dd9531ca2ff0430f6685ac9b07772@%3Cdev.mina.apache.org%3E", + "https://nvd.nist.gov/vuln/detail/CVE-2019-18276", + "https://security.gentoo.org/glsa/202105-34", + "https://security.netapp.com/advisory/ntap-20200430-0003/", + "https://ubuntu.com/security/notices/USN-5380-1", + "https://www.oracle.com/security-alerts/cpuapr2022.html", + "https://www.youtube.com/watch?v=-wGtxJ8opa8" + ], + "PublishedDate": "2019-11-28T01:15:00Z", + "LastModifiedDate": "2022-06-07T18:41:00Z" + }, + { + "VulnerabilityID": "CVE-2022-0563", + "PkgName": "bsdutils", + "InstalledVersion": "2.33.1-0.1", + "Layer": { + "Digest": "sha256:8559a31e96f442f2c7b6da49d6c84705f98a39d8be10b3f5f14821d0ee8417df", + "DiffID": "sha256:13cb14c2acd34e45446a50af25cb05095a17624678dbafbcc9e26086547c1d74" + }, + "SeveritySource": "debian", + "PrimaryURL": "https://avd.aquasec.com/nvd/cve-2022-0563", + "DataSource": { + "ID": "debian", + "Name": "Debian Security Tracker", + "URL": "https://salsa.debian.org/security-tracker-team/security-tracker" + }, + "Title": "util-linux: partial disclosure of arbitrary files in chfn and chsh when compiled with libreadline", + "Description": "A flaw was found in the util-linux chfn and chsh utilities when compiled with Readline support. The Readline library uses an \"INPUTRC\" environment variable to get a path to the library config file. When the library cannot parse the specified file, it prints an error message containing data from the file. This flaw allows an unprivileged user to read root-owned files, potentially leading to privilege escalation. This flaw affects util-linux versions prior to 2.37.4.", + "Severity": "LOW", + "CweIDs": [ + "CWE-209" + ], + "CVSS": { + "nvd": { + "V2Vector": "AV:L/AC:M/Au:N/C:P/I:N/A:N", + "V3Vector": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N", + "V2Score": 1.9, + "V3Score": 5.5 + }, + "redhat": { + "V3Vector": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N", + "V3Score": 5.5 + } + }, + "References": [ + "https://access.redhat.com/security/cve/CVE-2022-0563", + "https://lore.kernel.org/util-linux/20220214110609.msiwlm457ngoic6w@ws.net.home/T/#u", + "https://nvd.nist.gov/vuln/detail/CVE-2022-0563", + "https://security.netapp.com/advisory/ntap-20220331-0002/" + ], + "PublishedDate": "2022-02-21T19:15:00Z", + "LastModifiedDate": "2022-06-03T14:15:00Z" + } + ] + } + ] + } + ], + "Misconfigurations": [ + { + "Namespace": "default", + "Kind": "Deployment", + "Name": "redis-follower", + "Results": [ + { + "Target": "Deployment/redis-follower", + "Class": "config", + "Type": "kubernetes", + "MisconfSummary": { + "Successes": 23, + "Failures": 8, + "Exceptions": 0 + }, + "Misconfigurations": [ + { + "Type": "Kubernetes Security Check", + "ID": "KSV001", + "Title": "Process can elevate its own privileges", + "Description": "A program inside the container can elevate its own privileges and run as root, which might give the program control over the container and node.", + "Message": "Container 'follower' of Deployment 'redis-follower' should set 'securityContext.allowPrivilegeEscalation' to false", + "Namespace": "builtin.kubernetes.KSV001", + "Query": "data.builtin.kubernetes.KSV001.deny", + "Resolution": "Set 'set containers[].securityContext.allowPrivilegeEscalation' to 'false'.", + "Severity": "MEDIUM", + "PrimaryURL": "https://avd.aquasec.com/misconfig/ksv001", + "References": [ + "https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted", + "https://avd.aquasec.com/misconfig/ksv001" + ], + "Status": "FAIL", + "Layer": {}, + "CauseMetadata": { + "Provider": "Kubernetes", + "Service": "general", + "StartLine": 132, + "EndLine": 143, + "Code": { + "Lines": [ + { + "Number": 132, + "Content": " - image: gcr.io/google_samples/gb-redis-follower:v2", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": true, + "LastCause": false + }, + { + "Number": 133, + "Content": " imagePullPolicy: IfNotPresent", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 134, + "Content": " name: follower", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 135, + "Content": " ports:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 136, + "Content": " - containerPort: 6379", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 137, + "Content": " protocol: TCP", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 138, + "Content": " resources:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 139, + "Content": " requests:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 140, + "Content": " cpu: 100m", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": true + }, + { + "Number": 141, + "Content": "", + "IsCause": false, + "Annotation": "", + "Truncated": true, + "FirstCause": false, + "LastCause": false + } + ] + } + } + } + ] + } + ] + }, + { + "Namespace": "default", + "Kind": "Deployment", + "Name": "redis-follower", + "Results": [ + { + "Target": "Deployment/redis-follower", + "Class": "config", + "Type": "kubernetes", + "MisconfSummary": { + "Successes": 23, + "Failures": 8, + "Exceptions": 0 + }, + "Misconfigurations": [ + { + "Type": "Kubernetes Security Check", + "ID": "KSV001", + "Title": "Process can elevate its own privileges", + "Description": "A program inside the container can elevate its own privileges and run as root, which might give the program control over the container and node.", + "Message": "Container 'leader' of Deployment 'redis-follower' should set 'securityContext.allowPrivilegeEscalation' to false", + "Namespace": "builtin.kubernetes.KSV001", + "Query": "data.builtin.kubernetes.KSV001.deny", + "Resolution": "Set 'set containers[].securityContext.allowPrivilegeEscalation' to 'false'.", + "Severity": "MEDIUM", + "PrimaryURL": "https://avd.aquasec.com/misconfig/ksv001", + "References": [ + "https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted", + "https://avd.aquasec.com/misconfig/ksv001" + ], + "Status": "FAIL", + "Layer": {}, + "CauseMetadata": { + "Provider": "Kubernetes", + "Service": "general", + "StartLine": 132, + "EndLine": 143, + "Code": { + "Lines": [ + { + "Number": 132, + "Content": " - image: docker.io/redis:6.0.5", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": true, + "LastCause": false + }, + { + "Number": 133, + "Content": " imagePullPolicy: IfNotPresent", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 134, + "Content": " name: leader", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 135, + "Content": " ports:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 136, + "Content": " - containerPort: 6379", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 137, + "Content": " protocol: TCP", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 138, + "Content": " resources:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 139, + "Content": " requests:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 140, + "Content": " cpu: 100m", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": true + }, + { + "Number": 141, + "Content": "", + "IsCause": false, + "Annotation": "", + "Truncated": true, + "FirstCause": false, + "LastCause": false + } + ] + } + } + }, + { + "Type": "Kubernetes Security Check", + "ID": "KSV003", + "Title": "Default capabilities not dropped", + "Description": "The container should drop all default capabilities and add only those that are needed for its execution.", + "Message": "Container 'leader' of Deployment 'redis-follower' should add 'ALL' to 'securityContext.capabilities.drop'", + "Namespace": "builtin.kubernetes.KSV003", + "Query": "data.builtin.kubernetes.KSV003.deny", + "Resolution": "Add 'ALL' to containers[].securityContext.capabilities.drop.", + "Severity": "LOW", + "PrimaryURL": "https://avd.aquasec.com/misconfig/ksv003", + "References": [ + "https://kubesec.io/basics/containers-securitycontext-capabilities-drop-index-all/", + "https://avd.aquasec.com/misconfig/ksv003" + ], + "Status": "FAIL", + "Layer": {}, + "CauseMetadata": { + "Provider": "Kubernetes", + "Service": "general", + "StartLine": 132, + "EndLine": 143, + "Code": { + "Lines": [ + { + "Number": 132, + "Content": " - image: docker.io/redis:6.0.5", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": true, + "LastCause": false + }, + { + "Number": 133, + "Content": " imagePullPolicy: IfNotPresent", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 134, + "Content": " name: leader", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 135, + "Content": " ports:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 136, + "Content": " - containerPort: 6379", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 137, + "Content": " protocol: TCP", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 138, + "Content": " resources:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 139, + "Content": " requests:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 140, + "Content": " cpu: 100m", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": true + }, + { + "Number": 141, + "Content": "", + "IsCause": false, + "Annotation": "", + "Truncated": true, + "FirstCause": false, + "LastCause": false + } + ] + } + } + }, + { + "Type": "Kubernetes Security Check", + "ID": "KSV011", + "Title": "CPU not limited", + "Description": "Enforcing CPU limits prevents DoS via resource exhaustion.", + "Message": "Container 'leader' of Deployment 'redis-follower' should set 'resources.limits.cpu'", + "Namespace": "builtin.kubernetes.KSV011", + "Query": "data.builtin.kubernetes.KSV011.deny", + "Resolution": "Set a limit value under 'containers[].resources.limits.cpu'.", + "Severity": "LOW", + "PrimaryURL": "https://avd.aquasec.com/misconfig/ksv011", + "References": [ + "https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-resource-requests-and-limits", + "https://avd.aquasec.com/misconfig/ksv011" + ], + "Status": "FAIL", + "Layer": {}, + "CauseMetadata": { + "Provider": "Kubernetes", + "Service": "general", + "StartLine": 132, + "EndLine": 143, + "Code": { + "Lines": [ + { + "Number": 132, + "Content": " - image: docker.io/redis:6.0.5", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": true, + "LastCause": false + }, + { + "Number": 133, + "Content": " imagePullPolicy: IfNotPresent", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 134, + "Content": " name: leader", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 135, + "Content": " ports:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 136, + "Content": " - containerPort: 6379", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 137, + "Content": " protocol: TCP", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 138, + "Content": " resources:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 139, + "Content": " requests:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 140, + "Content": " cpu: 100m", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": true + }, + { + "Number": 141, + "Content": "", + "IsCause": false, + "Annotation": "", + "Truncated": true, + "FirstCause": false, + "LastCause": false + } + ] + } + } + }, + { + "Type": "Kubernetes Security Check", + "ID": "KSV012", + "Title": "Runs as root user", + "Description": "'runAsNonRoot' forces the running image to run as a non-root user to ensure least privileges.", + "Message": "Container 'leader' of Deployment 'redis-follower' should set 'securityContext.runAsNonRoot' to true", + "Namespace": "builtin.kubernetes.KSV012", + "Query": "data.builtin.kubernetes.KSV012.deny", + "Resolution": "Set 'containers[].securityContext.runAsNonRoot' to true.", + "Severity": "MEDIUM", + "PrimaryURL": "https://avd.aquasec.com/misconfig/ksv012", + "References": [ + "https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted", + "https://avd.aquasec.com/misconfig/ksv012" + ], + "Status": "FAIL", + "Layer": {}, + "CauseMetadata": { + "Provider": "Kubernetes", + "Service": "general", + "StartLine": 132, + "EndLine": 143, + "Code": { + "Lines": [ + { + "Number": 132, + "Content": " - image: docker.io/redis:6.0.5", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": true, + "LastCause": false + }, + { + "Number": 133, + "Content": " imagePullPolicy: IfNotPresent", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 134, + "Content": " name: leader", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 135, + "Content": " ports:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 136, + "Content": " - containerPort: 6379", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 137, + "Content": " protocol: TCP", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 138, + "Content": " resources:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 139, + "Content": " requests:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 140, + "Content": " cpu: 100m", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": true + }, + { + "Number": 141, + "Content": "", + "IsCause": false, + "Annotation": "", + "Truncated": true, + "FirstCause": false, + "LastCause": false + } + ] + } + } + }, + { + "Type": "Kubernetes Security Check", + "ID": "KSV014", + "Title": "Root file system is not read-only", + "Description": "An immutable root file system prevents applications from writing to their local disk. This can limit intrusions, as attackers will not be able to tamper with the file system or write foreign executables to disk.", + "Message": "Container 'leader' of Deployment 'redis-follower' should set 'securityContext.readOnlyRootFilesystem' to true", + "Namespace": "builtin.kubernetes.KSV014", + "Query": "data.builtin.kubernetes.KSV014.deny", + "Resolution": "Change 'containers[].securityContext.readOnlyRootFilesystem' to 'true'.", + "Severity": "LOW", + "PrimaryURL": "https://avd.aquasec.com/misconfig/ksv014", + "References": [ + "https://kubesec.io/basics/containers-securitycontext-readonlyrootfilesystem-true/", + "https://avd.aquasec.com/misconfig/ksv014" + ], + "Status": "FAIL", + "Layer": {}, + "CauseMetadata": { + "Provider": "Kubernetes", + "Service": "general", + "StartLine": 132, + "EndLine": 143, + "Code": { + "Lines": [ + { + "Number": 132, + "Content": " - image: docker.io/redis:6.0.5", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": true, + "LastCause": false + }, + { + "Number": 133, + "Content": " imagePullPolicy: IfNotPresent", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 134, + "Content": " name: leader", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 135, + "Content": " ports:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 136, + "Content": " - containerPort: 6379", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 137, + "Content": " protocol: TCP", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 138, + "Content": " resources:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 139, + "Content": " requests:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 140, + "Content": " cpu: 100m", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": true + }, + { + "Number": 141, + "Content": "", + "IsCause": false, + "Annotation": "", + "Truncated": true, + "FirstCause": false, + "LastCause": false + } + ] + } + } + } + ] + } + ] + }, + { + "Namespace": "default", + "Kind": "Service", + "Name": "redis-follower", + "Results": [ + { + "Target": "Service/redis-follower", + "Class": "config", + "Type": "kubernetes", + "MisconfSummary": { + "Successes": 31, + "Failures": 0, + "Exceptions": 0 + } + } + ] + }, + { + "Namespace": "default", + "Kind": "Service", + "Name": "redis-follower", + "Results": [ + { + "Target": "Service/redis-follower", + "Class": "config", + "Type": "kubernetes", + "MisconfSummary": { + "Successes": 31, + "Failures": 0, + "Exceptions": 0 + } + } + ] + } + ] + } diff --git a/unittests/scans/trivy/kubernetes_fabricated_internal_duplicates_subset.json b/unittests/scans/trivy/kubernetes_fabricated_internal_duplicates_subset.json new file mode 100644 index 00000000000..f23476a9295 --- /dev/null +++ b/unittests/scans/trivy/kubernetes_fabricated_internal_duplicates_subset.json @@ -0,0 +1,388 @@ +{ + "ClusterName": "arn:aws:eks:us-east-1:576036489467:cluster/monitoring-test-cluster", + "Vulnerabilities": [ + { + "Namespace": "default", + "Kind": "Deployment", + "Name": "redis-follower", + "Results": [ + { + "Target": "docker.io/redis:6.0.5 (debian 10.4)", + "Class": "os-pkgs", + "Type": "debian", + "Vulnerabilities": [ + { + "VulnerabilityID": "CVE-2020-27350", + "VendorIDs": [ + "DSA-4808-1" + ], + "PkgName": "apt", + "InstalledVersion": "1.8.2.1", + "FixedVersion": "1.8.2.2", + "Layer": { + "Digest": "sha256:8559a31e96f442f2c7b6da49d6c84705f98a39d8be10b3f5f14821d0ee8417df", + "DiffID": "sha256:13cb14c2acd34e45446a50af25cb05095a17624678dbafbcc9e26086547c1d74" + }, + "SeveritySource": "nvd", + "PrimaryURL": "https://avd.aquasec.com/nvd/cve-2020-27350", + "DataSource": { + "ID": "debian", + "Name": "Debian Security Tracker", + "URL": "https://salsa.debian.org/security-tracker-team/security-tracker" + }, + "Title": "apt: integer overflows and underflows while parsing .deb packages", + "Description": "APT had several integer overflows and underflows while parsing .deb packages, aka GHSL-2020-168 GHSL-2020-169, in files apt-pkg/contrib/extracttar.cc, apt-pkg/deb/debfile.cc, and apt-pkg/contrib/arfile.cc. This issue affects: apt 1.2.32ubuntu0 versions prior to 1.2.32ubuntu0.2; 1.6.12ubuntu0 versions prior to 1.6.12ubuntu0.2; 2.0.2ubuntu0 versions prior to 2.0.2ubuntu0.2; 2.1.10ubuntu0 versions prior to 2.1.10ubuntu0.1;", + "Severity": "MEDIUM", + "CweIDs": [ + "CWE-190" + ], + "CVSS": { + "nvd": { + "V2Vector": "AV:L/AC:L/Au:N/C:P/I:P/A:P", + "V3Vector": "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:L", + "V2Score": 4.6, + "V3Score": 5.7 + }, + "redhat": { + "V3Vector": "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:L", + "V3Score": 5.7 + } + }, + "References": [ + "https://access.redhat.com/security/cve/CVE-2020-27350", + "https://bugs.launchpad.net/bugs/1899193", + "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-27350", + "https://security.netapp.com/advisory/ntap-20210108-0005/", + "https://ubuntu.com/security/notices/USN-4667-1", + "https://ubuntu.com/security/notices/USN-4667-2", + "https://usn.ubuntu.com/usn/usn-4667-1", + "https://www.debian.org/security/2020/dsa-4808" + ], + "PublishedDate": "2020-12-10T04:15:00Z", + "LastModifiedDate": "2021-01-08T12:15:00Z" + }, + { + "VulnerabilityID": "CVE-2011-3374", + "PkgName": "apt", + "InstalledVersion": "1.8.2.1", + "Layer": { + "Digest": "sha256:8559a31e96f442f2c7b6da49d6c84705f98a39d8be10b3f5f14821d0ee8417df", + "DiffID": "sha256:13cb14c2acd34e45446a50af25cb05095a17624678dbafbcc9e26086547c1d74" + }, + "SeveritySource": "debian", + "PrimaryURL": "https://avd.aquasec.com/nvd/cve-2011-3374", + "DataSource": { + "ID": "debian", + "Name": "Debian Security Tracker", + "URL": "https://salsa.debian.org/security-tracker-team/security-tracker" + }, + "Title": "It was found that apt-key in apt, all versions, do not correctly valid ...", + "Description": "It was found that apt-key in apt, all versions, do not correctly validate gpg keys with the master keyring, leading to a potential man-in-the-middle attack.", + "Severity": "LOW", + "CweIDs": [ + "CWE-347" + ], + "CVSS": { + "nvd": { + "V2Vector": "AV:N/AC:M/Au:N/C:N/I:P/A:N", + "V3Vector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N", + "V2Score": 4.3, + "V3Score": 3.7 + } + }, + "References": [ + "https://access.redhat.com/security/cve/cve-2011-3374", + "https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=642480", + "https://people.canonical.com/~ubuntu-security/cve/2011/CVE-2011-3374.html", + "https://seclists.org/fulldisclosure/2011/Sep/221", + "https://security-tracker.debian.org/tracker/CVE-2011-3374", + "https://snyk.io/vuln/SNYK-LINUX-APT-116518", + "https://ubuntu.com/security/CVE-2011-3374" + ], + "PublishedDate": "2019-11-26T00:15:00Z", + "LastModifiedDate": "2021-02-09T16:08:00Z" + } + ] + } + ] + }, + { + "Namespace": "default", + "Kind": "Deployment", + "Name": "redis-follower", + "Results": [ + { + "Target": "docker.io/redis:6.0.5 (debian 10.4)", + "Class": "os-pkgs", + "Type": "debian", + "Vulnerabilities": [ + { + "VulnerabilityID": "CVE-2020-27350", + "VendorIDs": [ + "DSA-4808-1" + ], + "PkgName": "apt", + "InstalledVersion": "1.8.2.1", + "FixedVersion": "1.8.2.2", + "Layer": { + "Digest": "sha256:8559a31e96f442f2c7b6da49d6c84705f98a39d8be10b3f5f14821d0ee8417df", + "DiffID": "sha256:13cb14c2acd34e45446a50af25cb05095a17624678dbafbcc9e26086547c1d74" + }, + "SeveritySource": "nvd", + "PrimaryURL": "https://avd.aquasec.com/nvd/cve-2020-27350", + "DataSource": { + "ID": "debian", + "Name": "Debian Security Tracker", + "URL": "https://salsa.debian.org/security-tracker-team/security-tracker" + }, + "Title": "apt: integer overflows and underflows while parsing .deb packages", + "Description": "APT had several integer overflows and underflows while parsing .deb packages, aka GHSL-2020-168 GHSL-2020-169, in files apt-pkg/contrib/extracttar.cc, apt-pkg/deb/debfile.cc, and apt-pkg/contrib/arfile.cc. This issue affects: apt 1.2.32ubuntu0 versions prior to 1.2.32ubuntu0.2; 1.6.12ubuntu0 versions prior to 1.6.12ubuntu0.2; 2.0.2ubuntu0 versions prior to 2.0.2ubuntu0.2; 2.1.10ubuntu0 versions prior to 2.1.10ubuntu0.1;", + "Severity": "MEDIUM", + "CweIDs": [ + "CWE-190" + ], + "CVSS": { + "nvd": { + "V2Vector": "AV:L/AC:L/Au:N/C:P/I:P/A:P", + "V3Vector": "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:L", + "V2Score": 4.6, + "V3Score": 5.7 + }, + "redhat": { + "V3Vector": "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:L", + "V3Score": 5.7 + } + }, + "References": [ + "https://access.redhat.com/security/cve/CVE-2020-27350", + "https://bugs.launchpad.net/bugs/1899193", + "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-27350", + "https://security.netapp.com/advisory/ntap-20210108-0005/", + "https://ubuntu.com/security/notices/USN-4667-1", + "https://ubuntu.com/security/notices/USN-4667-2", + "https://usn.ubuntu.com/usn/usn-4667-1", + "https://www.debian.org/security/2020/dsa-4808" + ], + "PublishedDate": "2020-12-10T04:15:00Z", + "LastModifiedDate": "2021-01-08T12:15:00Z" + }, + { + "VulnerabilityID": "CVE-2011-3374", + "PkgName": "apt", + "InstalledVersion": "1.8.2.1", + "Layer": { + "Digest": "sha256:8559a31e96f442f2c7b6da49d6c84705f98a39d8be10b3f5f14821d0ee8417df", + "DiffID": "sha256:13cb14c2acd34e45446a50af25cb05095a17624678dbafbcc9e26086547c1d74" + }, + "SeveritySource": "debian", + "PrimaryURL": "https://avd.aquasec.com/nvd/cve-2011-3374", + "DataSource": { + "ID": "debian", + "Name": "Debian Security Tracker", + "URL": "https://salsa.debian.org/security-tracker-team/security-tracker" + }, + "Title": "It was found that apt-key in apt, all versions, do not correctly valid ...", + "Description": "It was found that apt-key in apt, all versions, do not correctly validate gpg keys with the master keyring, leading to a potential man-in-the-middle attack.", + "Severity": "LOW", + "CweIDs": [ + "CWE-347" + ], + "CVSS": { + "nvd": { + "V2Vector": "AV:N/AC:M/Au:N/C:N/I:P/A:N", + "V3Vector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N", + "V2Score": 4.3, + "V3Score": 3.7 + } + }, + "References": [ + "https://access.redhat.com/security/cve/cve-2011-3374", + "https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=642480", + "https://people.canonical.com/~ubuntu-security/cve/2011/CVE-2011-3374.html", + "https://seclists.org/fulldisclosure/2011/Sep/221", + "https://security-tracker.debian.org/tracker/CVE-2011-3374", + "https://snyk.io/vuln/SNYK-LINUX-APT-116518", + "https://ubuntu.com/security/CVE-2011-3374" + ], + "PublishedDate": "2019-11-26T00:15:00Z", + "LastModifiedDate": "2021-02-09T16:08:00Z" + } + ] + } + ] + } + ], + "Misconfigurations": [ + { + "Namespace": "default", + "Kind": "Deployment", + "Name": "redis-follower", + "Results": [ + { + "Target": "Deployment/redis-follower", + "Class": "config", + "Type": "kubernetes", + "MisconfSummary": { + "Successes": 23, + "Failures": 8, + "Exceptions": 0 + }, + "Misconfigurations": [ + { + "Type": "Kubernetes Security Check", + "ID": "KSV014", + "Title": "Root file system is not read-only", + "Description": "An immutable root file system prevents applications from writing to their local disk. This can limit intrusions, as attackers will not be able to tamper with the file system or write foreign executables to disk.", + "Message": "Container 'leader' of Deployment 'redis-follower' should set 'securityContext.readOnlyRootFilesystem' to true", + "Namespace": "builtin.kubernetes.KSV014", + "Query": "data.builtin.kubernetes.KSV014.deny", + "Resolution": "Change 'containers[].securityContext.readOnlyRootFilesystem' to 'true'.", + "Severity": "LOW", + "PrimaryURL": "https://avd.aquasec.com/misconfig/ksv014", + "References": [ + "https://kubesec.io/basics/containers-securitycontext-readonlyrootfilesystem-true/", + "https://avd.aquasec.com/misconfig/ksv014" + ], + "Status": "FAIL", + "Layer": {}, + "CauseMetadata": { + "Provider": "Kubernetes", + "Service": "general", + "StartLine": 132, + "EndLine": 143, + "Code": { + "Lines": [ + { + "Number": 132, + "Content": " - image: docker.io/redis:6.0.5", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": true, + "LastCause": false + }, + { + "Number": 133, + "Content": " imagePullPolicy: IfNotPresent", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 134, + "Content": " name: leader", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 135, + "Content": " ports:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 136, + "Content": " - containerPort: 6379", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 137, + "Content": " protocol: TCP", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 138, + "Content": " resources:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 139, + "Content": " requests:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 140, + "Content": " cpu: 100m", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": true + }, + { + "Number": 141, + "Content": "", + "IsCause": false, + "Annotation": "", + "Truncated": true, + "FirstCause": false, + "LastCause": false + } + ] + } + } + } + ] + } + ] + }, + { + "Namespace": "default", + "Kind": "Service", + "Name": "redis-follower", + "Results": [ + { + "Target": "Service/redis-follower", + "Class": "config", + "Type": "kubernetes", + "MisconfSummary": { + "Successes": 31, + "Failures": 0, + "Exceptions": 0 + } + } + ] + }, + { + "Namespace": "default", + "Kind": "Service", + "Name": "redis-follower", + "Results": [ + { + "Target": "Service/redis-follower", + "Class": "config", + "Type": "kubernetes", + "MisconfSummary": { + "Successes": 31, + "Failures": 0, + "Exceptions": 0 + } + } + ] + } + ] +} diff --git a/unittests/scans/trivy/kubernetes_fabricated_subset.json b/unittests/scans/trivy/kubernetes_fabricated_subset.json new file mode 100644 index 00000000000..b25c79e601b --- /dev/null +++ b/unittests/scans/trivy/kubernetes_fabricated_subset.json @@ -0,0 +1,1141 @@ +{ + "ClusterName": "arn:aws:eks:us-east-1:576036489467:cluster/monitoring-test-cluster", + "Vulnerabilities": [ + { + "Namespace": "default", + "Kind": "Deployment", + "Name": "redis-follower", + "Results": [ + { + "Target": "gcr.io/google_samples/gb-redis-follower:v2 (debian 10.4)", + "Class": "os-pkgs", + "Type": "debian", + "Vulnerabilities": [ + { + "VulnerabilityID": "CVE-2020-27350", + "VendorIDs": [ + "DSA-4808-1" + ], + "PkgName": "apt", + "InstalledVersion": "1.8.2.1", + "FixedVersion": "1.8.2.2", + "Layer": { + "Digest": "sha256:8559a31e96f442f2c7b6da49d6c84705f98a39d8be10b3f5f14821d0ee8417df", + "DiffID": "sha256:13cb14c2acd34e45446a50af25cb05095a17624678dbafbcc9e26086547c1d74" + }, + "SeveritySource": "nvd", + "PrimaryURL": "https://avd.aquasec.com/nvd/cve-2020-27350", + "DataSource": { + "ID": "debian", + "Name": "Debian Security Tracker", + "URL": "https://salsa.debian.org/security-tracker-team/security-tracker" + }, + "Title": "apt: integer overflows and underflows while parsing .deb packages", + "Description": "APT had several integer overflows and underflows while parsing .deb packages, aka GHSL-2020-168 GHSL-2020-169, in files apt-pkg/contrib/extracttar.cc, apt-pkg/deb/debfile.cc, and apt-pkg/contrib/arfile.cc. This issue affects: apt 1.2.32ubuntu0 versions prior to 1.2.32ubuntu0.2; 1.6.12ubuntu0 versions prior to 1.6.12ubuntu0.2; 2.0.2ubuntu0 versions prior to 2.0.2ubuntu0.2; 2.1.10ubuntu0 versions prior to 2.1.10ubuntu0.1;", + "Severity": "MEDIUM", + "CweIDs": [ + "CWE-190" + ], + "CVSS": { + "nvd": { + "V2Vector": "AV:L/AC:L/Au:N/C:P/I:P/A:P", + "V3Vector": "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:L", + "V2Score": 4.6, + "V3Score": 5.7 + }, + "redhat": { + "V3Vector": "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:L", + "V3Score": 5.7 + } + }, + "References": [ + "https://access.redhat.com/security/cve/CVE-2020-27350", + "https://bugs.launchpad.net/bugs/1899193", + "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-27350", + "https://security.netapp.com/advisory/ntap-20210108-0005/", + "https://ubuntu.com/security/notices/USN-4667-1", + "https://ubuntu.com/security/notices/USN-4667-2", + "https://usn.ubuntu.com/usn/usn-4667-1", + "https://www.debian.org/security/2020/dsa-4808" + ], + "PublishedDate": "2020-12-10T04:15:00Z", + "LastModifiedDate": "2021-01-08T12:15:00Z" + }, + { + "VulnerabilityID": "CVE-2011-3374", + "PkgName": "apt", + "InstalledVersion": "1.8.2.1", + "Layer": { + "Digest": "sha256:8559a31e96f442f2c7b6da49d6c84705f98a39d8be10b3f5f14821d0ee8417df", + "DiffID": "sha256:13cb14c2acd34e45446a50af25cb05095a17624678dbafbcc9e26086547c1d74" + }, + "SeveritySource": "debian", + "PrimaryURL": "https://avd.aquasec.com/nvd/cve-2011-3374", + "DataSource": { + "ID": "debian", + "Name": "Debian Security Tracker", + "URL": "https://salsa.debian.org/security-tracker-team/security-tracker" + }, + "Title": "It was found that apt-key in apt, all versions, do not correctly valid ...", + "Description": "It was found that apt-key in apt, all versions, do not correctly validate gpg keys with the master keyring, leading to a potential man-in-the-middle attack.", + "Severity": "LOW", + "CweIDs": [ + "CWE-347" + ], + "CVSS": { + "nvd": { + "V2Vector": "AV:N/AC:M/Au:N/C:N/I:P/A:N", + "V3Vector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N", + "V2Score": 4.3, + "V3Score": 3.7 + } + }, + "References": [ + "https://access.redhat.com/security/cve/cve-2011-3374", + "https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=642480", + "https://people.canonical.com/~ubuntu-security/cve/2011/CVE-2011-3374.html", + "https://seclists.org/fulldisclosure/2011/Sep/221", + "https://security-tracker.debian.org/tracker/CVE-2011-3374", + "https://snyk.io/vuln/SNYK-LINUX-APT-116518", + "https://ubuntu.com/security/CVE-2011-3374" + ], + "PublishedDate": "2019-11-26T00:15:00Z", + "LastModifiedDate": "2021-02-09T16:08:00Z" + }, + { + "VulnerabilityID": "CVE-2019-18276", + "PkgName": "bash", + "InstalledVersion": "5.0-4", + "Layer": { + "Digest": "sha256:8559a31e96f442f2c7b6da49d6c84705f98a39d8be10b3f5f14821d0ee8417df", + "DiffID": "sha256:13cb14c2acd34e45446a50af25cb05095a17624678dbafbcc9e26086547c1d74" + }, + "SeveritySource": "debian", + "PrimaryURL": "https://avd.aquasec.com/nvd/cve-2019-18276", + "DataSource": { + "ID": "debian", + "Name": "Debian Security Tracker", + "URL": "https://salsa.debian.org/security-tracker-team/security-tracker" + }, + "Title": "bash: when effective UID is not equal to its real UID the saved UID is not dropped", + "Description": "An issue was discovered in disable_priv_mode in shell.c in GNU Bash through 5.0 patch 11. By default, if Bash is run with its effective UID not equal to its real UID, it will drop privileges by setting its effective UID to its real UID. However, it does so incorrectly. On Linux and other systems that support \"saved UID\" functionality, the saved UID is not dropped. An attacker with command execution in the shell can use \"enable -f\" for runtime loading of a new builtin, which can be a shared object that calls setuid() and therefore regains privileges. However, binaries running with an effective UID of 0 are unaffected.", + "Severity": "LOW", + "CweIDs": [ + "CWE-273" + ], + "CVSS": { + "nvd": { + "V2Vector": "AV:L/AC:L/Au:N/C:C/I:C/A:C", + "V3Vector": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", + "V2Score": 7.2, + "V3Score": 7.8 + }, + "redhat": { + "V3Vector": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", + "V3Score": 7.8 + } + }, + "References": [ + "http://packetstormsecurity.com/files/155498/Bash-5.0-Patch-11-Privilege-Escalation.html", + "https://access.redhat.com/security/cve/CVE-2019-18276", + "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-18276", + "https://github.com/bminor/bash/commit/951bdaad7a18cc0dc1036bba86b18b90874d39ff", + "https://linux.oracle.com/cve/CVE-2019-18276.html", + "https://linux.oracle.com/errata/ELSA-2021-1679.html", + "https://lists.apache.org/thread.html/rf9fa47ab66495c78bb4120b0754dd9531ca2ff0430f6685ac9b07772@%3Cdev.mina.apache.org%3E", + "https://nvd.nist.gov/vuln/detail/CVE-2019-18276", + "https://security.gentoo.org/glsa/202105-34", + "https://security.netapp.com/advisory/ntap-20200430-0003/", + "https://ubuntu.com/security/notices/USN-5380-1", + "https://www.oracle.com/security-alerts/cpuapr2022.html", + "https://www.youtube.com/watch?v=-wGtxJ8opa8" + ], + "PublishedDate": "2019-11-28T01:15:00Z", + "LastModifiedDate": "2022-06-07T18:41:00Z" + } + ] + } + ] + }, + { + "Namespace": "default", + "Kind": "Deployment", + "Name": "redis-leader", + "Results": [ + { + "Target": "docker.io/redis:6.0.5 (debian 10.4)", + "Class": "os-pkgs", + "Type": "debian", + "Vulnerabilities": [ + { + "VulnerabilityID": "CVE-2020-27350", + "VendorIDs": [ + "DSA-4808-1" + ], + "PkgName": "apt", + "InstalledVersion": "1.8.2.1", + "FixedVersion": "1.8.2.2", + "Layer": { + "Digest": "sha256:8559a31e96f442f2c7b6da49d6c84705f98a39d8be10b3f5f14821d0ee8417df", + "DiffID": "sha256:13cb14c2acd34e45446a50af25cb05095a17624678dbafbcc9e26086547c1d74" + }, + "SeveritySource": "nvd", + "PrimaryURL": "https://avd.aquasec.com/nvd/cve-2020-27350", + "DataSource": { + "ID": "debian", + "Name": "Debian Security Tracker", + "URL": "https://salsa.debian.org/security-tracker-team/security-tracker" + }, + "Title": "apt: integer overflows and underflows while parsing .deb packages", + "Description": "APT had several integer overflows and underflows while parsing .deb packages, aka GHSL-2020-168 GHSL-2020-169, in files apt-pkg/contrib/extracttar.cc, apt-pkg/deb/debfile.cc, and apt-pkg/contrib/arfile.cc. This issue affects: apt 1.2.32ubuntu0 versions prior to 1.2.32ubuntu0.2; 1.6.12ubuntu0 versions prior to 1.6.12ubuntu0.2; 2.0.2ubuntu0 versions prior to 2.0.2ubuntu0.2; 2.1.10ubuntu0 versions prior to 2.1.10ubuntu0.1;", + "Severity": "MEDIUM", + "CweIDs": [ + "CWE-190" + ], + "CVSS": { + "nvd": { + "V2Vector": "AV:L/AC:L/Au:N/C:P/I:P/A:P", + "V3Vector": "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:L", + "V2Score": 4.6, + "V3Score": 5.7 + }, + "redhat": { + "V3Vector": "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:L", + "V3Score": 5.7 + } + }, + "References": [ + "https://access.redhat.com/security/cve/CVE-2020-27350", + "https://bugs.launchpad.net/bugs/1899193", + "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-27350", + "https://security.netapp.com/advisory/ntap-20210108-0005/", + "https://ubuntu.com/security/notices/USN-4667-1", + "https://ubuntu.com/security/notices/USN-4667-2", + "https://usn.ubuntu.com/usn/usn-4667-1", + "https://www.debian.org/security/2020/dsa-4808" + ], + "PublishedDate": "2020-12-10T04:15:00Z", + "LastModifiedDate": "2021-01-08T12:15:00Z" + }, + { + "VulnerabilityID": "CVE-2011-3374", + "PkgName": "apt", + "InstalledVersion": "1.8.2.1", + "Layer": { + "Digest": "sha256:8559a31e96f442f2c7b6da49d6c84705f98a39d8be10b3f5f14821d0ee8417df", + "DiffID": "sha256:13cb14c2acd34e45446a50af25cb05095a17624678dbafbcc9e26086547c1d74" + }, + "SeveritySource": "debian", + "PrimaryURL": "https://avd.aquasec.com/nvd/cve-2011-3374", + "DataSource": { + "ID": "debian", + "Name": "Debian Security Tracker", + "URL": "https://salsa.debian.org/security-tracker-team/security-tracker" + }, + "Title": "It was found that apt-key in apt, all versions, do not correctly valid ...", + "Description": "It was found that apt-key in apt, all versions, do not correctly validate gpg keys with the master keyring, leading to a potential man-in-the-middle attack.", + "Severity": "LOW", + "CweIDs": [ + "CWE-347" + ], + "CVSS": { + "nvd": { + "V2Vector": "AV:N/AC:M/Au:N/C:N/I:P/A:N", + "V3Vector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N", + "V2Score": 4.3, + "V3Score": 3.7 + } + }, + "References": [ + "https://access.redhat.com/security/cve/cve-2011-3374", + "https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=642480", + "https://people.canonical.com/~ubuntu-security/cve/2011/CVE-2011-3374.html", + "https://seclists.org/fulldisclosure/2011/Sep/221", + "https://security-tracker.debian.org/tracker/CVE-2011-3374", + "https://snyk.io/vuln/SNYK-LINUX-APT-116518", + "https://ubuntu.com/security/CVE-2011-3374" + ], + "PublishedDate": "2019-11-26T00:15:00Z", + "LastModifiedDate": "2021-02-09T16:08:00Z" + }, + { + "VulnerabilityID": "CVE-2019-18276", + "PkgName": "bash", + "InstalledVersion": "5.0-4", + "Layer": { + "Digest": "sha256:8559a31e96f442f2c7b6da49d6c84705f98a39d8be10b3f5f14821d0ee8417df", + "DiffID": "sha256:13cb14c2acd34e45446a50af25cb05095a17624678dbafbcc9e26086547c1d74" + }, + "SeveritySource": "debian", + "PrimaryURL": "https://avd.aquasec.com/nvd/cve-2019-18276", + "DataSource": { + "ID": "debian", + "Name": "Debian Security Tracker", + "URL": "https://salsa.debian.org/security-tracker-team/security-tracker" + }, + "Title": "bash: when effective UID is not equal to its real UID the saved UID is not dropped", + "Description": "An issue was discovered in disable_priv_mode in shell.c in GNU Bash through 5.0 patch 11. By default, if Bash is run with its effective UID not equal to its real UID, it will drop privileges by setting its effective UID to its real UID. However, it does so incorrectly. On Linux and other systems that support \"saved UID\" functionality, the saved UID is not dropped. An attacker with command execution in the shell can use \"enable -f\" for runtime loading of a new builtin, which can be a shared object that calls setuid() and therefore regains privileges. However, binaries running with an effective UID of 0 are unaffected.", + "Severity": "LOW", + "CweIDs": [ + "CWE-273" + ], + "CVSS": { + "nvd": { + "V2Vector": "AV:L/AC:L/Au:N/C:C/I:C/A:C", + "V3Vector": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", + "V2Score": 7.2, + "V3Score": 7.8 + }, + "redhat": { + "V3Vector": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", + "V3Score": 7.8 + } + }, + "References": [ + "http://packetstormsecurity.com/files/155498/Bash-5.0-Patch-11-Privilege-Escalation.html", + "https://access.redhat.com/security/cve/CVE-2019-18276", + "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-18276", + "https://github.com/bminor/bash/commit/951bdaad7a18cc0dc1036bba86b18b90874d39ff", + "https://linux.oracle.com/cve/CVE-2019-18276.html", + "https://linux.oracle.com/errata/ELSA-2021-1679.html", + "https://lists.apache.org/thread.html/rf9fa47ab66495c78bb4120b0754dd9531ca2ff0430f6685ac9b07772@%3Cdev.mina.apache.org%3E", + "https://nvd.nist.gov/vuln/detail/CVE-2019-18276", + "https://security.gentoo.org/glsa/202105-34", + "https://security.netapp.com/advisory/ntap-20200430-0003/", + "https://ubuntu.com/security/notices/USN-5380-1", + "https://www.oracle.com/security-alerts/cpuapr2022.html", + "https://www.youtube.com/watch?v=-wGtxJ8opa8" + ], + "PublishedDate": "2019-11-28T01:15:00Z", + "LastModifiedDate": "2022-06-07T18:41:00Z" + }, + { + "VulnerabilityID": "CVE-2022-0563", + "PkgName": "bsdutils", + "InstalledVersion": "2.33.1-0.1", + "Layer": { + "Digest": "sha256:8559a31e96f442f2c7b6da49d6c84705f98a39d8be10b3f5f14821d0ee8417df", + "DiffID": "sha256:13cb14c2acd34e45446a50af25cb05095a17624678dbafbcc9e26086547c1d74" + }, + "SeveritySource": "debian", + "PrimaryURL": "https://avd.aquasec.com/nvd/cve-2022-0563", + "DataSource": { + "ID": "debian", + "Name": "Debian Security Tracker", + "URL": "https://salsa.debian.org/security-tracker-team/security-tracker" + }, + "Title": "util-linux: partial disclosure of arbitrary files in chfn and chsh when compiled with libreadline", + "Description": "A flaw was found in the util-linux chfn and chsh utilities when compiled with Readline support. The Readline library uses an \"INPUTRC\" environment variable to get a path to the library config file. When the library cannot parse the specified file, it prints an error message containing data from the file. This flaw allows an unprivileged user to read root-owned files, potentially leading to privilege escalation. This flaw affects util-linux versions prior to 2.37.4.", + "Severity": "LOW", + "CweIDs": [ + "CWE-209" + ], + "CVSS": { + "nvd": { + "V2Vector": "AV:L/AC:M/Au:N/C:P/I:N/A:N", + "V3Vector": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N", + "V2Score": 1.9, + "V3Score": 5.5 + }, + "redhat": { + "V3Vector": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N", + "V3Score": 5.5 + } + }, + "References": [ + "https://access.redhat.com/security/cve/CVE-2022-0563", + "https://lore.kernel.org/util-linux/20220214110609.msiwlm457ngoic6w@ws.net.home/T/#u", + "https://nvd.nist.gov/vuln/detail/CVE-2022-0563", + "https://security.netapp.com/advisory/ntap-20220331-0002/" + ], + "PublishedDate": "2022-02-21T19:15:00Z", + "LastModifiedDate": "2022-06-03T14:15:00Z" + } + ] + } + ] + } + ], + "Misconfigurations": [ + { + "Namespace": "default", + "Kind": "Deployment", + "Name": "redis-follower", + "Results": [ + { + "Target": "Deployment/redis-follower", + "Class": "config", + "Type": "kubernetes", + "MisconfSummary": { + "Successes": 23, + "Failures": 8, + "Exceptions": 0 + }, + "Misconfigurations": [ + { + "Type": "Kubernetes Security Check", + "ID": "KSV001", + "Title": "Process can elevate its own privileges", + "Description": "A program inside the container can elevate its own privileges and run as root, which might give the program control over the container and node.", + "Message": "Container 'follower' of Deployment 'redis-follower' should set 'securityContext.allowPrivilegeEscalation' to false", + "Namespace": "builtin.kubernetes.KSV001", + "Query": "data.builtin.kubernetes.KSV001.deny", + "Resolution": "Set 'set containers[].securityContext.allowPrivilegeEscalation' to 'false'.", + "Severity": "MEDIUM", + "PrimaryURL": "https://avd.aquasec.com/misconfig/ksv001", + "References": [ + "https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted", + "https://avd.aquasec.com/misconfig/ksv001" + ], + "Status": "FAIL", + "Layer": {}, + "CauseMetadata": { + "Provider": "Kubernetes", + "Service": "general", + "StartLine": 132, + "EndLine": 143, + "Code": { + "Lines": [ + { + "Number": 132, + "Content": " - image: gcr.io/google_samples/gb-redis-follower:v2", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": true, + "LastCause": false + }, + { + "Number": 133, + "Content": " imagePullPolicy: IfNotPresent", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 134, + "Content": " name: follower", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 135, + "Content": " ports:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 136, + "Content": " - containerPort: 6379", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 137, + "Content": " protocol: TCP", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 138, + "Content": " resources:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 139, + "Content": " requests:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 140, + "Content": " cpu: 100m", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": true + }, + { + "Number": 141, + "Content": "", + "IsCause": false, + "Annotation": "", + "Truncated": true, + "FirstCause": false, + "LastCause": false + } + ] + } + } + } + ] + } + ] + }, + { + "Namespace": "default", + "Kind": "Deployment", + "Name": "redis-leader", + "Results": [ + { + "Target": "Deployment/redis-leader", + "Class": "config", + "Type": "kubernetes", + "MisconfSummary": { + "Successes": 23, + "Failures": 8, + "Exceptions": 0 + }, + "Misconfigurations": [ + { + "Type": "Kubernetes Security Check", + "ID": "KSV001", + "Title": "Process can elevate its own privileges", + "Description": "A program inside the container can elevate its own privileges and run as root, which might give the program control over the container and node.", + "Message": "Container 'leader' of Deployment 'redis-leader' should set 'securityContext.allowPrivilegeEscalation' to false", + "Namespace": "builtin.kubernetes.KSV001", + "Query": "data.builtin.kubernetes.KSV001.deny", + "Resolution": "Set 'set containers[].securityContext.allowPrivilegeEscalation' to 'false'.", + "Severity": "MEDIUM", + "PrimaryURL": "https://avd.aquasec.com/misconfig/ksv001", + "References": [ + "https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted", + "https://avd.aquasec.com/misconfig/ksv001" + ], + "Status": "FAIL", + "Layer": {}, + "CauseMetadata": { + "Provider": "Kubernetes", + "Service": "general", + "StartLine": 132, + "EndLine": 143, + "Code": { + "Lines": [ + { + "Number": 132, + "Content": " - image: docker.io/redis:6.0.5", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": true, + "LastCause": false + }, + { + "Number": 133, + "Content": " imagePullPolicy: IfNotPresent", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 134, + "Content": " name: leader", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 135, + "Content": " ports:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 136, + "Content": " - containerPort: 6379", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 137, + "Content": " protocol: TCP", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 138, + "Content": " resources:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 139, + "Content": " requests:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 140, + "Content": " cpu: 100m", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": true + }, + { + "Number": 141, + "Content": "", + "IsCause": false, + "Annotation": "", + "Truncated": true, + "FirstCause": false, + "LastCause": false + } + ] + } + } + }, + { + "Type": "Kubernetes Security Check", + "ID": "KSV003", + "Title": "Default capabilities not dropped", + "Description": "The container should drop all default capabilities and add only those that are needed for its execution.", + "Message": "Container 'leader' of Deployment 'redis-leader' should add 'ALL' to 'securityContext.capabilities.drop'", + "Namespace": "builtin.kubernetes.KSV003", + "Query": "data.builtin.kubernetes.KSV003.deny", + "Resolution": "Add 'ALL' to containers[].securityContext.capabilities.drop.", + "Severity": "LOW", + "PrimaryURL": "https://avd.aquasec.com/misconfig/ksv003", + "References": [ + "https://kubesec.io/basics/containers-securitycontext-capabilities-drop-index-all/", + "https://avd.aquasec.com/misconfig/ksv003" + ], + "Status": "FAIL", + "Layer": {}, + "CauseMetadata": { + "Provider": "Kubernetes", + "Service": "general", + "StartLine": 132, + "EndLine": 143, + "Code": { + "Lines": [ + { + "Number": 132, + "Content": " - image: docker.io/redis:6.0.5", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": true, + "LastCause": false + }, + { + "Number": 133, + "Content": " imagePullPolicy: IfNotPresent", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 134, + "Content": " name: leader", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 135, + "Content": " ports:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 136, + "Content": " - containerPort: 6379", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 137, + "Content": " protocol: TCP", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 138, + "Content": " resources:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 139, + "Content": " requests:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 140, + "Content": " cpu: 100m", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": true + }, + { + "Number": 141, + "Content": "", + "IsCause": false, + "Annotation": "", + "Truncated": true, + "FirstCause": false, + "LastCause": false + } + ] + } + } + }, + { + "Type": "Kubernetes Security Check", + "ID": "KSV011", + "Title": "CPU not limited", + "Description": "Enforcing CPU limits prevents DoS via resource exhaustion.", + "Message": "Container 'leader' of Deployment 'redis-leader' should set 'resources.limits.cpu'", + "Namespace": "builtin.kubernetes.KSV011", + "Query": "data.builtin.kubernetes.KSV011.deny", + "Resolution": "Set a limit value under 'containers[].resources.limits.cpu'.", + "Severity": "LOW", + "PrimaryURL": "https://avd.aquasec.com/misconfig/ksv011", + "References": [ + "https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-resource-requests-and-limits", + "https://avd.aquasec.com/misconfig/ksv011" + ], + "Status": "FAIL", + "Layer": {}, + "CauseMetadata": { + "Provider": "Kubernetes", + "Service": "general", + "StartLine": 132, + "EndLine": 143, + "Code": { + "Lines": [ + { + "Number": 132, + "Content": " - image: docker.io/redis:6.0.5", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": true, + "LastCause": false + }, + { + "Number": 133, + "Content": " imagePullPolicy: IfNotPresent", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 134, + "Content": " name: leader", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 135, + "Content": " ports:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 136, + "Content": " - containerPort: 6379", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 137, + "Content": " protocol: TCP", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 138, + "Content": " resources:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 139, + "Content": " requests:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 140, + "Content": " cpu: 100m", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": true + }, + { + "Number": 141, + "Content": "", + "IsCause": false, + "Annotation": "", + "Truncated": true, + "FirstCause": false, + "LastCause": false + } + ] + } + } + }, + { + "Type": "Kubernetes Security Check", + "ID": "KSV012", + "Title": "Runs as root user", + "Description": "'runAsNonRoot' forces the running image to run as a non-root user to ensure least privileges.", + "Message": "Container 'leader' of Deployment 'redis-leader' should set 'securityContext.runAsNonRoot' to true", + "Namespace": "builtin.kubernetes.KSV012", + "Query": "data.builtin.kubernetes.KSV012.deny", + "Resolution": "Set 'containers[].securityContext.runAsNonRoot' to true.", + "Severity": "MEDIUM", + "PrimaryURL": "https://avd.aquasec.com/misconfig/ksv012", + "References": [ + "https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted", + "https://avd.aquasec.com/misconfig/ksv012" + ], + "Status": "FAIL", + "Layer": {}, + "CauseMetadata": { + "Provider": "Kubernetes", + "Service": "general", + "StartLine": 132, + "EndLine": 143, + "Code": { + "Lines": [ + { + "Number": 132, + "Content": " - image: docker.io/redis:6.0.5", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": true, + "LastCause": false + }, + { + "Number": 133, + "Content": " imagePullPolicy: IfNotPresent", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 134, + "Content": " name: leader", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 135, + "Content": " ports:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 136, + "Content": " - containerPort: 6379", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 137, + "Content": " protocol: TCP", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 138, + "Content": " resources:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 139, + "Content": " requests:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 140, + "Content": " cpu: 100m", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": true + }, + { + "Number": 141, + "Content": "", + "IsCause": false, + "Annotation": "", + "Truncated": true, + "FirstCause": false, + "LastCause": false + } + ] + } + } + }, + { + "Type": "Kubernetes Security Check", + "ID": "KSV014", + "Title": "Root file system is not read-only", + "Description": "An immutable root file system prevents applications from writing to their local disk. This can limit intrusions, as attackers will not be able to tamper with the file system or write foreign executables to disk.", + "Message": "Container 'leader' of Deployment 'redis-leader' should set 'securityContext.readOnlyRootFilesystem' to true", + "Namespace": "builtin.kubernetes.KSV014", + "Query": "data.builtin.kubernetes.KSV014.deny", + "Resolution": "Change 'containers[].securityContext.readOnlyRootFilesystem' to 'true'.", + "Severity": "LOW", + "PrimaryURL": "https://avd.aquasec.com/misconfig/ksv014", + "References": [ + "https://kubesec.io/basics/containers-securitycontext-readonlyrootfilesystem-true/", + "https://avd.aquasec.com/misconfig/ksv014" + ], + "Status": "FAIL", + "Layer": {}, + "CauseMetadata": { + "Provider": "Kubernetes", + "Service": "general", + "StartLine": 132, + "EndLine": 143, + "Code": { + "Lines": [ + { + "Number": 132, + "Content": " - image: docker.io/redis:6.0.5", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": true, + "LastCause": false + }, + { + "Number": 133, + "Content": " imagePullPolicy: IfNotPresent", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 134, + "Content": " name: leader", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 135, + "Content": " ports:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 136, + "Content": " - containerPort: 6379", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 137, + "Content": " protocol: TCP", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 138, + "Content": " resources:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 139, + "Content": " requests:", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 140, + "Content": " cpu: 100m", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": true + }, + { + "Number": 141, + "Content": "", + "IsCause": false, + "Annotation": "", + "Truncated": true, + "FirstCause": false, + "LastCause": false + } + ] + } + } + } + ] + } + ] + }, + { + "Namespace": "default", + "Kind": "Service", + "Name": "redis-follower", + "Results": [ + { + "Target": "Service/redis-follower", + "Class": "config", + "Type": "kubernetes", + "MisconfSummary": { + "Successes": 31, + "Failures": 0, + "Exceptions": 0 + } + } + ] + }, + { + "Namespace": "default", + "Kind": "Service", + "Name": "redis-leader", + "Results": [ + { + "Target": "Service/redis-leader", + "Class": "config", + "Type": "kubernetes", + "MisconfSummary": { + "Successes": 31, + "Failures": 0, + "Exceptions": 0 + } + } + ] + } + ] + } diff --git a/unittests/scans/zap/dvwa_baseline_dojo_fabricated_internal_duplicates.xml b/unittests/scans/zap/dvwa_baseline_dojo_fabricated_internal_duplicates.xml new file mode 100644 index 00000000000..09e56e93160 --- /dev/null +++ b/unittests/scans/zap/dvwa_baseline_dojo_fabricated_internal_duplicates.xml @@ -0,0 +1,833 @@ + + + 10020 + 10020 + X-Frame-Options Header Not Set + X-Frame-Options Header Not Set + 2 + 2 + Medium (Medium) + <p>X-Frame-Options header is not included in the HTTP response to protect against 'ClickJacking' attacks.</p> + + + http://172.17.0.2/vulnerabilities/brute/ + GET + X-Frame-Options + + + http://172.17.0.2/vulnerabilities/sqli_blind/ + GET + X-Frame-Options + + + http://172.17.0.2/vulnerabilities/exec/ + GET + X-Frame-Options + + + http://172.17.0.2/instructions.php + GET + X-Frame-Options + + + http://172.17.0.2/ + GET + X-Frame-Options + + + http://172.17.0.2/setup.php + GET + X-Frame-Options + + + http://172.17.0.2/vulnerabilities/upload/ + GET + X-Frame-Options + + + http://172.17.0.2/vulnerabilities/csrf/ + GET + X-Frame-Options + + + http://172.17.0.2/vulnerabilities/sqli/ + GET + X-Frame-Options + + + http://172.17.0.2/vulnerabilities/fi/?page=include.php + GET + X-Frame-Options + + + http://172.17.0.2/vulnerabilities/captcha/ + GET + X-Frame-Options + + + 11 + <p>Most modern Web browsers support the X-Frame-Options HTTP header. Ensure it's set on all web pages returned by your site (if you expect the page to be framed only by pages on your server (e.g. it's part of a FRAMESET) then you'll want to use SAMEORIGIN, otherwise if you never expect the page to be framed, you should use DENY. Alternatively consider implementing Content Security Policy's "frame-ancestors" directive. </p> + <p>https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options</p> + 16 + 15 + 3 + + + 10038 + 10038 + Content Security Policy (CSP) Header Not Set + Content Security Policy (CSP) Header Not Set + 2 + 3 + Medium (High) + <p>Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft to site defacement or distribution of malware. CSP provides a set of standard HTTP headers that allow website owners to declare approved sources of content that browsers should be allowed to load on that page — covered types are JavaScript, CSS, HTML frames, fonts, images and embeddable objects such as Java applets, ActiveX, audio and video files.</p> + + + http://172.17.0.2/vulnerabilities/fi/?page=include.php + GET + + + http://172.17.0.2/instructions.php + GET + + + http://172.17.0.2/vulnerabilities/sqli/ + GET + + + http://172.17.0.2/vulnerabilities/exec/ + GET + + + http://172.17.0.2/sitemap.xml + GET + + + http://172.17.0.2/setup.php + GET + + + http://172.17.0.2/ + GET + + + http://172.17.0.2/vulnerabilities/captcha/ + GET + + + http://172.17.0.2/vulnerabilities/upload/ + GET + + + http://172.17.0.2/vulnerabilities/brute/ + GET + + + http://172.17.0.2/vulnerabilities/csrf/ + GET + + + 11 + <p>Ensure that your web server, application server, load balancer, etc. is configured to set the Content-Security-Policy header, to achieve optimal browser support: "Content-Security-Policy" for Chrome 25+, Firefox 23+ and Safari 7+, "X-Content-Security-Policy" for Firefox 4.0+ and Internet Explorer 10+, and "X-WebKit-CSP" for Chrome 14+ and Safari 6+.</p> + <p>https://developer.mozilla.org/en-US/docs/Web/Security/CSP/Introducing_Content_Security_Policy</p><p>https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html</p><p>http://www.w3.org/TR/CSP/</p><p>http://w3c.github.io/webappsec/specs/content-security-policy/csp-specification.dev.html</p><p>http://www.html5rocks.com/en/tutorials/security/content-security-policy/</p><p>http://caniuse.com/#feat=contentsecuritypolicy</p><p>http://content-security-policy.com/</p> + 16 + 15 + 3 + + + 10038 + 10038 + Content Security Policy (CSP) Header Not Set + Content Security Policy (CSP) Header Not Set + 2 + 3 + Medium (High) + <p>Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft to site defacement or distribution of malware. CSP provides a set of standard HTTP headers that allow website owners to declare approved sources of content that browsers should be allowed to load on that page — covered types are JavaScript, CSS, HTML frames, fonts, images and embeddable objects such as Java applets, ActiveX, audio and video files.</p> + + + http://172.17.0.2/vulnerabilities/fi/?page=include.php + GET + + + http://172.17.0.2/instructions.php + GET + + + http://172.17.0.2/vulnerabilities/sqli/ + GET + + + http://172.17.0.2/vulnerabilities/exec/ + GET + + + http://172.17.0.2/sitemap.xml + GET + + + http://172.17.0.2/setup.php + GET + + + http://172.17.0.2/ + GET + + + http://172.17.0.2/vulnerabilities/captcha/ + GET + + + http://172.17.0.2/vulnerabilities/upload/ + GET + + + http://172.17.0.2/vulnerabilities/brute/ + GET + + + http://172.17.0.2/vulnerabilities/csrf/ + GET + + + 11 + <p>Ensure that your web server, application server, load balancer, etc. is configured to set the Content-Security-Policy header, to achieve optimal browser support: "Content-Security-Policy" for Chrome 25+, Firefox 23+ and Safari 7+, "X-Content-Security-Policy" for Firefox 4.0+ and Internet Explorer 10+, and "X-WebKit-CSP" for Chrome 14+ and Safari 6+.</p> + <p>https://developer.mozilla.org/en-US/docs/Web/Security/CSP/Introducing_Content_Security_Policy</p><p>https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html</p><p>http://www.w3.org/TR/CSP/</p><p>http://w3c.github.io/webappsec/specs/content-security-policy/csp-specification.dev.html</p><p>http://www.html5rocks.com/en/tutorials/security/content-security-policy/</p><p>http://caniuse.com/#feat=contentsecuritypolicy</p><p>http://content-security-policy.com/</p> + 16 + 15 + 3 + + + 10108 + 10108 + Reverse Tabnabbing + Reverse Tabnabbing + 2 + 2 + Medium (Medium) + <p>At least one link on this page is vulnerable to Reverse tabnabbing as it uses a target attribute without using both of the "noopener" and "noreferrer" keywords in the "rel" attribute, which allows the target page to take control of this page.</p> + + + http://172.17.0.2/vulnerabilities/brute/ + GET + <a href="https://www.owasp.org/index.php/Testing_for_Brute_Force_(OWASP-AT-004)" target="_blank">https://www.owasp.org/index.php/Testing_for_Brute_Force_(OWASP-AT-004)</a> + + + http://172.17.0.2/ + GET + <a href="https://www.virtualbox.org/" target="_blank">VirtualBox</a> + + + http://172.17.0.2/vulnerabilities/sqli/ + GET + <a href="http://www.securiteam.com/securityreviews/5DP0N1P76E.html" target="_blank">http://www.securiteam.com/securityreviews/5DP0N1P76E.html</a> + + + http://172.17.0.2/vulnerabilities/sqli_blind/ + GET + <a href="http://www.securiteam.com/securityreviews/5DP0N1P76E.html" target="_blank">http://www.securiteam.com/securityreviews/5DP0N1P76E.html</a> + + + http://172.17.0.2/vulnerabilities/xss_d/ + GET + <a href="https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)" target="_blank">https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)</a> + + + http://172.17.0.2/instructions.php + GET + <a href="https://www.virtualbox.org/" target="_blank">https://www.virtualbox.org/</a> + + + http://172.17.0.2/vulnerabilities/csrf/ + GET + <a href="https://www.owasp.org/index.php/Cross-Site_Request_Forgery" target="_blank">https://www.owasp.org/index.php/Cross-Site_Request_Forgery</a> + + + http://172.17.0.2/vulnerabilities/upload/ + GET + <a href="https://www.owasp.org/index.php/Unrestricted_File_Upload" target="_blank">https://www.owasp.org/index.php/Unrestricted_File_Upload</a> + + + http://172.17.0.2/vulnerabilities/fi/?page=include.php + GET + <a href="https://en.wikipedia.org/wiki/Remote_File_Inclusion" target="_blank">https://en.wikipedia.org/wiki/Remote_File_Inclusion</a> + + + http://172.17.0.2/vulnerabilities/captcha/ + GET + <a href="https://www.google.com/recaptcha/admin/create" target="_blank">https://www.google.com/recaptcha/admin/create</a> + + + http://172.17.0.2/vulnerabilities/exec/ + GET + <a href="http://www.scribd.com/doc/2530476/Php-Endangers-Remote-Code-Execution" target="_blank">http://www.scribd.com/doc/2530476/Php-Endangers-Remote-Code-Execution</a> + + + 11 + <p>Do not use a target attribute, or if you have to then also add the attribute: rel="noopener noreferrer".</p> + <p>https://owasp.org/www-community/attacks/Reverse_Tabnabbing</p><p>https://dev.to/ben/the-targetblank-vulnerability-by-example</p><p>https://mathiasbynens.github.io/rel-noopener/</p><p>https://medium.com/@jitbit/target-blank-the-most-underestimated-vulnerability-ever-96e328301f4c</p> + 3 + + + 10096 + 10096 + Timestamp Disclosure - Unix + Timestamp Disclosure - Unix + 0 + 1 + Informational (Low) + <p>A timestamp was disclosed by the application/web server - Unix</p> + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 1019803690 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 1839030562 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 909522486 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 722521979 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 40341101 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 1309151649 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 1732584194 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 405537848 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 1894986606 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 1473231341 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 155497632 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 1990404162 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 1700485571 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 1069501632 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 38016083 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 2022574463 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 373897302 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 1163531501 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 643717713 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 1444681467 + + + 67 + <p>Manually confirm that the timestamp data is not sensitive, and that the data cannot be aggregated to disclose exploitable patterns.</p> + <p>1019803690, which evaluates to: 2002-04-26 06:48:10</p> + <p>http://projects.webappsec.org/w/page/13246936/Information%20Leakage</p> + 200 + 13 + 3 + + + 10036 + 10036 + Server Leaks Version Information via "Server" HTTP Response Header Field + Server Leaks Version Information via "Server" HTTP Response Header Field + 1 + 3 + Low (High) + <p>The web/application server is leaking version information via the "Server" HTTP response header. Access to such information may facilitate attackers identifying other vulnerabilities your web/application server is subject to.</p> + + + http://172.17.0.2/vulnerabilities/brute/ + GET + Apache/2.4.25 (Debian) + + + http://172.17.0.2/vulnerabilities/captcha/ + GET + Apache/2.4.25 (Debian) + + + http://172.17.0.2/vulnerabilities/csrf/ + GET + Apache/2.4.25 (Debian) + + + http://172.17.0.2/instructions.php + GET + Apache/2.4.25 (Debian) + + + http://172.17.0.2/ + GET + Apache/2.4.25 (Debian) + + + http://172.17.0.2/setup.php + GET + Apache/2.4.25 (Debian) + + + http://172.17.0.2/vulnerabilities/upload/ + GET + Apache/2.4.25 (Debian) + + + http://172.17.0.2/robots.txt + GET + Apache/2.4.25 (Debian) + + + http://172.17.0.2/sitemap.xml + GET + Apache/2.4.25 (Debian) + + + http://172.17.0.2/vulnerabilities/fi/?page=include.php + GET + Apache/2.4.25 (Debian) + + + http://172.17.0.2/vulnerabilities/exec/ + GET + Apache/2.4.25 (Debian) + + + 11 + <p>Ensure that your web server, application server, load balancer, etc. is configured to suppress the "Server" header or provide generic details.</p> + <p>http://httpd.apache.org/docs/current/mod/core.html#servertokens</p><p>http://msdn.microsoft.com/en-us/library/ff648552.aspx#ht_urlscan_007</p><p>http://blogs.msdn.com/b/varunm/archive/2013/04/23/remove-unwanted-http-response-headers.aspx</p><p>http://www.troyhunt.com/2012/02/shhh-dont-let-your-response-headers.html</p> + 200 + 13 + 3 + + + 10202 + 10202 + Absence of Anti-CSRF Tokens + Absence of Anti-CSRF Tokens + 1 + 2 + Low (Medium) + <p>No Anti-CSRF tokens were found in a HTML submission form.</p><p>A cross-site request forgery is an attack that involves forcing a victim to send an HTTP request to a target destination without their knowledge or intent in order to perform an action as the victim. The underlying cause is application functionality using predictable URL/form actions in a repeatable way. The nature of the attack is that CSRF exploits the trust that a web site has for a user. By contrast, cross-site scripting (XSS) exploits the trust that a user has for a web site. Like XSS, CSRF attacks are not necessarily cross-site, but they can be. Cross-site request forgery is also known as CSRF, XSRF, one-click attack, session riding, confused deputy, and sea surf.</p><p></p><p>CSRF attacks are effective in a number of situations, including:</p><p> * The victim has an active session on the target site.</p><p> * The victim is authenticated via HTTP auth on the target site.</p><p> * The victim is on the same local network as the target site.</p><p></p><p>CSRF has primarily been used to perform an action against a target site using the victim's privileges, but recent techniques have been discovered to disclose information by gaining access to the response. The risk of information disclosure is dramatically increased when the target site is vulnerable to XSS, because XSS can be used as a platform for CSRF, allowing the attack to operate within the bounds of the same-origin policy.</p> + + + http://172.17.0.2/vulnerabilities/xss_d/ + GET + <form name="XSS" method="GET"> + + + http://172.17.0.2/vulnerabilities/captcha/ + GET + <form action="#" method="POST" style="display:none;"> + + + http://172.17.0.2/vulnerabilities/xss_s/ + GET + <form method="post" name="guestform" "> + + + http://172.17.0.2/vulnerabilities/csrf/ + GET + <form action="#" method="GET"> + + + http://172.17.0.2/setup.php + GET + <form action="#" method="post"> + + + http://172.17.0.2/vulnerabilities/brute/ + GET + <form action="#" method="GET"> + + + http://172.17.0.2/vulnerabilities/sqli/ + GET + <form action="#" method="GET"> + + + http://172.17.0.2/vulnerabilities/weak_id/ + GET + <form method="post"> + + + http://172.17.0.2/vulnerabilities/exec/ + GET + <form name="ping" action="#" method="post"> + + + http://172.17.0.2/vulnerabilities/sqli_blind/ + GET + <form action="#" method="GET"> + + + http://172.17.0.2/vulnerabilities/xss_r/ + GET + <form name="XSS" action="#" method="GET"> + + + http://172.17.0.2/vulnerabilities/upload/ + GET + <form enctype="multipart/form-data" action="#" method="POST"> + + + 12 + <p>Phase: Architecture and Design</p><p>Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid.</p><p>For example, use anti-CSRF packages such as the OWASP CSRFGuard.</p><p></p><p>Phase: Implementation</p><p>Ensure that your application is free of cross-site scripting issues, because most CSRF defenses can be bypassed using attacker-controlled script.</p><p></p><p>Phase: Architecture and Design</p><p>Generate a unique nonce for each form, place the nonce into the form, and verify the nonce upon receipt of the form. Be sure that the nonce is not predictable (CWE-330).</p><p>Note that this can be bypassed using XSS.</p><p></p><p>Identify especially dangerous operations. When the user performs a dangerous operation, send a separate confirmation request to ensure that the user intended to perform that operation.</p><p>Note that this can be bypassed using XSS.</p><p></p><p>Use the ESAPI Session Management control.</p><p>This control includes a component for CSRF.</p><p></p><p>Do not use the GET method for any request that triggers a state change.</p><p></p><p>Phase: Implementation</p><p>Check the HTTP Referer header to see if the request originated from an expected page. This could break legitimate functionality, because users or proxies may have disabled sending the Referer for privacy reasons.</p> + <p>No known Anti-CSRF token [anticsrf, CSRFToken, __RequestVerificationToken, csrfmiddlewaretoken, authenticity_token, OWASP_CSRFTOKEN, anoncsrf, csrf_token, _csrf, _csrfSecret, __csrf_magic, CSRF] was found in the following HTML form: [Form 1: "" ].</p> + <p>http://projects.webappsec.org/Cross-Site-Request-Forgery</p><p>http://cwe.mitre.org/data/definitions/352.html</p> + 352 + 9 + 3 + + 10202 + 10202 + Absence of Anti-CSRF Tokens + Absence of Anti-CSRF Tokens + 1 + 2 + Low (Medium) + <p>No Anti-CSRF tokens were found in a HTML submission form.</p><p>A cross-site request forgery is an attack that involves forcing a victim to send an HTTP request to a target destination without their knowledge or intent in order to perform an action as the victim. The underlying cause is application functionality using predictable URL/form actions in a repeatable way. The nature of the attack is that CSRF exploits the trust that a web site has for a user. By contrast, cross-site scripting (XSS) exploits the trust that a user has for a web site. Like XSS, CSRF attacks are not necessarily cross-site, but they can be. Cross-site request forgery is also known as CSRF, XSRF, one-click attack, session riding, confused deputy, and sea surf.</p><p></p><p>CSRF attacks are effective in a number of situations, including:</p><p> * The victim has an active session on the target site.</p><p> * The victim is authenticated via HTTP auth on the target site.</p><p> * The victim is on the same local network as the target site.</p><p></p><p>CSRF has primarily been used to perform an action against a target site using the victim's privileges, but recent techniques have been discovered to disclose information by gaining access to the response. The risk of information disclosure is dramatically increased when the target site is vulnerable to XSS, because XSS can be used as a platform for CSRF, allowing the attack to operate within the bounds of the same-origin policy.</p> + + + http://172.17.0.2/vulnerabilities/xss_d/ + GET + <form name="XSS" method="GET"> + + + http://172.17.0.2/vulnerabilities/captcha/ + GET + <form action="#" method="POST" style="display:none;"> + + + http://172.17.0.2/vulnerabilities/xss_s/ + GET + <form method="post" name="guestform" "> + + + http://172.17.0.2/vulnerabilities/csrf/ + GET + <form action="#" method="GET"> + + + http://172.17.0.2/setup.php + GET + <form action="#" method="post"> + + + http://172.17.0.2/vulnerabilities/brute/ + GET + <form action="#" method="GET"> + + + http://172.17.0.2/vulnerabilities/sqli/ + GET + <form action="#" method="GET"> + + + http://172.17.0.2/vulnerabilities/weak_id/ + GET + <form method="post"> + + + http://172.17.0.2/vulnerabilities/exec/ + GET + <form name="ping" action="#" method="post"> + + + http://172.17.0.2/vulnerabilities/sqli_blind/ + GET + <form action="#" method="GET"> + + + http://172.17.0.2/vulnerabilities/xss_r/ + GET + <form name="XSS" action="#" method="GET"> + + + http://172.17.0.2/vulnerabilities/upload/ + GET + <form enctype="multipart/form-data" action="#" method="POST"> + + + 12 + <p>Phase: Architecture and Design</p><p>Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid.</p><p>For example, use anti-CSRF packages such as the OWASP CSRFGuard.</p><p></p><p>Phase: Implementation</p><p>Ensure that your application is free of cross-site scripting issues, because most CSRF defenses can be bypassed using attacker-controlled script.</p><p></p><p>Phase: Architecture and Design</p><p>Generate a unique nonce for each form, place the nonce into the form, and verify the nonce upon receipt of the form. Be sure that the nonce is not predictable (CWE-330).</p><p>Note that this can be bypassed using XSS.</p><p></p><p>Identify especially dangerous operations. When the user performs a dangerous operation, send a separate confirmation request to ensure that the user intended to perform that operation.</p><p>Note that this can be bypassed using XSS.</p><p></p><p>Use the ESAPI Session Management control.</p><p>This control includes a component for CSRF.</p><p></p><p>Do not use the GET method for any request that triggers a state change.</p><p></p><p>Phase: Implementation</p><p>Check the HTTP Referer header to see if the request originated from an expected page. This could break legitimate functionality, because users or proxies may have disabled sending the Referer for privacy reasons.</p> + <p>No known Anti-CSRF token [anticsrf, CSRFToken, __RequestVerificationToken, csrfmiddlewaretoken, authenticity_token, OWASP_CSRFTOKEN, anoncsrf, csrf_token, _csrf, _csrfSecret, __csrf_magic, CSRF] was found in the following HTML form: [Form 1: "" ].</p> + <p>http://projects.webappsec.org/Cross-Site-Request-Forgery</p><p>http://cwe.mitre.org/data/definitions/352.html</p> + 352 + 9 + 3 + + + 10031 + 10031 + User Controllable HTML Element Attribute (Potential XSS) + User Controllable HTML Element Attribute (Potential XSS) + 0 + 1 + Informational (Low) + <p>This check looks at user-supplied input in query string parameters and POST data to identify where certain HTML attribute values might be controlled. This provides hot-spot detection for XSS (cross-site scripting) that will require further review by a security analyst to determine exploitability.</p> + + + http://172.17.0.2/vulnerabilities/captcha/ + POST + Change + + + http://172.17.0.2/vulnerabilities/exec/ + POST + Submit + + + http://172.17.0.2/vulnerabilities/csrf/?Change=Change&password_conf=ZAP&password_new=ZAP + GET + Change + + + http://172.17.0.2/vulnerabilities/captcha/ + POST + Change + + + http://172.17.0.2/vulnerabilities/exec/ + POST + Submit + + + http://172.17.0.2/vulnerabilities/brute/?Login=Login&password=ZAP&username=ZAP + GET + Login + + + http://172.17.0.2/vulnerabilities/upload/ + POST + MAX_FILE_SIZE + + + http://172.17.0.2/vulnerabilities/upload/ + POST + Upload + + + http://172.17.0.2/vulnerabilities/upload/ + POST + Upload + + + http://172.17.0.2/vulnerabilities/upload/ + POST + Upload + + + http://172.17.0.2/vulnerabilities/brute/?Login=Login&password=ZAP&username=ZAP + GET + Login + + + http://172.17.0.2/vulnerabilities/csrf/?Change=Change&password_conf=ZAP&password_new=ZAP + GET + Change + + + http://172.17.0.2/vulnerabilities/exec/ + POST + Submit + + + 13 + <p>Validate all input and sanitize output it before writing to any HTML attributes.</p> + <p>User-controlled HTML attribute values were found. Try injecting special characters to see if XSS might be possible. The page at the following URL:</p><p></p><p>http://172.17.0.2/vulnerabilities/captcha/</p><p></p><p>appears to include user input in: </p><p></p><p>a(n) [input] tag [name] attribute </p><p></p><p>The user input found was:</p><p>Change=Change</p><p></p><p>The user-controlled value was:</p><p>change</p> + <p>http://websecuritytool.codeplex.com/wikipage?title=Checks#user-controlled-html-attribute</p> + 20 + 20 + 3 + + + 10024 + 10024 + Information Disclosure - Sensitive Information in URL + Information Disclosure - Sensitive Information in URL + 0 + 2 + Informational (Medium) + <p>The request appeared to contain sensitive information leaked in the URL. This can violate PCI and most organizational compliance policies. You can configure the list of strings for this check to add or remove values specific to your environment.</p> + + + http://172.17.0.2/vulnerabilities/brute/?Login=Login&password=ZAP&username=ZAP + GET + username + username + + + http://172.17.0.2/vulnerabilities/csrf/?Change=Change&password_conf=ZAP&password_new=ZAP + GET + password_conf + password_conf + + + http://172.17.0.2/vulnerabilities/brute/?Login=Login&password=ZAP&username=ZAP + GET + password + password + + + http://172.17.0.2/vulnerabilities/csrf/?Change=Change&password_conf=ZAP&password_new=ZAP + GET + password_new + password_new + + + 4 + <p>Do not pass sensitive information in URIs.</p> + <p>The URL contains potentially sensitive information. The following string was found via the pattern: user</p><p>username</p> + <p></p> + 200 + 13 + 3 + + + 10054 + 10054 + Cookie Without SameSite Attribute + Cookie Without SameSite Attribute + 1 + 2 + Low (Medium) + <p>A cookie has been set without the SameSite attribute, which means that the cookie can be sent as a result of a 'cross-site' request. The SameSite attribute is an effective counter measure to cross-site request forgery, cross-site script inclusion, and timing attacks.</p> + + + http://172.17.0.2/security.php + POST + + + http://172.17.0.2/security.php + POST + PHPSESSID + Set-Cookie: PHPSESSID + + + http://172.17.0.2/vulnerabilities/weak_id/ + POST + + + 3 + <p>Ensure that the SameSite attribute is set to either 'lax' or ideally 'strict' for all cookies.</p> + <p>https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site</p> + 16 + 13 + 3 + + 10054 + 10054 + Cookie Without SameSite Attribute + Cookie Without SameSite Attribute + 1 + 2 + Low (Medium) + <p>A cookie has been set without the SameSite attribute, which means that the cookie can be sent as a result of a 'cross-site' request. The SameSite attribute is an effective counter measure to cross-site request forgery, cross-site script inclusion, and timing attacks.</p> + + + http://172.17.0.2/security.php + POST + + + http://172.17.0.2/security.php + POST + PHPSESSID + Set-Cookie: PHPSESSID + + + http://172.17.0.2/vulnerabilities/weak_id/ + POST + + + 3 + <p>Ensure that the SameSite attribute is set to either 'lax' or ideally 'strict' for all cookies.</p> + <p>https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site</p> + 16 + 13 + 3 + + + 10029 + 10029 + Cookie Poisoning + Cookie Poisoning + 0 + 1 + Informational (Low) + <p>This check looks at user-supplied input in query string parameters and POST data to identify where cookie parameters might be controlled. This is called a cookie poisoning attack, and becomes exploitable when an attacker can manipulate the cookie in various ways. In some cases this will not be exploitable, however, allowing URL parameters to set cookie values is generally considered a bug.</p> + + + http://172.17.0.2/security.php + POST + security + + + 1 + <p>Do not allow user input to control cookie names and values. If some query string parameters must be set in cookie values, be sure to filter out semicolon's that can serve as name/value pair delimiters.</p> + <p>An attacker may be able to poison cookie values through POST parameters. To test if this is a more serious issue, you should try resending that request as a GET, with the POST parameter included as a query string parameter. For example: http://nottrusted.com/page?value=maliciousInput.</p><p></p><p>This was identified at:</p><p></p><p>http://172.17.0.2/security.php</p><p></p><p>User-input was found in the following cookie:</p><p>security=low</p><p></p><p>The user input was:</p><p>security=low</p> + <p>http://websecuritytool.codeplex.com/wikipage?title=Checks#user-controlled-cookie</p> + 20 + 20 + 3 + + diff --git a/unittests/scans/zap/dvwa_baseline_dojo_fabricated_internal_duplicates_subset.xml b/unittests/scans/zap/dvwa_baseline_dojo_fabricated_internal_duplicates_subset.xml new file mode 100644 index 00000000000..e67280969c8 --- /dev/null +++ b/unittests/scans/zap/dvwa_baseline_dojo_fabricated_internal_duplicates_subset.xml @@ -0,0 +1,462 @@ + + + 10020 + 10020 + X-Frame-Options Header Not Set + X-Frame-Options Header Not Set + 2 + 2 + Medium (Medium) + <p>X-Frame-Options header is not included in the HTTP response to protect against 'ClickJacking' attacks.</p> + + + http://172.17.0.2/vulnerabilities/brute/ + GET + X-Frame-Options + + + http://172.17.0.2/vulnerabilities/sqli_blind/ + GET + X-Frame-Options + + + http://172.17.0.2/vulnerabilities/exec/ + GET + X-Frame-Options + + + http://172.17.0.2/instructions.php + GET + X-Frame-Options + + + http://172.17.0.2/ + GET + X-Frame-Options + + + http://172.17.0.2/setup.php + GET + X-Frame-Options + + + http://172.17.0.2/vulnerabilities/upload/ + GET + X-Frame-Options + + + http://172.17.0.2/vulnerabilities/csrf/ + GET + X-Frame-Options + + + http://172.17.0.2/vulnerabilities/sqli/ + GET + X-Frame-Options + + + http://172.17.0.2/vulnerabilities/fi/?page=include.php + GET + X-Frame-Options + + + http://172.17.0.2/vulnerabilities/captcha/ + GET + X-Frame-Options + + + 11 + <p>Most modern Web browsers support the X-Frame-Options HTTP header. Ensure it's set on all web pages returned by your site (if you expect the page to be framed only by pages on your server (e.g. it's part of a FRAMESET) then you'll want to use SAMEORIGIN, otherwise if you never expect the page to be framed, you should use DENY. Alternatively consider implementing Content Security Policy's "frame-ancestors" directive. </p> + <p>https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options</p> + 16 + 15 + 3 + + + 10038 + 10038 + Content Security Policy (CSP) Header Not Set + Content Security Policy (CSP) Header Not Set + 2 + 3 + Medium (High) + <p>Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft to site defacement or distribution of malware. CSP provides a set of standard HTTP headers that allow website owners to declare approved sources of content that browsers should be allowed to load on that page — covered types are JavaScript, CSS, HTML frames, fonts, images and embeddable objects such as Java applets, ActiveX, audio and video files.</p> + + + http://172.17.0.2/vulnerabilities/fi/?page=include.php + GET + + + http://172.17.0.2/instructions.php + GET + + + http://172.17.0.2/vulnerabilities/sqli/ + GET + + + http://172.17.0.2/vulnerabilities/exec/ + GET + + + http://172.17.0.2/sitemap.xml + GET + + + http://172.17.0.2/setup.php + GET + + + http://172.17.0.2/ + GET + + + http://172.17.0.2/vulnerabilities/captcha/ + GET + + + http://172.17.0.2/vulnerabilities/upload/ + GET + + + http://172.17.0.2/vulnerabilities/brute/ + GET + + + http://172.17.0.2/vulnerabilities/csrf/ + GET + + + 11 + <p>Ensure that your web server, application server, load balancer, etc. is configured to set the Content-Security-Policy header, to achieve optimal browser support: "Content-Security-Policy" for Chrome 25+, Firefox 23+ and Safari 7+, "X-Content-Security-Policy" for Firefox 4.0+ and Internet Explorer 10+, and "X-WebKit-CSP" for Chrome 14+ and Safari 6+.</p> + <p>https://developer.mozilla.org/en-US/docs/Web/Security/CSP/Introducing_Content_Security_Policy</p><p>https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html</p><p>http://www.w3.org/TR/CSP/</p><p>http://w3c.github.io/webappsec/specs/content-security-policy/csp-specification.dev.html</p><p>http://www.html5rocks.com/en/tutorials/security/content-security-policy/</p><p>http://caniuse.com/#feat=contentsecuritypolicy</p><p>http://content-security-policy.com/</p> + 16 + 15 + 3 + + + 10038 + 10038 + Content Security Policy (CSP) Header Not Set + Content Security Policy (CSP) Header Not Set + 2 + 3 + Medium (High) + <p>Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft to site defacement or distribution of malware. CSP provides a set of standard HTTP headers that allow website owners to declare approved sources of content that browsers should be allowed to load on that page — covered types are JavaScript, CSS, HTML frames, fonts, images and embeddable objects such as Java applets, ActiveX, audio and video files.</p> + + + http://172.17.0.2/vulnerabilities/fi/?page=include.php + GET + + + http://172.17.0.2/instructions.php + GET + + + http://172.17.0.2/vulnerabilities/sqli/ + GET + + + http://172.17.0.2/vulnerabilities/exec/ + GET + + + http://172.17.0.2/sitemap.xml + GET + + + http://172.17.0.2/setup.php + GET + + + http://172.17.0.2/ + GET + + + http://172.17.0.2/vulnerabilities/captcha/ + GET + + + http://172.17.0.2/vulnerabilities/upload/ + GET + + + http://172.17.0.2/vulnerabilities/brute/ + GET + + + http://172.17.0.2/vulnerabilities/csrf/ + GET + + + 11 + <p>Ensure that your web server, application server, load balancer, etc. is configured to set the Content-Security-Policy header, to achieve optimal browser support: "Content-Security-Policy" for Chrome 25+, Firefox 23+ and Safari 7+, "X-Content-Security-Policy" for Firefox 4.0+ and Internet Explorer 10+, and "X-WebKit-CSP" for Chrome 14+ and Safari 6+.</p> + <p>https://developer.mozilla.org/en-US/docs/Web/Security/CSP/Introducing_Content_Security_Policy</p><p>https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html</p><p>http://www.w3.org/TR/CSP/</p><p>http://w3c.github.io/webappsec/specs/content-security-policy/csp-specification.dev.html</p><p>http://www.html5rocks.com/en/tutorials/security/content-security-policy/</p><p>http://caniuse.com/#feat=contentsecuritypolicy</p><p>http://content-security-policy.com/</p> + 16 + 15 + 3 + + + 10108 + 10108 + Reverse Tabnabbing + Reverse Tabnabbing + 2 + 2 + Medium (Medium) + <p>At least one link on this page is vulnerable to Reverse tabnabbing as it uses a target attribute without using both of the "noopener" and "noreferrer" keywords in the "rel" attribute, which allows the target page to take control of this page.</p> + + + http://172.17.0.2/vulnerabilities/brute/ + GET + <a href="https://www.owasp.org/index.php/Testing_for_Brute_Force_(OWASP-AT-004)" target="_blank">https://www.owasp.org/index.php/Testing_for_Brute_Force_(OWASP-AT-004)</a> + + + http://172.17.0.2/ + GET + <a href="https://www.virtualbox.org/" target="_blank">VirtualBox</a> + + + http://172.17.0.2/vulnerabilities/sqli/ + GET + <a href="http://www.securiteam.com/securityreviews/5DP0N1P76E.html" target="_blank">http://www.securiteam.com/securityreviews/5DP0N1P76E.html</a> + + + http://172.17.0.2/vulnerabilities/sqli_blind/ + GET + <a href="http://www.securiteam.com/securityreviews/5DP0N1P76E.html" target="_blank">http://www.securiteam.com/securityreviews/5DP0N1P76E.html</a> + + + http://172.17.0.2/vulnerabilities/xss_d/ + GET + <a href="https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)" target="_blank">https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)</a> + + + http://172.17.0.2/instructions.php + GET + <a href="https://www.virtualbox.org/" target="_blank">https://www.virtualbox.org/</a> + + + http://172.17.0.2/vulnerabilities/csrf/ + GET + <a href="https://www.owasp.org/index.php/Cross-Site_Request_Forgery" target="_blank">https://www.owasp.org/index.php/Cross-Site_Request_Forgery</a> + + + http://172.17.0.2/vulnerabilities/upload/ + GET + <a href="https://www.owasp.org/index.php/Unrestricted_File_Upload" target="_blank">https://www.owasp.org/index.php/Unrestricted_File_Upload</a> + + + http://172.17.0.2/vulnerabilities/fi/?page=include.php + GET + <a href="https://en.wikipedia.org/wiki/Remote_File_Inclusion" target="_blank">https://en.wikipedia.org/wiki/Remote_File_Inclusion</a> + + + http://172.17.0.2/vulnerabilities/captcha/ + GET + <a href="https://www.google.com/recaptcha/admin/create" target="_blank">https://www.google.com/recaptcha/admin/create</a> + + + http://172.17.0.2/vulnerabilities/exec/ + GET + <a href="http://www.scribd.com/doc/2530476/Php-Endangers-Remote-Code-Execution" target="_blank">http://www.scribd.com/doc/2530476/Php-Endangers-Remote-Code-Execution</a> + + + 11 + <p>Do not use a target attribute, or if you have to then also add the attribute: rel="noopener noreferrer".</p> + <p>https://owasp.org/www-community/attacks/Reverse_Tabnabbing</p><p>https://dev.to/ben/the-targetblank-vulnerability-by-example</p><p>https://mathiasbynens.github.io/rel-noopener/</p><p>https://medium.com/@jitbit/target-blank-the-most-underestimated-vulnerability-ever-96e328301f4c</p> + 3 + + + 10096 + 10096 + Timestamp Disclosure - Unix + Timestamp Disclosure - Unix + 0 + 1 + Informational (Low) + <p>A timestamp was disclosed by the application/web server - Unix</p> + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 1019803690 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 1839030562 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 909522486 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 722521979 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 40341101 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 1309151649 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 1732584194 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 405537848 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 1894986606 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 1473231341 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 155497632 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 1990404162 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 1700485571 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 1069501632 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 38016083 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 2022574463 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 373897302 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 1163531501 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 643717713 + + + http://172.17.0.2/vulnerabilities/javascript/ + GET + 1444681467 + + + 67 + <p>Manually confirm that the timestamp data is not sensitive, and that the data cannot be aggregated to disclose exploitable patterns.</p> + <p>1019803690, which evaluates to: 2002-04-26 06:48:10</p> + <p>http://projects.webappsec.org/w/page/13246936/Information%20Leakage</p> + 200 + 13 + 3 + + + 10036 + 10036 + Server Leaks Version Information via "Server" HTTP Response Header Field + Server Leaks Version Information via "Server" HTTP Response Header Field + 1 + 3 + Low (High) + <p>The web/application server is leaking version information via the "Server" HTTP response header. Access to such information may facilitate attackers identifying other vulnerabilities your web/application server is subject to.</p> + + + http://172.17.0.2/vulnerabilities/brute/ + GET + Apache/2.4.25 (Debian) + + + http://172.17.0.2/vulnerabilities/captcha/ + GET + Apache/2.4.25 (Debian) + + + http://172.17.0.2/vulnerabilities/csrf/ + GET + Apache/2.4.25 (Debian) + + + http://172.17.0.2/instructions.php + GET + Apache/2.4.25 (Debian) + + + http://172.17.0.2/ + GET + Apache/2.4.25 (Debian) + + + http://172.17.0.2/setup.php + GET + Apache/2.4.25 (Debian) + + + http://172.17.0.2/vulnerabilities/upload/ + GET + Apache/2.4.25 (Debian) + + + http://172.17.0.2/robots.txt + GET + Apache/2.4.25 (Debian) + + + http://172.17.0.2/sitemap.xml + GET + Apache/2.4.25 (Debian) + + + http://172.17.0.2/vulnerabilities/fi/?page=include.php + GET + Apache/2.4.25 (Debian) + + + http://172.17.0.2/vulnerabilities/exec/ + GET + Apache/2.4.25 (Debian) + + + 11 + <p>Ensure that your web server, application server, load balancer, etc. is configured to suppress the "Server" header or provide generic details.</p> + <p>http://httpd.apache.org/docs/current/mod/core.html#servertokens</p><p>http://msdn.microsoft.com/en-us/library/ff648552.aspx#ht_urlscan_007</p><p>http://blogs.msdn.com/b/varunm/archive/2013/04/23/remove-unwanted-http-response-headers.aspx</p><p>http://www.troyhunt.com/2012/02/shhh-dont-let-your-response-headers.html</p> + 200 + 13 + 3 + + diff --git a/unittests/test_importers_deduplication.py b/unittests/test_importers_deduplication.py index f418a624d66..bad44a7f974 100644 --- a/unittests/test_importers_deduplication.py +++ b/unittests/test_importers_deduplication.py @@ -292,154 +292,261 @@ def _test_same_product_different_engagements_dedupe_on_engagements_no_duplicates return second_test.id - # Test cases for ZAP (LEGACY algorithm) - def test_zap_single_import_no_duplicates(self): - """Test that importing ZAP scan (LEGACY algorithm) creates 0 duplicate findings""" - self._test_single_import_assess_duplicates("scans/zap/dvwa_baseline_dojo_subset.xml", "ZAP Scan", 0) - - def test_zap_full_then_subset_duplicates(self): - """Test that importing full ZAP scan then subset creates duplicates""" - # For now, use the same file for both full and subset since we don't have a proper subset - # This will test the same file imported twice into the same engagement - self._test_full_then_subset_duplicates("scans/zap/dvwa_baseline_dojo_subset.xml", "scans/zap/dvwa_baseline_dojo_subset.xml", "ZAP Scan", 10) - - def test_zap_different_products_no_duplicates(self): - """Test that importing ZAP scan into different products creates 0 duplicates""" - self._test_different_products_no_duplicates("scans/zap/dvwa_baseline_dojo_subset.xml", "ZAP Scan", 0) - - def test_zap_same_product_different_engagements_duplicates(self): - """Test that importing ZAP scan into same product but different engagements creates duplicates""" - self._test_same_product_different_engagements_duplicates("scans/zap/dvwa_baseline_dojo_subset.xml", "ZAP Scan", 10) - - def test_zap_same_product_different_engagements_dedupe_on_engagements_no_duplicates(self): - """Test that importing ZAP scan into same product but different engagements with dedupe_on_engagements creates 0 duplicates""" - self._test_same_product_different_engagements_dedupe_on_engagements_no_duplicates("scans/zap/dvwa_baseline_dojo_subset.xml", "ZAP Scan", 0) - - # Test cases for Checkmarx (UNIQUE_ID_FROM_TOOL algorithm) - def test_checkmarx_single_import_no_duplicates(self): - """Test that importing Checkmarx scan (UNIQUE_ID_FROM_TOOL algorithm) creates 0 duplicate findings""" - self._test_single_import_assess_duplicates("scans/checkmarx/multiple_findings.json", "Checkmarx Scan detailed", 0) - - def test_checkmarx_full_then_subset_duplicates(self): - """Test that importing full Checkmarx scan then subset creates duplicates""" - # For now, use the same file for both full and subset - self._test_full_then_subset_duplicates("scans/checkmarx/multiple_findings.json", "scans/checkmarx/multiple_findings.json", - "Checkmarx Scan detailed", 10) - - def test_checkmarx_different_products_no_duplicates(self): - """Test that importing Checkmarx scan into different products creates 0 duplicates""" - self._test_different_products_no_duplicates("scans/checkmarx/multiple_findings.json", "Checkmarx Scan detailed", 0) - - def test_checkmarx_same_product_different_engagements_duplicates(self): - """Test that importing Checkmarx scan into same product but different engagements creates duplicates""" - self._test_same_product_different_engagements_duplicates("scans/checkmarx/multiple_findings.json", "Checkmarx Scan detailed", 10) - - def test_checkmarx_same_product_different_engagements_dedupe_on_engagements_no_duplicates(self): - """Test that importing Checkmarx scan into same product but different engagements with dedupe_on_engagements creates 0 duplicates""" - self._test_same_product_different_engagements_dedupe_on_engagements_no_duplicates("scans/checkmarx/multiple_findings.json", - "Checkmarx Scan detailed", 0) - - # Test cases for Trivy (HASH_CODE algorithm) - def test_trivy_single_import_no_duplicates(self): - """Test that importing Trivy scan (HASH_CODE algorithm) creates 0 duplicate findings""" - self._test_single_import_assess_duplicates("scans/trivy/kubernetes.json", "Trivy Scan", 0) - - def test_trivy_full_then_subset_duplicates(self): - """Test that importing full Trivy scan then subset creates duplicates""" - # For now, use the same file for both full and subset - self._test_full_then_subset_duplicates("scans/trivy/kubernetes.json", "scans/trivy/kubernetes.json", "Trivy Scan", 20) - - def test_trivy_different_products_no_duplicates(self): - """Test that importing Trivy scan into different products creates 0 duplicates""" - self._test_different_products_no_duplicates("scans/trivy/kubernetes.json", "Trivy Scan", 0) - - def test_trivy_same_product_different_engagements_duplicates(self): - """Test that importing Trivy scan into same product but different engagements creates duplicates""" - self._test_same_product_different_engagements_duplicates("scans/trivy/kubernetes.json", "Trivy Scan", 20) - - def test_trivy_same_product_different_engagements_dedupe_on_engagements_no_duplicates(self): - """Test that importing Trivy scan into same product but different engagements with dedupe_on_engagements creates 0 duplicates""" - self._test_same_product_different_engagements_dedupe_on_engagements_no_duplicates("scans/trivy/kubernetes.json", "Trivy Scan", 0) - - # Test cases for SARIF (UNIQUE_ID_FROM_TOOL_OR_HASH_CODE algorithm) - # The samples for SARIF is the bash report that has internal duplicates - # These are used on purpose so we capture the behaviour of import and reimport in this scenario. - def test_sarif_single_import_no_duplicates(self): - """Test that importing SARIF scan (UNIQUE_ID_FROM_TOOL_OR_HASH_CODE algorithm) creates 0 duplicate findings""" - # bash-report.sarif has 18 internal duplicates, so we expect 18 duplicates even on first import - test_id = self._test_single_import_assess_duplicates("scans/sarif/bash-report.sarif", "SARIF", 18) - - # duplicates should be sorted by id (currently not usefull as tests are running celery tasks in the foreground) - for finding in Finding.objects.filter(test_id=test_id, duplicate=True): - self.assertTrue(finding.duplicate_finding.id < finding.id) - - def test_sarif_full_then_subset_duplicates(self): - """Test that importing full SARIF scan then subset creates duplicates""" - # For now, use the same file for both full and subset - # First import has 18 internal duplicates, second import also has 18 internal duplicates + 9 cross-import duplicates = 27 total in second test - # Total = 18 (first) + 27 (second) = 45 - self._test_full_then_subset_duplicates("scans/sarif/bash-report.sarif", "scans/sarif/bash-report.sarif", "SARIF", 27, first_import_duplicates=18) - - def test_sarif_different_products_no_duplicates(self): - """Test that importing SARIF scan into different products creates 0 duplicates""" - # bash-report.sarif has 18 internal duplicates per import - self._test_different_products_no_duplicates("scans/sarif/bash-report.sarif", "SARIF", 18) - - def test_sarif_same_product_different_engagements_duplicates(self): - """Test that importing SARIF scan into same product but different engagements creates duplicates""" - # 18 internal duplicates in first import + 18 in second import + 9 cross-import duplicates = 45 total - self._test_same_product_different_engagements_duplicates("scans/sarif/bash-report.sarif", "SARIF", 45) - - def test_sarif_same_product_different_engagements_dedupe_on_engagements_no_duplicates(self): - """Test that importing SARIF scan into same product but different engagements with dedupe_on_engagements creates 0 duplicates""" - # bash-report.sarif has 18 internal duplicates per import - # Second test has 18 internal duplicates (no cross-engagement duplicates due to dedupe_on_engagements=True) - # Total product duplicates = 18 (first) + 18 (second) = 36 - self._test_same_product_different_engagements_dedupe_on_engagements_no_duplicates("scans/sarif/bash-report.sarif", "SARIF", - 18, first_import_duplicates=18) - - # Test cases for Veracode (UNIQUE_ID_FROM_TOOL_OR_HASH_CODE algorithm) - def test_veracode_single_import_no_duplicates(self): - """Test that importing Veracode scan (UNIQUE_ID_FROM_TOOL_OR_HASH_CODE algorithm) creates 0 duplicate findings""" - self._test_single_import_assess_duplicates("scans/veracode/veracode_scan.xml", "Veracode Scan", 0) - - def test_veracode_full_then_subset_duplicates(self): - """Test that importing full Veracode scan then subset creates duplicates""" - # For now, use the same file for both full and subset - self._test_full_then_subset_duplicates("scans/veracode/veracode_scan.xml", "scans/veracode/veracode_scan.xml", "Veracode Scan", 7) - - def test_veracode_different_products_no_duplicates(self): - """Test that importing Veracode scan into different products creates 0 duplicates""" - self._test_different_products_no_duplicates("scans/veracode/veracode_scan.xml", "Veracode Scan", 0) - - def test_veracode_same_product_different_engagements_duplicates(self): - """Test that importing Veracode scan into same product but different engagements creates duplicates""" - self._test_same_product_different_engagements_duplicates("scans/veracode/veracode_scan.xml", "Veracode Scan", 7) - - def test_veracode_same_product_different_engagements_dedupe_on_engagements_no_duplicates(self): - """Test that importing Veracode scan into same product but different engagements with dedupe_on_engagements creates 0 duplicates""" - self._test_same_product_different_engagements_dedupe_on_engagements_no_duplicates("scans/veracode/veracode_scan.xml", "Veracode Scan", 0) - - # Test cases for StackHawk (HASH_CODE algorithm) - def test_stackhawk_single_import_no_duplicates(self): - """Test that importing StackHawk scan (HASH_CODE algorithm) creates 0 duplicate findings""" - self._test_single_import_assess_duplicates("scans/stackhawk/stackhawk_many_vul_without_duplicated_findings.json", "StackHawk HawkScan", 0) - - def test_stackhawk_full_then_subset_duplicates(self): - """Test that importing full StackHawk scan then subset creates duplicates""" - self._test_full_then_subset_duplicates("scans/stackhawk/stackhawk_many_vul_without_duplicated_findings.json", - "scans/stackhawk/stackhawk_many_vul_without_duplicated_findings_subset.json", "StackHawk HawkScan", 5) - - def test_stackhawk_different_products_no_duplicates(self): - """Test that importing StackHawk scan into different products creates 0 duplicates""" - self._test_different_products_no_duplicates("scans/stackhawk/stackhawk_many_vul_without_duplicated_findings.json", "StackHawk HawkScan", 0) - - def test_stackhawk_same_product_different_engagements_duplicates(self): - """Test that importing StackHawk scan into same product but different engagements creates duplicates""" - self._test_same_product_different_engagements_duplicates("scans/stackhawk/stackhawk_many_vul_without_duplicated_findings.json", - "StackHawk HawkScan", 6) - - def test_stackhawk_same_product_different_engagements_dedupe_on_engagements_no_duplicates(self): - """Test that importing StackHawk scan into same product but different engagements with dedupe_on_engagements creates 0 duplicates""" - self._test_same_product_different_engagements_dedupe_on_engagements_no_duplicates("scans/stackhawk/stackhawk_many_vul_without_duplicated_findings.json", - "StackHawk HawkScan", 0) + # We need to cover all 4 types of deduplication algorithms: + # - LEGACY (Zap) + # - UNIQUE_ID_FROM_TOOL (Checkmarx) + # - HASH_CODE (Trivy) + # - UNIQUE_ID_FROM_TOOL_OR_HASH_CODE (SARIF) + # - UNIQUE_ID_FROM_TOOL_OR_HASH_CODE (Veracode) + # - UNIQUE_ID_FROM_TOOL_OR_HASH_CODE (StackHawk) + + # # Test cases for ZAP (LEGACY algorithm) + # def test_zap_single_import_no_duplicates(self): + # """Test that importing ZAP scan (LEGACY algorithm) creates 0 duplicate findings""" + # self._test_single_import_assess_duplicates("scans/zap/dvwa_baseline_dojo.xml", "ZAP Scan", 0) + + # def test_zap_full_then_subset_duplicates(self): + # """Test that importing full ZAP scan then subset creates duplicates""" + # self._test_full_then_subset_duplicates("scans/zap/dvwa_baseline_dojo.xml", "scans/zap/dvwa_baseline_dojo_subset.xml", "ZAP Scan", 10) + + # def test_zap_different_products_no_duplicates(self): + # """Test that importing ZAP scan into different products creates 0 duplicates""" + # self._test_different_products_no_duplicates("scans/zap/dvwa_baseline_dojo.xml", "ZAP Scan", 0) + + # def test_zap_same_product_different_engagements_duplicates(self): + # """Test that importing ZAP scan into same product but different engagements creates duplicates""" + # self._test_same_product_different_engagements_duplicates("scans/zap/dvwa_baseline_dojo.xml", "ZAP Scan", 19) + + # def test_zap_same_product_different_engagements_dedupe_on_engagements_no_duplicates(self): + # """Test that importing ZAP scan into same product but different engagements with dedupe_on_engagements creates 0 duplicates""" + # self._test_same_product_different_engagements_dedupe_on_engagements_no_duplicates("scans/zap/dvwa_baseline_dojo.xml", "ZAP Scan", 0) + + # # Test cases for ZAP (LEGACY algorithm) with internal duplicates + # def test_zap_single_import_internal_duplicates(self): + # """Test that importing ZAP scan (LEGACY algorithm) creates 3 internal duplicates""" + # self._test_single_import_assess_duplicates("scans/zap/dvwa_baseline_dojo_fabricated_internal_duplicates.xml", "ZAP Scan", 3) + + # def test_zap_full_then_subset_internal_duplicates(self): + # """Test that importing full ZAP scan then subset creates 3 internal duplicates + 6 cross engagement duplicates""" + # self._test_full_then_subset_duplicates("scans/zap/dvwa_baseline_dojo_fabricated_internal_duplicates.xml", "scans/zap/dvwa_baseline_dojo_fabricated_internal_duplicates_subset.xml", "ZAP Scan", 6, first_import_duplicates=3) + + # def test_zap_different_products_internal_duplicates(self): + # """Test that importing ZAP scan into different products creates 3 internal duplicates""" + # self._test_different_products_no_duplicates("scans/zap/dvwa_baseline_dojo_fabricated_internal_duplicates.xml", "ZAP Scan", 3) + + # def test_zap_same_product_different_engagements_internal_duplicates(self): + # """Test that importing ZAP scan into same product but different engagements creates 13 duplicates + 3 internal duplicates = 16 total duplicates""" + # self._test_same_product_different_engagements_duplicates("scans/zap/dvwa_baseline_dojo_fabricated_internal_duplicates.xml", "ZAP Scan", 16) + + # def test_zap_same_product_different_engagements_dedupe_on_engagements_internal_duplicates(self): + # """Test that importing ZAP scan with 3 internal dupcliates into same product but different engagements with dedupe_on_engagements creates only 3 duplicates and no cross engagement duplicates""" + # self._test_same_product_different_engagements_dedupe_on_engagements_no_duplicates("scans/zap/dvwa_baseline_dojo_fabricated_internal_duplicates.xml", "ZAP Scan", 3, first_import_duplicates=3) + + # # Test cases for Checkmarx Scan detailed (UNIQUE_ID_FROM_TOOL algorithm) + # # Please note the non-detailed version uses HASH_CODE algorithm + # def test_checkmarx_single_import_no_duplicates(self): + # """Test that importing Checkmarx scan (UNIQUE_ID_FROM_TOOL algorithm) creates 0 duplicate findings""" + # self._test_single_import_assess_duplicates("scans/checkmarx/multiple_findings.json", "Checkmarx Scan detailed", 0) + + # def test_checkmarx_full_then_subset_duplicates(self): + # """Test that importing full Checkmarx scan then subset creates duplicates""" + # # For now, use the same file for both full and subset + # self._test_full_then_subset_duplicates("scans/checkmarx/multiple_findings.json", "scans/checkmarx/multiple_findings_fabricated_subset.json", + # "Checkmarx Scan detailed", 5) + + # def test_checkmarx_different_products_no_duplicates(self): + # """Test that importing Checkmarx scan into different products creates 0 duplicates""" + # self._test_different_products_no_duplicates("scans/checkmarx/multiple_findings.json", "Checkmarx Scan detailed", 0) + + # def test_checkmarx_same_product_different_engagements_duplicates(self): + # """Test that importing Checkmarx scan into same product but different engagements creates duplicates""" + # self._test_same_product_different_engagements_duplicates("scans/checkmarx/multiple_findings.json", "Checkmarx Scan detailed", 10) + + # def test_checkmarx_same_product_different_engagements_dedupe_on_engagements_no_duplicates(self): + # """Test that importing Checkmarx scan into same product but different engagements with dedupe_on_engagements creates 0 duplicates""" + # self._test_same_product_different_engagements_dedupe_on_engagements_no_duplicates("scans/checkmarx/multiple_findings.json", + # "Checkmarx Scan detailed", 0) + + # # Test cases for Checkmarx Scan detailed (UNIQUE_ID_FROM_TOOL algorithm) with internal duplicates + # # Please note the non-detailed version uses HASH_CODE algorithm + # def test_checkmarx_single_import_internal_duplicates(self): + # """Test that importing Checkmarx scan (UNIQUE_ID_FROM_TOOL algorithm) creates 5 internal duplicates""" + # self._test_single_import_assess_duplicates("scans/checkmarx/multiple_findings_fabricated_internal_duplicates.json", "Checkmarx Scan detailed", 6) + + # def test_checkmarx_full_then_subset_internal_duplicates(self): + # """Test that importing full Checkmarx scan then subset creates 3 internal duplicates + 6 cross engagement duplicates""" + # self._test_full_then_subset_duplicates("scans/checkmarx/multiple_findings_fabricated_internal_duplicates.json", "scans/checkmarx/multiple_findings_fabricated_internal_duplicates_subset.json", "Checkmarx Scan detailed", 6, first_import_duplicates=6) + + # def test_checkmarx_different_products_internal_duplicates(self): + # """Test that importing Checkmarx scan into different products creates 5 internal duplicates""" + # self._test_different_products_no_duplicates("scans/checkmarx/multiple_findings_fabricated_internal_duplicates.json", "Checkmarx Scan detailed", 6) + + # def test_checkmarx_same_product_different_engagements_internal_duplicates(self): + # """Test that importing Checkmarx scan into same product but different engagements creates 13 duplicates + 3 internal duplicates = 16 total duplicates""" + # self._test_same_product_different_engagements_duplicates("scans/checkmarx/multiple_findings_fabricated_internal_duplicates.json", "Checkmarx Scan detailed", 16) + + # def test_checkmarx_same_product_different_engagements_dedupe_on_engagements_internal_duplicates(self): + # """Test that importing Checkmarx scan with 6 internal dupcliates into same product but different engagements with dedupe_on_engagements creates only 6 duplicates and no cross engagement duplicates""" + # self._test_same_product_different_engagements_dedupe_on_engagements_no_duplicates("scans/checkmarx/multiple_findings_fabricated_internal_duplicates.json", "Checkmarx Scan detailed", 6, first_import_duplicates=6) + + # # Test cases for Trivy (HASH_CODE algorithm) + # def test_trivy_single_import_no_duplicates(self): + # """Test that importing Trivy scan (HASH_CODE algorithm) creates 0 duplicate findings""" + # self._test_single_import_assess_duplicates("scans/trivy/kubernetes.json", "Trivy Scan", 0) + + # def test_trivy_full_then_subset_duplicates(self): + # """Test that importing full Trivy scan then subset creates duplicates""" + # # For now, use the same file for both full and subset + # self._test_full_then_subset_duplicates("scans/trivy/kubernetes.json", "scans/trivy/kubernetes_fabricated_subset.json", "Trivy Scan", 13) + + # def test_trivy_different_products_no_duplicates(self): + # """Test that importing Trivy scan into different products creates 0 duplicates""" + # self._test_different_products_no_duplicates("scans/trivy/kubernetes.json", "Trivy Scan", 0) + + # def test_trivy_same_product_different_engagements_duplicates(self): + # """Test that importing Trivy scan into same product but different engagements creates duplicates""" + # self._test_same_product_different_engagements_duplicates("scans/trivy/kubernetes.json", "Trivy Scan", 20) + + # def test_trivy_same_product_different_engagements_dedupe_on_engagements_no_duplicates(self): + # """Test that importing Trivy scan into same product but different engagements with dedupe_on_engagements creates 0 duplicates""" + # self._test_same_product_different_engagements_dedupe_on_engagements_no_duplicates("scans/trivy/kubernetes.json", "Trivy Scan", 0) + + # Test cases for Trivy (HASH_CODE algorithm) with internal duplicates + def test_trivy_single_import_internal_duplicates(self): + """Test that importing Trivy scan (HASH_CODE algorithm) creates 3 internal duplicates""" + self._test_single_import_assess_duplicates("scans/trivy/kubernetes_fabricated_internal_duplicates.json", "Trivy Scan", 3) + + def test_trivy_full_then_subset_internal_duplicates(self): + """Test that importing full Trivy scan then subset creates 3 internal duplicates + 5 cross engagement duplicates""" + self._test_full_then_subset_duplicates("scans/trivy/kubernetes_fabricated_internal_duplicates.json", "scans/trivy/kubernetes_fabricated_internal_duplicates_subset.json", "Trivy Scan", 5, first_import_duplicates=3) + + def test_trivy_different_products_internal_duplicates(self): + """Test that importing Trivy scan into different products creates 3 internal duplicates""" + self._test_different_products_no_duplicates("scans/trivy/kubernetes_fabricated_internal_duplicates.json", "Trivy Scan", 3) + + def test_trivy_same_product_different_engagements_internal_duplicates(self): + """Test that importing Trivy scan into same product but different engagements creates 13 duplicates + 3 internal duplicates = 16 total duplicates""" + self._test_same_product_different_engagements_duplicates("scans/trivy/kubernetes_fabricated_internal_duplicates.json", "Trivy Scan", 16) + + def test_trivy_same_product_different_engagements_dedupe_on_engagements_internal_duplicates(self): + """Test that importing Trivy scan with 6 internal dupcliates into same product but different engagements with dedupe_on_engagements creates only 3 duplicates and no cross engagement duplicates""" + self._test_same_product_different_engagements_dedupe_on_engagements_no_duplicates("scans/trivy/kubernetes_fabricated_internal_duplicates.json", "Trivy Scan", 3, first_import_duplicates=3) + + # # Test cases for SARIF (UNIQUE_ID_FROM_TOOL_OR_HASH_CODE algorithm) + # # The samples for SARIF is the bash report that has internal duplicates + # # These are used on purpose so we capture the behaviour of import and reimport in this scenario. + # def test_sarif_single_import_no_duplicates(self): + # """Test that importing SARIF scan (UNIQUE_ID_FROM_TOOL_OR_HASH_CODE algorithm) creates 0 duplicate findings""" + # # bash-report.sarif has 18 internal duplicates, so we expect 18 duplicates even on first import + # test_id = self._test_single_import_assess_duplicates("scans/sarif/bash-report-fabricated-no-internal-dupes.sarif", "SARIF", 0) + + # # duplicates should be sorted by id (currently not usefull as tests are running celery tasks in the foreground) + # for finding in Finding.objects.filter(test_id=test_id, duplicate=True): + # self.assertTrue(finding.duplicate_finding.id < finding.id) + + # def test_sarif_full_then_subset_duplicates(self): + # """Test that importing full SARIF scan then subset creates duplicates""" + # # For now, use the same file for both full and subset + # # First import has 18 internal duplicates, second import also has 18 internal duplicates + 9 cross-import duplicates = 27 total in second test + # # Total = 18 (first) + 27 (second) = 45 + # self._test_full_then_subset_duplicates("scans/sarif/bash-report-fabricated-no-internal-dupes.sarif", "scans/sarif/bash-report-fabricated-no-internal-dupes-subset.sarif", "SARIF", 4) + + # def test_sarif_different_products_no_duplicates(self): + # """Test that importing SARIF scan into different products creates 0 duplicates""" + # # bash-report.sarif has 18 internal duplicates per import + # self._test_different_products_no_duplicates("scans/sarif/bash-report-fabricated-no-internal-dupes.sarif", "SARIF", 0) + + # def test_sarif_same_product_different_engagements_duplicates(self): + # """Test that importing SARIF scan into same product but different engagements creates duplicates""" + # # 18 internal duplicates in first import + 18 in second import + 9 cross-import duplicates = 45 total + # self._test_same_product_different_engagements_duplicates("scans/sarif/bash-report-fabricated-no-internal-dupes.sarif", "SARIF", 5) + + # def test_sarif_same_product_different_engagements_dedupe_on_engagements_no_duplicates(self): + # """Test that importing SARIF scan into same product but different engagements with dedupe_on_engagements creates 0 duplicates""" + # # bash-report.sarif has 18 internal duplicates per import + # # Second test has 18 internal duplicates (no cross-engagement duplicates due to dedupe_on_engagements=True) + # # Total product duplicates = 18 (first) + 18 (second) = 36 + # self._test_same_product_different_engagements_dedupe_on_engagements_no_duplicates("scans/sarif/bash-report-fabricated-no-internal-dupes.sarif", "SARIF", 0) + + # # Test cases for SARIF (UNIQUE_ID_FROM_TOOL_OR_HASH_CODE algorithm) + # # The samples for SARIF is the bash report that has internal duplicates + # # These are used on purpose so we capture the behaviour of import and reimport in this scenario. + # def test_sarif_single_import_no_duplicates_internal_duplicates(self): + # """Test that importing SARIF scan (UNIQUE_ID_FROM_TOOL_OR_HASH_CODE algorithm) creates 0 duplicate findings""" + # # bash-report.sarif has 18 internal duplicates, so we expect 18 duplicates even on first import + # test_id = self._test_single_import_assess_duplicates("scans/sarif/bash-report.sarif", "SARIF", 18) + + # # duplicates should be sorted by id (currently not usefull as tests are running celery tasks in the foreground) + # for finding in Finding.objects.filter(test_id=test_id, duplicate=True): + # self.assertTrue(finding.duplicate_finding.id < finding.id) + + # def test_sarif_full_then_subset_duplicates_internal_duplicates(self): + # """Test that importing full SARIF scan then subset creates duplicates""" + # # For now, use the same file for both full and subset + # # First import has 18 internal duplicates, second import also has 18 internal duplicates + 9 cross-import duplicates = 27 total in second test + # # Total = 18 (first) + 27 (second) = 45 + # self._test_full_then_subset_duplicates("scans/sarif/bash-report.sarif", "scans/sarif/bash-report.sarif", "SARIF", 27, first_import_duplicates=18) + + # def test_sarif_different_products_no_duplicates_internal_duplicates(self): + # """Test that importing SARIF scan into different products creates 0 duplicates""" + # # bash-report.sarif has 18 internal duplicates per import + # self._test_different_products_no_duplicates("scans/sarif/bash-report.sarif", "SARIF", 18) + + # def test_sarif_same_product_different_engagements_duplicates_internal_duplicates(self): + # """Test that importing SARIF scan into same product but different engagements creates duplicates""" + # # 18 internal duplicates in first import + 18 in second import + 9 cross-import duplicates = 45 total + # self._test_same_product_different_engagements_duplicates("scans/sarif/bash-report.sarif", "SARIF", 45) + + # def test_sarif_same_product_different_engagements_dedupe_on_engagements_no_duplicates_internal_duplicates(self): + # """Test that importing SARIF scan into same product but different engagements with dedupe_on_engagements creates 0 duplicates""" + # # bash-report.sarif has 18 internal duplicates per import + # # Second test has 18 internal duplicates (no cross-engagement duplicates due to dedupe_on_engagements=True) + # # Total product duplicates = 18 (first) + 18 (second) = 36 + # self._test_same_product_different_engagements_dedupe_on_engagements_no_duplicates("scans/sarif/bash-report.sarif", "SARIF", + # 18, first_import_duplicates=18) + + # # Test cases for Veracode (UNIQUE_ID_FROM_TOOL_OR_HASH_CODE algorithm) + # def test_veracode_single_import_no_duplicates(self): + # """Test that importing Veracode scan (UNIQUE_ID_FROM_TOOL_OR_HASH_CODE algorithm) creates 0 duplicate findings""" + # self._test_single_import_assess_duplicates("scans/veracode/veracode_scan.xml", "Veracode Scan", 0) + + # def test_veracode_full_then_subset_duplicates(self): + # """Test that importing full Veracode scan then subset creates duplicates""" + # # For now, use the same file for both full and subset + # self._test_full_then_subset_duplicates("scans/veracode/veracode_scan.xml", "scans/veracode/veracode_scan.xml", "Veracode Scan", 7) + + # def test_veracode_different_products_no_duplicates(self): + # """Test that importing Veracode scan into different products creates 0 duplicates""" + # self._test_different_products_no_duplicates("scans/veracode/veracode_scan.xml", "Veracode Scan", 0) + + # def test_veracode_same_product_different_engagements_duplicates(self): + # """Test that importing Veracode scan into same product but different engagements creates duplicates""" + # self._test_same_product_different_engagements_duplicates("scans/veracode/veracode_scan.xml", "Veracode Scan", 7) + + # def test_veracode_same_product_different_engagements_dedupe_on_engagements_no_duplicates(self): + # """Test that importing Veracode scan into same product but different engagements with dedupe_on_engagements creates 0 duplicates""" + # self._test_same_product_different_engagements_dedupe_on_engagements_no_duplicates("scans/veracode/veracode_scan.xml", "Veracode Scan", 0) + + # # Test cases for StackHawk (HASH_CODE algorithm) + # def test_stackhawk_single_import_no_duplicates(self): + # """Test that importing StackHawk scan (HASH_CODE algorithm) creates 0 duplicate findings""" + # self._test_single_import_assess_duplicates("scans/stackhawk/stackhawk_many_vul_without_duplicated_findings.json", "StackHawk HawkScan", 0) + + # def test_stackhawk_full_then_subset_duplicates(self): + # """Test that importing full StackHawk scan then subset creates duplicates""" + # self._test_full_then_subset_duplicates("scans/stackhawk/stackhawk_many_vul_without_duplicated_findings.json", + # "scans/stackhawk/stackhawk_many_vul_without_duplicated_findings_subset.json", "StackHawk HawkScan", 5) + + # def test_stackhawk_different_products_no_duplicates(self): + # """Test that importing StackHawk scan into different products creates 0 duplicates""" + # self._test_different_products_no_duplicates("scans/stackhawk/stackhawk_many_vul_without_duplicated_findings.json", "StackHawk HawkScan", 0) + + # def test_stackhawk_same_product_different_engagements_duplicates(self): + # """Test that importing StackHawk scan into same product but different engagements creates duplicates""" + # self._test_same_product_different_engagements_duplicates("scans/stackhawk/stackhawk_many_vul_without_duplicated_findings.json", + # "StackHawk HawkScan", 6) + + # def test_stackhawk_same_product_different_engagements_dedupe_on_engagements_no_duplicates(self): + # """Test that importing StackHawk scan into same product but different engagements with dedupe_on_engagements creates 0 duplicates""" + # self._test_same_product_different_engagements_dedupe_on_engagements_no_duplicates("scans/stackhawk/stackhawk_many_vul_without_duplicated_findings.json", + # "StackHawk HawkScan", 0) From 4f6992ded4d038d98a8efe6cfc4c61b18fb603d6 Mon Sep 17 00:00:00 2001 From: Valentijn Scholten Date: Sun, 19 Oct 2025 11:44:17 +0200 Subject: [PATCH 06/50] deduplication: add more importer unit tests --- unittests/test_importers_deduplication.py | 236 +++++++++++----------- 1 file changed, 118 insertions(+), 118 deletions(-) diff --git a/unittests/test_importers_deduplication.py b/unittests/test_importers_deduplication.py index bad44a7f974..7f814f8174a 100644 --- a/unittests/test_importers_deduplication.py +++ b/unittests/test_importers_deduplication.py @@ -432,121 +432,121 @@ def test_trivy_same_product_different_engagements_dedupe_on_engagements_internal """Test that importing Trivy scan with 6 internal dupcliates into same product but different engagements with dedupe_on_engagements creates only 3 duplicates and no cross engagement duplicates""" self._test_same_product_different_engagements_dedupe_on_engagements_no_duplicates("scans/trivy/kubernetes_fabricated_internal_duplicates.json", "Trivy Scan", 3, first_import_duplicates=3) - # # Test cases for SARIF (UNIQUE_ID_FROM_TOOL_OR_HASH_CODE algorithm) - # # The samples for SARIF is the bash report that has internal duplicates - # # These are used on purpose so we capture the behaviour of import and reimport in this scenario. - # def test_sarif_single_import_no_duplicates(self): - # """Test that importing SARIF scan (UNIQUE_ID_FROM_TOOL_OR_HASH_CODE algorithm) creates 0 duplicate findings""" - # # bash-report.sarif has 18 internal duplicates, so we expect 18 duplicates even on first import - # test_id = self._test_single_import_assess_duplicates("scans/sarif/bash-report-fabricated-no-internal-dupes.sarif", "SARIF", 0) - - # # duplicates should be sorted by id (currently not usefull as tests are running celery tasks in the foreground) - # for finding in Finding.objects.filter(test_id=test_id, duplicate=True): - # self.assertTrue(finding.duplicate_finding.id < finding.id) - - # def test_sarif_full_then_subset_duplicates(self): - # """Test that importing full SARIF scan then subset creates duplicates""" - # # For now, use the same file for both full and subset - # # First import has 18 internal duplicates, second import also has 18 internal duplicates + 9 cross-import duplicates = 27 total in second test - # # Total = 18 (first) + 27 (second) = 45 - # self._test_full_then_subset_duplicates("scans/sarif/bash-report-fabricated-no-internal-dupes.sarif", "scans/sarif/bash-report-fabricated-no-internal-dupes-subset.sarif", "SARIF", 4) - - # def test_sarif_different_products_no_duplicates(self): - # """Test that importing SARIF scan into different products creates 0 duplicates""" - # # bash-report.sarif has 18 internal duplicates per import - # self._test_different_products_no_duplicates("scans/sarif/bash-report-fabricated-no-internal-dupes.sarif", "SARIF", 0) - - # def test_sarif_same_product_different_engagements_duplicates(self): - # """Test that importing SARIF scan into same product but different engagements creates duplicates""" - # # 18 internal duplicates in first import + 18 in second import + 9 cross-import duplicates = 45 total - # self._test_same_product_different_engagements_duplicates("scans/sarif/bash-report-fabricated-no-internal-dupes.sarif", "SARIF", 5) - - # def test_sarif_same_product_different_engagements_dedupe_on_engagements_no_duplicates(self): - # """Test that importing SARIF scan into same product but different engagements with dedupe_on_engagements creates 0 duplicates""" - # # bash-report.sarif has 18 internal duplicates per import - # # Second test has 18 internal duplicates (no cross-engagement duplicates due to dedupe_on_engagements=True) - # # Total product duplicates = 18 (first) + 18 (second) = 36 - # self._test_same_product_different_engagements_dedupe_on_engagements_no_duplicates("scans/sarif/bash-report-fabricated-no-internal-dupes.sarif", "SARIF", 0) - - # # Test cases for SARIF (UNIQUE_ID_FROM_TOOL_OR_HASH_CODE algorithm) - # # The samples for SARIF is the bash report that has internal duplicates - # # These are used on purpose so we capture the behaviour of import and reimport in this scenario. - # def test_sarif_single_import_no_duplicates_internal_duplicates(self): - # """Test that importing SARIF scan (UNIQUE_ID_FROM_TOOL_OR_HASH_CODE algorithm) creates 0 duplicate findings""" - # # bash-report.sarif has 18 internal duplicates, so we expect 18 duplicates even on first import - # test_id = self._test_single_import_assess_duplicates("scans/sarif/bash-report.sarif", "SARIF", 18) - - # # duplicates should be sorted by id (currently not usefull as tests are running celery tasks in the foreground) - # for finding in Finding.objects.filter(test_id=test_id, duplicate=True): - # self.assertTrue(finding.duplicate_finding.id < finding.id) - - # def test_sarif_full_then_subset_duplicates_internal_duplicates(self): - # """Test that importing full SARIF scan then subset creates duplicates""" - # # For now, use the same file for both full and subset - # # First import has 18 internal duplicates, second import also has 18 internal duplicates + 9 cross-import duplicates = 27 total in second test - # # Total = 18 (first) + 27 (second) = 45 - # self._test_full_then_subset_duplicates("scans/sarif/bash-report.sarif", "scans/sarif/bash-report.sarif", "SARIF", 27, first_import_duplicates=18) - - # def test_sarif_different_products_no_duplicates_internal_duplicates(self): - # """Test that importing SARIF scan into different products creates 0 duplicates""" - # # bash-report.sarif has 18 internal duplicates per import - # self._test_different_products_no_duplicates("scans/sarif/bash-report.sarif", "SARIF", 18) - - # def test_sarif_same_product_different_engagements_duplicates_internal_duplicates(self): - # """Test that importing SARIF scan into same product but different engagements creates duplicates""" - # # 18 internal duplicates in first import + 18 in second import + 9 cross-import duplicates = 45 total - # self._test_same_product_different_engagements_duplicates("scans/sarif/bash-report.sarif", "SARIF", 45) - - # def test_sarif_same_product_different_engagements_dedupe_on_engagements_no_duplicates_internal_duplicates(self): - # """Test that importing SARIF scan into same product but different engagements with dedupe_on_engagements creates 0 duplicates""" - # # bash-report.sarif has 18 internal duplicates per import - # # Second test has 18 internal duplicates (no cross-engagement duplicates due to dedupe_on_engagements=True) - # # Total product duplicates = 18 (first) + 18 (second) = 36 - # self._test_same_product_different_engagements_dedupe_on_engagements_no_duplicates("scans/sarif/bash-report.sarif", "SARIF", - # 18, first_import_duplicates=18) - - # # Test cases for Veracode (UNIQUE_ID_FROM_TOOL_OR_HASH_CODE algorithm) - # def test_veracode_single_import_no_duplicates(self): - # """Test that importing Veracode scan (UNIQUE_ID_FROM_TOOL_OR_HASH_CODE algorithm) creates 0 duplicate findings""" - # self._test_single_import_assess_duplicates("scans/veracode/veracode_scan.xml", "Veracode Scan", 0) - - # def test_veracode_full_then_subset_duplicates(self): - # """Test that importing full Veracode scan then subset creates duplicates""" - # # For now, use the same file for both full and subset - # self._test_full_then_subset_duplicates("scans/veracode/veracode_scan.xml", "scans/veracode/veracode_scan.xml", "Veracode Scan", 7) - - # def test_veracode_different_products_no_duplicates(self): - # """Test that importing Veracode scan into different products creates 0 duplicates""" - # self._test_different_products_no_duplicates("scans/veracode/veracode_scan.xml", "Veracode Scan", 0) - - # def test_veracode_same_product_different_engagements_duplicates(self): - # """Test that importing Veracode scan into same product but different engagements creates duplicates""" - # self._test_same_product_different_engagements_duplicates("scans/veracode/veracode_scan.xml", "Veracode Scan", 7) - - # def test_veracode_same_product_different_engagements_dedupe_on_engagements_no_duplicates(self): - # """Test that importing Veracode scan into same product but different engagements with dedupe_on_engagements creates 0 duplicates""" - # self._test_same_product_different_engagements_dedupe_on_engagements_no_duplicates("scans/veracode/veracode_scan.xml", "Veracode Scan", 0) - - # # Test cases for StackHawk (HASH_CODE algorithm) - # def test_stackhawk_single_import_no_duplicates(self): - # """Test that importing StackHawk scan (HASH_CODE algorithm) creates 0 duplicate findings""" - # self._test_single_import_assess_duplicates("scans/stackhawk/stackhawk_many_vul_without_duplicated_findings.json", "StackHawk HawkScan", 0) - - # def test_stackhawk_full_then_subset_duplicates(self): - # """Test that importing full StackHawk scan then subset creates duplicates""" - # self._test_full_then_subset_duplicates("scans/stackhawk/stackhawk_many_vul_without_duplicated_findings.json", - # "scans/stackhawk/stackhawk_many_vul_without_duplicated_findings_subset.json", "StackHawk HawkScan", 5) - - # def test_stackhawk_different_products_no_duplicates(self): - # """Test that importing StackHawk scan into different products creates 0 duplicates""" - # self._test_different_products_no_duplicates("scans/stackhawk/stackhawk_many_vul_without_duplicated_findings.json", "StackHawk HawkScan", 0) - - # def test_stackhawk_same_product_different_engagements_duplicates(self): - # """Test that importing StackHawk scan into same product but different engagements creates duplicates""" - # self._test_same_product_different_engagements_duplicates("scans/stackhawk/stackhawk_many_vul_without_duplicated_findings.json", - # "StackHawk HawkScan", 6) - - # def test_stackhawk_same_product_different_engagements_dedupe_on_engagements_no_duplicates(self): - # """Test that importing StackHawk scan into same product but different engagements with dedupe_on_engagements creates 0 duplicates""" - # self._test_same_product_different_engagements_dedupe_on_engagements_no_duplicates("scans/stackhawk/stackhawk_many_vul_without_duplicated_findings.json", - # "StackHawk HawkScan", 0) + # Test cases for SARIF (UNIQUE_ID_FROM_TOOL_OR_HASH_CODE algorithm) + # The samples for SARIF is the bash report that has internal duplicates + # These are used on purpose so we capture the behaviour of import and reimport in this scenario. + def test_sarif_single_import_no_duplicates(self): + """Test that importing SARIF scan (UNIQUE_ID_FROM_TOOL_OR_HASH_CODE algorithm) creates 0 duplicate findings""" + # bash-report.sarif has 18 internal duplicates, so we expect 18 duplicates even on first import + test_id = self._test_single_import_assess_duplicates("scans/sarif/bash-report-fabricated-no-internal-dupes.sarif", "SARIF", 0) + + # duplicates should be sorted by id (currently not usefull as tests are running celery tasks in the foreground) + for finding in Finding.objects.filter(test_id=test_id, duplicate=True): + self.assertTrue(finding.duplicate_finding.id < finding.id) + + def test_sarif_full_then_subset_duplicates(self): + """Test that importing full SARIF scan then subset creates duplicates""" + # For now, use the same file for both full and subset + # First import has 18 internal duplicates, second import also has 18 internal duplicates + 9 cross-import duplicates = 27 total in second test + # Total = 18 (first) + 27 (second) = 45 + self._test_full_then_subset_duplicates("scans/sarif/bash-report-fabricated-no-internal-dupes.sarif", "scans/sarif/bash-report-fabricated-no-internal-dupes-subset.sarif", "SARIF", 4) + + def test_sarif_different_products_no_duplicates(self): + """Test that importing SARIF scan into different products creates 0 duplicates""" + # bash-report.sarif has 18 internal duplicates per import + self._test_different_products_no_duplicates("scans/sarif/bash-report-fabricated-no-internal-dupes.sarif", "SARIF", 0) + + def test_sarif_same_product_different_engagements_duplicates(self): + """Test that importing SARIF scan into same product but different engagements creates duplicates""" + # 18 internal duplicates in first import + 18 in second import + 9 cross-import duplicates = 45 total + self._test_same_product_different_engagements_duplicates("scans/sarif/bash-report-fabricated-no-internal-dupes.sarif", "SARIF", 5) + + def test_sarif_same_product_different_engagements_dedupe_on_engagements_no_duplicates(self): + """Test that importing SARIF scan into same product but different engagements with dedupe_on_engagements creates 0 duplicates""" + # bash-report.sarif has 18 internal duplicates per import + # Second test has 18 internal duplicates (no cross-engagement duplicates due to dedupe_on_engagements=True) + # Total product duplicates = 18 (first) + 18 (second) = 36 + self._test_same_product_different_engagements_dedupe_on_engagements_no_duplicates("scans/sarif/bash-report-fabricated-no-internal-dupes.sarif", "SARIF", 0) + + # Test cases for SARIF (UNIQUE_ID_FROM_TOOL_OR_HASH_CODE algorithm) with internal duplicates + # The samples for SARIF is the bash report that has internal duplicates + # These are used on purpose so we capture the behaviour of import and reimport in this scenario. + def test_sarif_single_import_internal_duplicates(self): + """Test that importing SARIF scan (UNIQUE_ID_FROM_TOOL_OR_HASH_CODE algorithm) creates 18 internal duplicates""" + # bash-report.sarif has 18 internal duplicates, so we expect 18 duplicates even on first import + test_id = self._test_single_import_assess_duplicates("scans/sarif/bash-report.sarif", "SARIF", 18) + + # duplicates should be sorted by id (currently not usefull as tests are running celery tasks in the foreground) + for finding in Finding.objects.filter(test_id=test_id, duplicate=True): + self.assertTrue(finding.duplicate_finding.id < finding.id) + + def test_sarif_full_then_subset_internal_duplicates(self): + """Test that importing full SARIF scan then subset creates 18 internal duplicates + 9 cross-import duplicates = 27 duplicates in second import""" + # For now, use the same file for both full and subset + # First import has 18 internal duplicates, second import also has 18 internal duplicates + 9 cross-import duplicates = 27 total in second test + # Total = 18 (first) + 27 (second) = 45 + self._test_full_then_subset_duplicates("scans/sarif/bash-report.sarif", "scans/sarif/bash-report.sarif", "SARIF", 27, first_import_duplicates=18) + + def test_sarif_different_products_internal_duplicates(self): + """Test that importing SARIF scan into different products creates 18 internal duplicates per import""" + # bash-report.sarif has 18 internal duplicates per import + self._test_different_products_no_duplicates("scans/sarif/bash-report.sarif", "SARIF", 18) + + def test_sarif_same_product_different_engagements_internal_duplicates(self): + """Test that importing SARIF scan into same product but different engagements creates 45 total duplicates (18 + 18 + 9)""" + # 18 internal duplicates in first import + 18 in second import + 9 cross-import duplicates = 45 total + self._test_same_product_different_engagements_duplicates("scans/sarif/bash-report.sarif", "SARIF", 45) + + def test_sarif_same_product_different_engagements_dedupe_on_engagements_internal_duplicates(self): + """Test that importing SARIF scan with 18 internal duplicates into same product but different engagements with dedupe_on_engagements creates only 18 duplicates (no cross-engagement duplicates)""" + # bash-report.sarif has 18 internal duplicates per import + # Second test has 18 internal duplicates (no cross-engagement duplicates due to dedupe_on_engagements=True) + # Total product duplicates = 18 (first) + 18 (second) = 36 + self._test_same_product_different_engagements_dedupe_on_engagements_no_duplicates("scans/sarif/bash-report.sarif", "SARIF", + 18, first_import_duplicates=18) + + # Test cases for Veracode (UNIQUE_ID_FROM_TOOL_OR_HASH_CODE algorithm) + def test_veracode_single_import_no_duplicates(self): + """Test that importing Veracode scan (UNIQUE_ID_FROM_TOOL_OR_HASH_CODE algorithm) creates 0 duplicate findings""" + self._test_single_import_assess_duplicates("scans/veracode/veracode_scan.xml", "Veracode Scan", 0) + + def test_veracode_full_then_subset_duplicates(self): + """Test that importing full Veracode scan then subset creates duplicates""" + # For now, use the same file for both full and subset + self._test_full_then_subset_duplicates("scans/veracode/veracode_scan.xml", "scans/veracode/veracode_scan.xml", "Veracode Scan", 7) + + def test_veracode_different_products_no_duplicates(self): + """Test that importing Veracode scan into different products creates 0 duplicates""" + self._test_different_products_no_duplicates("scans/veracode/veracode_scan.xml", "Veracode Scan", 0) + + def test_veracode_same_product_different_engagements_duplicates(self): + """Test that importing Veracode scan into same product but different engagements creates duplicates""" + self._test_same_product_different_engagements_duplicates("scans/veracode/veracode_scan.xml", "Veracode Scan", 7) + + def test_veracode_same_product_different_engagements_dedupe_on_engagements_no_duplicates(self): + """Test that importing Veracode scan into same product but different engagements with dedupe_on_engagements creates 0 duplicates""" + self._test_same_product_different_engagements_dedupe_on_engagements_no_duplicates("scans/veracode/veracode_scan.xml", "Veracode Scan", 0) + + # Test cases for StackHawk (HASH_CODE algorithm) + def test_stackhawk_single_import_no_duplicates(self): + """Test that importing StackHawk scan (HASH_CODE algorithm) creates 0 duplicate findings""" + self._test_single_import_assess_duplicates("scans/stackhawk/stackhawk_many_vul_without_duplicated_findings.json", "StackHawk HawkScan", 0) + + def test_stackhawk_full_then_subset_duplicates(self): + """Test that importing full StackHawk scan then subset creates duplicates""" + self._test_full_then_subset_duplicates("scans/stackhawk/stackhawk_many_vul_without_duplicated_findings.json", + "scans/stackhawk/stackhawk_many_vul_without_duplicated_findings_subset.json", "StackHawk HawkScan", 5) + + def test_stackhawk_different_products_no_duplicates(self): + """Test that importing StackHawk scan into different products creates 0 duplicates""" + self._test_different_products_no_duplicates("scans/stackhawk/stackhawk_many_vul_without_duplicated_findings.json", "StackHawk HawkScan", 0) + + def test_stackhawk_same_product_different_engagements_duplicates(self): + """Test that importing StackHawk scan into same product but different engagements creates duplicates""" + self._test_same_product_different_engagements_duplicates("scans/stackhawk/stackhawk_many_vul_without_duplicated_findings.json", + "StackHawk HawkScan", 6) + + def test_stackhawk_same_product_different_engagements_dedupe_on_engagements_no_duplicates(self): + """Test that importing StackHawk scan into same product but different engagements with dedupe_on_engagements creates 0 duplicates""" + self._test_same_product_different_engagements_dedupe_on_engagements_no_duplicates("scans/stackhawk/stackhawk_many_vul_without_duplicated_findings.json", + "StackHawk HawkScan", 0) From 15a06e6dd083eea90d0eb5754b3c83e8709e3601 Mon Sep 17 00:00:00 2001 From: Valentijn Scholten Date: Sun, 19 Oct 2025 11:30:20 +0200 Subject: [PATCH 07/50] deduplication: log hash_code_fields_always --- dojo/models.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/dojo/models.py b/dojo/models.py index d5b672c53c7..edffbe7166c 100644 --- a/dojo/models.py +++ b/dojo/models.py @@ -2231,7 +2231,9 @@ def hash_code_fields(self): else: deduplicationLogger.debug("Section HASHCODE_FIELDS_PER_SCANNER not found in settings.dist.py") - deduplicationLogger.debug(f"HASHCODE_FIELDS_PER_SCANNER is: {hashCodeFields}") + hash_code_fields_always = getattr(settings, "HASH_CODE_FIELDS_ALWAYS", []) + deduplicationLogger.debug(f"HASHCODE_FIELDS_PER_SCANNER is: {hashCodeFields} + HASH_CODE_FIELDS_ALWAYS: {hash_code_fields_always}") + return hashCodeFields @property @@ -2935,6 +2937,13 @@ def compute_hash_code(self): # Generically use the finding attribute having the same name, converts to str in case it's integer fields_to_hash += str(getattr(self, hashcodeField)) deduplicationLogger.debug(hashcodeField + " : " + str(getattr(self, hashcodeField))) + + # Log the hash_code fields that are always included (but are not part of the hash_code_fields list as they are inserted downtstream in self.hash_fields) + hash_code_fields_always = getattr(settings, "HASH_CODE_FIELDS_ALWAYS", []) + for hashcodeField in hash_code_fields_always: + if getattr(self, hashcodeField): + deduplicationLogger.debug(hashcodeField + " : " + str(getattr(self, hashcodeField))) + deduplicationLogger.debug("compute_hash_code - fields_to_hash = " + fields_to_hash) return self.hash_fields(fields_to_hash) From 8bb5292c0fb2022a5f03ce2bebc77832d1ea5dbc Mon Sep 17 00:00:00 2001 From: Valentijn Scholten Date: Sun, 19 Oct 2025 00:16:55 +0200 Subject: [PATCH 08/50] view_finding: show unique_id_from_tool with hash_code --- dojo/templates/dojo/view_finding.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dojo/templates/dojo/view_finding.html b/dojo/templates/dojo/view_finding.html index 9001cbcd9cb..b0c601ddc2a 100755 --- a/dojo/templates/dojo/view_finding.html +++ b/dojo/templates/dojo/view_finding.html @@ -285,7 +285,7 @@

{% block header_body %} - {{ finding.id }} + {{ finding.id }} {% if finding.severity %} @@ -1209,7 +1209,7 @@

Credential var i = $($(this).find('i').get(0)); i.toggleClass('glyphicon-chevron-up').toggleClass('glyphicon-chevron-down'); }) - + // Configure tooltips for CVSS vectors - try multiple approaches $(document).on('shown.bs.tooltip shown.bs.popover', function() { $('.tooltip-inner, .popover-content').css({ @@ -1221,7 +1221,7 @@

Credential 'max-width': '1200px !important' }); }); - + // Force tooltip configuration $('.has-popover').each(function() { $(this).on('mouseenter', function() { From b2ea7eb22cc610bd308a33311cc278f8d2bb51bb Mon Sep 17 00:00:00 2001 From: Valentijn Scholten Date: Sun, 19 Oct 2025 00:25:47 +0200 Subject: [PATCH 09/50] view_finding: show unique_id_from_tool with hash_code --- dojo/templates/dojo/findings_list_snippet.html | 2 +- dojo/templates/dojo/view_finding.html | 2 +- dojo/templates/dojo/view_test.html | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dojo/templates/dojo/findings_list_snippet.html b/dojo/templates/dojo/findings_list_snippet.html index c6d9331b1ce..fd9ec39d07e 100644 --- a/dojo/templates/dojo/findings_list_snippet.html +++ b/dojo/templates/dojo/findings_list_snippet.html @@ -664,7 +664,7 @@

title="Test: {{ finding.test }}">{{ finding.test.test_type }} {% endif %} - + {{ finding|finding_display_status|safe }} {{ finding|import_history }} {% if system_settings.enable_jira %} diff --git a/dojo/templates/dojo/view_finding.html b/dojo/templates/dojo/view_finding.html index b0c601ddc2a..a992a22d401 100755 --- a/dojo/templates/dojo/view_finding.html +++ b/dojo/templates/dojo/view_finding.html @@ -285,7 +285,7 @@

{% block header_body %} - {{ finding.id }} + {{ finding.id }} {% if finding.severity %} diff --git a/dojo/templates/dojo/view_test.html b/dojo/templates/dojo/view_test.html index 824cad760b7..677a586b239 100644 --- a/dojo/templates/dojo/view_test.html +++ b/dojo/templates/dojo/view_test.html @@ -36,7 +36,7 @@

id="dropdownMenu1" data-toggle="dropdown" aria-expanded="true" - aria-label="Test options" + aria-label="Test options" > @@ -697,7 +697,7 @@

-
+