diff --git a/dojo/tools/coverity_api/parser.py b/dojo/tools/coverity_api/parser.py index fdd6332b2fa..3b3a72a7862 100644 --- a/dojo/tools/coverity_api/parser.py +++ b/dojo/tools/coverity_api/parser.py @@ -26,8 +26,11 @@ def get_findings(self, file, test): items = [] for issue in tree["viewContentsV1"]["rows"]: - # get only security findings - if issue.get("displayIssueKind") != "Security": + # get security findings and Quality RESOURCE_LEAK findings + if not ( + issue.get("displayIssueKind") == "Security" + or (issue.get("displayIssueKind") == "Quality" and issue.get("checker") == "RESOURCE_LEAK") + ): continue description_formated = "\n".join( diff --git a/unittests/scans/coverity_api/only_non_resource_leak_quality.json b/unittests/scans/coverity_api/only_non_resource_leak_quality.json new file mode 100644 index 00000000000..c8ec40de596 --- /dev/null +++ b/unittests/scans/coverity_api/only_non_resource_leak_quality.json @@ -0,0 +1,100 @@ +{ + "viewContentsV1": { + "offset": 0, + "totalRows": 3, + "columns": [ + {"name": "cid", "label": "CID"}, + {"name": "displayType", "label": "Type"}, + {"name": "displayImpact", "label": "Impact"}, + {"name": "status", "label": "Status"}, + {"name": "firstDetected", "label": "First Detected"}, + {"name": "owner", "label": "Owner"}, + {"name": "classification", "label": "Classification"}, + {"name": "severity", "label": "Severity"}, + {"name": "action", "label": "Action"}, + {"name": "displayComponent", "label": "Component"}, + {"name": "displayCategory", "label": "Category"}, + {"name": "displayFile", "label": "File"}, + {"name": "displayFunction", "label": "Function"}, + {"name": "displayIssueKind", "label": "Issue Kind"}, + {"name": "lastDetected", "label": "Last Snapshot Date"}, + {"name": "checker", "label": "Checker"}, + {"name": "occurrenceCount", "label": "Count"}, + {"name": "cwe", "label": "CWE"}, + {"name": "lastTriaged", "label": "Last Triaged"}, + {"name": "externalReference", "label": "External Reference"}, + {"name": "lastDetectedTarget", "label": "Last Snapshot Target"} + ], + "rows": [ + { + "cid": 10001, + "displayType": "Dereference null return value", + "displayImpact": "Medium", + "status": "New", + "firstDetected": "03/23/21", + "owner": "Unassigned", + "classification": "Unclassified", + "severity": "Unspecified", + "action": "Undecided", + "displayComponent": "Other", + "displayCategory": "Null pointer dereferences", + "displayFile": "Aaaaaa/Bbbbbb/Cccccc/Dddddd.rs", + "displayFunction": "Eeeeee.Ffffff.Gggggg::Hhhhhh", + "displayIssueKind": "Quality", + "lastDetected": "04/07/21", + "checker": "NULL_RETURNS", + "occurrenceCount": 1, + "cwe": 476, + "lastTriaged": "", + "externalReference": "", + "lastDetectedTarget": "" + }, + { + "cid": 10002, + "displayType": "Explicit null dereferenced", + "displayImpact": "Medium", + "status": "New", + "firstDetected": "03/23/21", + "owner": "Unassigned", + "classification": "Unclassified", + "severity": "Unspecified", + "action": "Undecided", + "displayComponent": "Other", + "displayCategory": "Null pointer dereferences", + "displayFile": "Iiiiii/Jjjjjj/Kkkkkk/Llllll.rs", + "displayFunction": "Mmmmmm.Nnnnnn.Oooooo::Pppppp", + "displayIssueKind": "Quality", + "lastDetected": "04/07/21", + "checker": "FORWARD_NULL", + "occurrenceCount": 2, + "cwe": 476, + "lastTriaged": "", + "externalReference": "", + "lastDetectedTarget": "" + }, + { + "cid": 10003, + "displayType": "Dereference null return (stat)", + "displayImpact": "Medium", + "status": "New", + "firstDetected": "03/23/21", + "owner": "Unassigned", + "classification": "Unclassified", + "severity": "Unspecified", + "action": "Undecided", + "displayComponent": "Other", + "displayCategory": "Null pointer dereferences", + "displayFile": "Qqqqqq/Rrrrrr/Ssssss/Tttttt.rs", + "displayFunction": "Uuuuuu.Vvvvvv.Wwwwww::Xxxxxx", + "displayIssueKind": "Quality", + "lastDetected": "04/07/21", + "checker": "NULL_RETURNS", + "occurrenceCount": 1, + "cwe": 476, + "lastTriaged": "", + "externalReference": "", + "lastDetectedTarget": "" + } + ] + } +} diff --git a/unittests/tools/test_coverity_api_parser.py b/unittests/tools/test_coverity_api_parser.py index d93e6c5f623..3e9de920671 100644 --- a/unittests/tools/test_coverity_api_parser.py +++ b/unittests/tools/test_coverity_api_parser.py @@ -19,8 +19,8 @@ def test_parse_no_findings(self): self.assertEqual(0, len(findings)) def test_parse_only_quality(self): - """This report only have quality findings""" - with (get_unit_tests_scans_path("coverity_api") / "only_quality.json").open(encoding="utf-8") as testfile: + """Non-RESOURCE_LEAK quality findings are excluded""" + with (get_unit_tests_scans_path("coverity_api") / "only_non_resource_leak_quality.json").open(encoding="utf-8") as testfile: parser = CoverityApiParser() findings = parser.get_findings(testfile, Test()) self.assertEqual(0, len(findings)) @@ -30,11 +30,21 @@ def test_parse_some_findings(self): parser = CoverityApiParser() findings = parser.get_findings(testfile, Test()) self.assertIsInstance(findings, list) - self.assertEqual(1, len(findings)) + self.assertEqual(6, len(findings)) with self.subTest(i=0): - finding = findings[0] + finding = findings[0] # first RESOURCE_LEAK finding self.assertTrue(finding.active) - self.assertFalse(finding.verified) # this one is marked as new ("status": "New") + self.assertFalse(finding.verified) + self.assertEqual("Resource leak", finding.title) + self.assertEqual("High", finding.severity) + self.assertEqual(404, finding.cwe) + self.assertEqual("Wdkrtgthhl/Llwfzgphzw/Fashvkaxzx/Okkfacqsxw.rs", finding.file_path) + self.assertEqual(datetime.date(2021, 3, 23), finding.date) + self.assertEqual(22480, finding.unique_id_from_tool) + with self.subTest(i=4): + finding = findings[4] # security finding + self.assertTrue(finding.active) + self.assertFalse(finding.verified) self.assertEqual("Risky cryptographic hashing function", finding.title) self.assertEqual("Medium", finding.severity) self.assertEqual(328, finding.cwe) @@ -47,9 +57,9 @@ def test_parse_few_findings_triaged_as_bug(self): parser = CoverityApiParser() findings = parser.get_findings(testfile, Test()) self.assertIsInstance(findings, list) - self.assertEqual(1, len(findings)) - with self.subTest(i=0): - finding = findings[0] + self.assertEqual(13, len(findings)) + with self.subTest(i=1): + finding = findings[1] # security finding (triaged as bug) self.assertTrue(finding.active) self.assertTrue(finding.verified) self.assertEqual("HTTP header injection", finding.title) @@ -64,9 +74,19 @@ def test_parse_some_findings_mitigated(self): parser = CoverityApiParser() findings = parser.get_findings(testfile, Test()) self.assertIsInstance(findings, list) - self.assertEqual(20, len(findings)) + self.assertEqual(25, len(findings)) with self.subTest(i=0): - finding = findings[0] # this one is dismissed as a false positive + finding = findings[0] # RESOURCE_LEAK finding (active, status New) + self.assertTrue(finding.active) + self.assertFalse(finding.verified) + self.assertEqual("Resource leak", finding.title) + self.assertEqual("High", finding.severity) + self.assertEqual(404, finding.cwe) + self.assertEqual("Vzfkposilb/Ejgmugyeam/Ekcbsjzuiq/Isjhjabnfe.rs", finding.file_path) + self.assertEqual(datetime.date(2021, 3, 31), finding.date) + self.assertEqual(22496, finding.unique_id_from_tool) + with self.subTest(i=2): + finding = findings[2] # this one is dismissed as a false positive self.assertFalse(finding.active) self.assertTrue(finding.verified) self.assertTrue(finding.false_p) @@ -76,8 +96,8 @@ def test_parse_some_findings_mitigated(self): self.assertEqual("Pfozpmtueo/Vtoqmbvmzf/Noxacjclcz/Aymctwefbi.rs", finding.file_path) self.assertEqual(datetime.date(2021, 3, 26), finding.date) self.assertEqual(22486, finding.unique_id_from_tool) - with self.subTest(i=10): - finding = findings[10] + with self.subTest(i=12): + finding = findings[12] self.assertFalse(finding.active) self.assertTrue(finding.verified) self.assertEqual("Use of hard-coded password", finding.title) @@ -86,8 +106,8 @@ def test_parse_some_findings_mitigated(self): self.assertEqual("Hvsilgzkwz/Lhmxrchybr/Edcoanzncg/Oowieyoxvn.rs", finding.file_path) self.assertEqual(datetime.date(2021, 3, 15), finding.date) self.assertEqual(22421, finding.unique_id_from_tool) - with self.subTest(i=19): - finding = findings[19] + with self.subTest(i=23): + finding = findings[23] self.assertFalse(finding.active) self.assertTrue(finding.verified) self.assertEqual("Cross-site scripting", finding.title)