diff --git a/dojo/tools/trivy/parser.py b/dojo/tools/trivy/parser.py index fa41dd6c350..25746cd4089 100644 --- a/dojo/tools/trivy/parser.py +++ b/dojo/tools/trivy/parser.py @@ -344,7 +344,7 @@ def get_result_items(self, test, results, service_name=None, artifact_name=""): service=service_name, **status_fields, ) - finding.unsaved_tags = [vul_type, target_class] + finding.unsaved_tags = [tag for tag in (vul_type, target_class) if tag] if vuln_id: finding.unsaved_vulnerability_ids = [vuln_id] @@ -405,7 +405,7 @@ def get_result_items(self, test, results, service_name=None, artifact_name=""): if misc_avdid: finding.unsaved_vulnerability_ids = [] finding.unsaved_vulnerability_ids.append(misc_avdid) - finding.unsaved_tags = [target_type, target_class] + finding.unsaved_tags = [tag for tag in (target_type, target_class) if tag] items.append(finding) secrets = target_data.get("Secrets", []) @@ -436,7 +436,7 @@ def get_result_items(self, test, results, service_name=None, artifact_name=""): fix_available=True, service=service_name, ) - finding.unsaved_tags = [target_class] + finding.unsaved_tags = [tag for tag in (target_class,) if tag] items.append(finding) licenses = target_data.get("Licenses", []) @@ -470,7 +470,7 @@ def get_result_items(self, test, results, service_name=None, artifact_name=""): fix_available=True, service=service_name, ) - finding.unsaved_tags = [target_class] + finding.unsaved_tags = [tag for tag in (target_class,) if tag] items.append(finding) return items diff --git a/dojo/validators.py b/dojo/validators.py index 4c7c4f29d24..7c5bae5beea 100644 --- a/dojo/validators.py +++ b/dojo/validators.py @@ -38,8 +38,10 @@ def clean_tags(value: str | list[str], exception_class: Callable = ValidationErr return value if isinstance(value, list): - # Replace ALL occurrences of problematic characters in each tag - return [TAG_PATTERN.sub("_", tag) for tag in value] + # Replace ALL occurrences of problematic characters in each tag. + # Parsers can emit None tags (e.g. optional report fields); drop them + # instead of crashing the import pipeline (TypeError in re.sub). + return [TAG_PATTERN.sub("_", tag) for tag in value if tag is not None] if isinstance(value, str): # Replace ALL occurrences of problematic characters in the tag diff --git a/unittests/test_validators.py b/unittests/test_validators.py new file mode 100644 index 00000000000..9a98974e28c --- /dev/null +++ b/unittests/test_validators.py @@ -0,0 +1,32 @@ +from django.core.exceptions import ValidationError + +from dojo.validators import clean_tags +from unittests.dojo_test_case import DojoTestCase + + +class TestCleanTags(DojoTestCase): + + def test_clean_tags_string(self): + self.assertEqual("simple_tag", clean_tags("simple tag")) + + def test_clean_tags_list(self): + self.assertEqual(["tag_one", "tag_two"], clean_tags(["tag one", "tag,two"])) + + def test_clean_tags_empty_values(self): + self.assertEqual([], clean_tags([])) + self.assertEqual("", clean_tags("")) + self.assertIsNone(clean_tags(None)) + + def test_clean_tags_list_with_none_entries(self): + """ + Parsers can emit None tags (e.g. Trivy legacy reports without a + "Class" field). clean_tags must filter them out instead of raising + TypeError from the regex (see import pipeline crash in + default_importer._process_findings_internal). + """ + self.assertEqual(["os-pkgs"], clean_tags(["os-pkgs", None])) + self.assertEqual([], clean_tags([None, None])) + + def test_clean_tags_invalid_type_raises(self): + with self.assertRaises(ValidationError): + clean_tags(42) diff --git a/unittests/tools/test_trivy_parser.py b/unittests/tools/test_trivy_parser.py index f43b079ea18..9aef80e263e 100644 --- a/unittests/tools/test_trivy_parser.py +++ b/unittests/tools/test_trivy_parser.py @@ -22,6 +22,10 @@ def test_legacy_many_vulns(self): parser = TrivyParser() findings = parser.get_findings(test_file, Test()) self.assertEqual(len(findings), 93) + # Legacy reports have no "Class" field; tags must not contain None + # or the import pipeline crashes in clean_tags (TypeError) + for finding in findings: + self.assertNotIn(None, finding.unsaved_tags) finding = findings[0] self.assertEqual("Low", finding.severity) self.assertEqual(1, len(finding.unsaved_vulnerability_ids))