From e8e03ba6bb9e3b3dcd9e1af2d3a8bd7f4aa4622c Mon Sep 17 00:00:00 2001 From: slManuel Date: Mon, 6 Jul 2026 11:32:44 -0600 Subject: [PATCH 1/2] feat(defect_dojo): make apply_tags_to_findings a configurable import-scan parameter apply_tags_to_findings was hardcoded to "true" in the import scan driver adapter, forcing the behavior on every consumer. Following the existing configuration pattern (REIMPORT_SCAN, GET_EXACT_PRODUCT), it is now a proper parameter of the import-scan request: - add apply_tags_to_findings to ImportScanRequest (default False) - expose it in ImportScanSerializer (Bool, load_default=False) - read it from the remote config (DEFECT_DOJO.APPLY_TAGS_TO_FINDINGS) with a safe default so consumers without the key are unaffected - the driver adapter serializes request.apply_tags_to_findings instead of the literal "true" Defaults to false to preserve behavior for consumers that do not opt in; set APPLY_TAGS_TO_FINDINGS: true in the remote config to enable it. --- .../defect_dojo/defect_dojo.py | 3 +++ .../defect_dojo/test_defect_dojo.py | 3 +++ .../domain/request_objects/import_scan.py | 1 + .../domain/serializers/import_scan.py | 1 + .../driver_adapters/import_scan.py | 4 ++-- .../driver_adapter/test_import_scan.py | 20 +++++++++++++++++++ 6 files changed, 30 insertions(+), 2 deletions(-) diff --git a/tools/devsecops_engine_tools/engine_core/src/infrastructure/driven_adapters/defect_dojo/defect_dojo.py b/tools/devsecops_engine_tools/engine_core/src/infrastructure/driven_adapters/defect_dojo/defect_dojo.py index 3023b3924..9a2ca67e8 100755 --- a/tools/devsecops_engine_tools/engine_core/src/infrastructure/driven_adapters/defect_dojo/defect_dojo.py +++ b/tools/devsecops_engine_tools/engine_core/src/infrastructure/driven_adapters/defect_dojo/defect_dojo.py @@ -577,6 +577,9 @@ def _build_request_importscan( "get_exact_product": vulnerability_management.config_tool[ "VULNERABILITY_MANAGER" ]["DEFECT_DOJO"].get("GET_EXACT_PRODUCT", False), + "apply_tags_to_findings": vulnerability_management.config_tool[ + "VULNERABILITY_MANAGER" + ]["DEFECT_DOJO"].get("APPLY_TAGS_TO_FINDINGS", False), "tool_sonarqube_configuration": ( tool_sonar_conf_mapping[ vulnerability_management.sonar_instance.upper() diff --git a/tools/devsecops_engine_tools/engine_core/test/infrastructure/driven_adapters/defect_dojo/test_defect_dojo.py b/tools/devsecops_engine_tools/engine_core/test/infrastructure/driven_adapters/defect_dojo/test_defect_dojo.py index 134224358..e532ca326 100755 --- a/tools/devsecops_engine_tools/engine_core/test/infrastructure/driven_adapters/defect_dojo/test_defect_dojo.py +++ b/tools/devsecops_engine_tools/engine_core/test/infrastructure/driven_adapters/defect_dojo/test_defect_dojo.py @@ -171,6 +171,7 @@ def test_send_vulnerability_management(self, mock_send_import_scan): test_title="engine_iac_k8s", reimport_scan=True, get_exact_product=False, + apply_tags_to_findings=False, remote_config_source="github", remote_config_repo="remote_config", remote_config_path="mapping_path", @@ -338,6 +339,7 @@ def test_build_request_with_cmdb(self): test_title="engine_iac_k8s", reimport_scan=True, get_exact_product=False, + apply_tags_to_findings=False, remote_config_source="github", remote_config_repo="remote_config", remote_config_path="mapping_path", @@ -447,6 +449,7 @@ def test_build_request_without_cmdb(self): "test_title": "engine_iac_k8s_test_2", "reimport_scan": True, "get_exact_product": False, + "apply_tags_to_findings": False, } ) self.assertEqual(result, "import_scan_request_result") diff --git a/tools/devsecops_engine_tools/engine_utilities/defect_dojo/domain/request_objects/import_scan.py b/tools/devsecops_engine_tools/engine_utilities/defect_dojo/domain/request_objects/import_scan.py index d4a3d8f2f..fbef1445d 100755 --- a/tools/devsecops_engine_tools/engine_utilities/defect_dojo/domain/request_objects/import_scan.py +++ b/tools/devsecops_engine_tools/engine_utilities/defect_dojo/domain/request_objects/import_scan.py @@ -24,6 +24,7 @@ class ImportScanRequest: deduplication_on_engagement: str = "" lead: str = "" tags: List[str] = dataclasses.field(default_factory=list) + apply_tags_to_findings: bool = False close_old_findings: str = "" close_old_findings_product_scope: str = "" push_to_jira: str = "" diff --git a/tools/devsecops_engine_tools/engine_utilities/defect_dojo/domain/serializers/import_scan.py b/tools/devsecops_engine_tools/engine_utilities/defect_dojo/domain/serializers/import_scan.py index 3a24def86..1c8015d2b 100755 --- a/tools/devsecops_engine_tools/engine_utilities/defect_dojo/domain/serializers/import_scan.py +++ b/tools/devsecops_engine_tools/engine_utilities/defect_dojo/domain/serializers/import_scan.py @@ -232,6 +232,7 @@ class ImportScanSerializer(Schema): expression = fields.Str(required=True) reimport_scan = fields.Bool(required=False) get_exact_product = fields.Bool(required=False) + apply_tags_to_findings = fields.Bool(required=False, load_default=False) @post_load def make_cmdb(self, data, **kwargs): diff --git a/tools/devsecops_engine_tools/engine_utilities/defect_dojo/infraestructure/driver_adapters/import_scan.py b/tools/devsecops_engine_tools/engine_utilities/defect_dojo/infraestructure/driver_adapters/import_scan.py index 8a6f43e61..035035aa3 100755 --- a/tools/devsecops_engine_tools/engine_utilities/defect_dojo/infraestructure/driver_adapters/import_scan.py +++ b/tools/devsecops_engine_tools/engine_utilities/defect_dojo/infraestructure/driver_adapters/import_scan.py @@ -32,7 +32,7 @@ def import_scan_api(self, request: ImportScanRequest) -> ImportScanRequest: "deduplication_on_engagement": request.deduplication_on_engagement, "lead": request.lead, "tags": ",".join(request.tags) if request.tags else "", - "apply_tags_to_findings": "true", + "apply_tags_to_findings": str(request.apply_tags_to_findings).lower(), "close_old_findings": str(request.close_old_findings), "close_old_findings_product_scope": str(request.close_old_findings_product_scope), "push_to_jira": str(request.push_to_jira), @@ -90,7 +90,7 @@ def import_scan(self, request: ImportScanRequest, files) -> ImportScanRequest: "deduplication_on_engagement": request.deduplication_on_engagement, "lead": request.lead, "tags": request.tags, - "apply_tags_to_findings": "true", + "apply_tags_to_findings": str(request.apply_tags_to_findings).lower(), "close_old_findings": request.close_old_findings, "close_old_findings_product_scope": request.close_old_findings_product_scope, "push_to_jira": request.push_to_jira, diff --git a/tools/devsecops_engine_tools/engine_utilities/defect_dojo/test/infraestucture/driver_adapter/test_import_scan.py b/tools/devsecops_engine_tools/engine_utilities/defect_dojo/test/infraestucture/driver_adapter/test_import_scan.py index 81c2eefbb..3b4db1f3d 100644 --- a/tools/devsecops_engine_tools/engine_utilities/defect_dojo/test/infraestucture/driver_adapter/test_import_scan.py +++ b/tools/devsecops_engine_tools/engine_utilities/defect_dojo/test/infraestucture/driver_adapter/test_import_scan.py @@ -52,3 +52,23 @@ def test_post_import_scan_info_failure(): ) with pytest.raises(ApiError): rest_import_scan.import_scan(request, file) + + +def test_import_scan_applies_tags_to_findings_when_enabled(): + session_mock = session_manager_post(status_code=201, mock_response="import_scan.json") + request = ImportScanRequest(apply_tags_to_findings=True) + rest_import_scan = ImportScanRestConsumer(request, session_mock) + with open(f"{DEVSECOPS_ENGINE_UTILITIES_PATH}/defect_dojo/test/files/import_scan.json", "r") as fp: + rest_import_scan.import_scan(request, fp) + sent_data = session_mock._instance.post.call_args.kwargs["data"] + assert sent_data["apply_tags_to_findings"] == "true" + + +def test_import_scan_does_not_apply_tags_to_findings_by_default(): + session_mock = session_manager_post(status_code=201, mock_response="import_scan.json") + request = ImportScanRequest() + rest_import_scan = ImportScanRestConsumer(request, session_mock) + with open(f"{DEVSECOPS_ENGINE_UTILITIES_PATH}/defect_dojo/test/files/import_scan.json", "r") as fp: + rest_import_scan.import_scan(request, fp) + sent_data = session_mock._instance.post.call_args.kwargs["data"] + assert sent_data["apply_tags_to_findings"] == "false" From 961dbea1d92665e3fcb075f7ccf433d24f336bfa Mon Sep 17 00:00:00 2001 From: slManuel Date: Mon, 6 Jul 2026 15:37:11 -0600 Subject: [PATCH 2/2] docs(defect_dojo): document APPLY_TAGS_TO_FINDINGS remote config option Add the new DEFECT_DOJO.APPLY_TAGS_TO_FINDINGS flag to the example remote config (example_remote_config_local) and the engine_core module docs (JSON examples + parameter reference), alongside REIMPORT_SCAN. Defaults to false. --- docs/Docusaurus/docs/modules/engine_core.md | 6 ++++++ example_remote_config_local/engine_core/ConfigTool.json | 1 + 2 files changed, 7 insertions(+) diff --git a/docs/Docusaurus/docs/modules/engine_core.md b/docs/Docusaurus/docs/modules/engine_core.md index ea22448f1..1da9dafbc 100644 --- a/docs/Docusaurus/docs/modules/engine_core.md +++ b/docs/Docusaurus/docs/modules/engine_core.md @@ -57,6 +57,7 @@ Configuration of the driven adapters in the main layer and management of on/off }, "REGEX_EXPRESSION_CODE_APP": "", "REIMPORT_SCAN": false, + "APPLY_TAGS_TO_FINDINGS": false, "CMDB": { "USE_CMDB": false, "HOST_CMDB": "", @@ -314,6 +315,9 @@ Configuration of the driven adapters in the main layer and management of on/off - **REIMPORT_SCAN**: Boolean value that determines whether the scan results should be re-imported into DefectDojo. - If set to `true`, the tool will attempt to re-import scan results, updating the same test. - If set to `false`, each scan will be imported as a new test. + - **APPLY_TAGS_TO_FINDINGS**: Optional boolean value (default `false`) that determines whether the tags applied to the test are also propagated to each individual finding when importing or reimporting into DefectDojo. + - If set to `true`, DefectDojo applies the test tags to every finding. + - If set to `false` (default), the tags remain only at the test level. - **CMDB**: Configuration for the integration with the Configuration Management Database (CMDB). - **USE_CMDB**: Boolean value indicating whether to use CMDB integration (`true` or `false`). - **HOST_CMDB**: URL endpoint for the CMDB API. @@ -489,6 +493,7 @@ Then, the remote config settings should look similar to this: "MAX_RETRIES_QUERY": 5, "REGEX_EXPRESSION_CODE_APP": "^([^-]+)", "REIMPORT_SCAN": false, + "APPLY_TAGS_TO_FINDINGS": false, "CMDB": { "USE_CMDB": true, "HOST_CMDB": "http://host_cmdb_example", @@ -562,6 +567,7 @@ The remote config settings should look similar to this: "MAX_RETRIES_QUERY": 5, "REGEX_EXPRESSION_CODE_APP": "^([^-]+)", "REIMPORT_SCAN": false, + "APPLY_TAGS_TO_FINDINGS": false, "CMDB": { "USE_CMDB": false } diff --git a/example_remote_config_local/engine_core/ConfigTool.json b/example_remote_config_local/engine_core/ConfigTool.json index 2d395833c..2faa68f0c 100644 --- a/example_remote_config_local/engine_core/ConfigTool.json +++ b/example_remote_config_local/engine_core/ConfigTool.json @@ -37,6 +37,7 @@ }, "REGEX_EXPRESSION_CODE_APP": "^([^-]+)", "REIMPORT_SCAN": false, + "APPLY_TAGS_TO_FINDINGS": false, "CMDB": { "USE_CMDB": false, "HOST_CMDB": "",