Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/content/supported_tools/parsers/file/generic.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Generic Findings Import can be used to import any report in CSV or JSON format.
- known_exploited: Indicator if the finding is listed in Known Exploited List. Must be TRUE, or FALSE
- ransomware_used: Indicator if the finding is used in Ransomware. Must be TRUE, or FALSE
- fix_available: Indicator if fix available for the finding. Must be TRUE, or FALSE
- fix_version: Version where fix is available. String value.
- kev_date: Date the finding was added to Known Exploited Vulnerabilities list in mm/dd/yyyy format or ISO format.

The CSV expects a header row with the names of the attributes.
Expand Down Expand Up @@ -94,6 +95,7 @@ The list of supported fields in JSON format:
- known_exploited: Bool
- ransomware_used: Bool
- fix_available: Bool
- fix_version: String

### Example JSON

Expand All @@ -114,6 +116,7 @@ The list of supported fields in JSON format:
"known_exploited": true,
"ransomware_used": true,
"fix_available": true,
"fix_version": "0.0.00",
"kev_date": "2024-05-01",
"file_path": "src/first.cpp",
"line": 13,
Expand Down
3 changes: 3 additions & 0 deletions dojo/tools/generic/csv_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ def _get_findings_csv(self, filename):
if "fix_available" in row:
finding.fix_available = bool(row["fix_available"])

if "fix_version" in row:
finding.fix_version = row["fix_version"]

# manage endpoints
if row.get("Url"):
if settings.V3_FEATURE_LOCATIONS:
Expand Down
1 change: 1 addition & 0 deletions dojo/tools/generic/json_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ def _get_test_json(self, data):
"known_exploited",
"ransomware_used",
"fix_available",
"fix_version",
}.union(required)
not_allowed = sorted(set(item).difference(allowed))
if not_allowed:
Expand Down
3 changes: 3 additions & 0 deletions unittests/scans/generic/generic_report_fix_version.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Date,Title,CweId,Url,Severity,Description,Mitigation,Impact,References,Active,Verified,fix_available,fix_version
01/15/2025,Test finding with fix_version,502,,High,Vulnerability with fix version available,Upgrade to 2.1.3,,,TRUE,FALSE,TRUE,2.1.3
01/15/2025,Test finding without fix_version,611,,Medium,Vulnerability without fix version,,,,TRUE,FALSE,FALSE,
25 changes: 25 additions & 0 deletions unittests/scans/generic/generic_report_fix_version.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"findings": [
{
"title": "test title with fix_version",
"description": "Vulnerability with fix version available",
"severity": "High",
"date": "2025-01-15",
"cve": "CVE-2025-12345",
"cwe": 502,
"fix_available": true,
"fix_version": "2.1.3",
"component_name": "spring-core",
"component_version": "6.1.0"
},
{
"title": "test title without fix_version",
"description": "Vulnerability without fix version",
"severity": "Medium",
"date": "2025-01-15",
"cve": "CVE-2025-67890",
"cwe": 611,
"fix_available": false
}
]
}
32 changes: 32 additions & 0 deletions unittests/tools/test_generic_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -684,3 +684,35 @@ def test_parse_json_custom_test_with_meta(self):
self.assertEqual(test.description, "The contents of this report is from a tool that gathers vulnerabilities both statically and dynamically")
self.assertEqual(test.dynamic_tool, True)
self.assertEqual(test.static_tool, True)

def test_parse_json_with_fix_version(self):
with (get_unit_tests_scans_path("generic") / "generic_report_fix_version.json").open(encoding="utf-8") as file:
parser = GenericParser()
findings = parser.get_findings(file, self.test)
self.validate_locations(findings)
self.assertEqual(2, len(findings))

finding = findings[0]
self.assertEqual("test title with fix_version", finding.title)
self.assertTrue(finding.fix_available)
self.assertEqual("2.1.3", finding.fix_version)
self.assertEqual("spring-core", finding.component_name)

finding = findings[1]
self.assertEqual("test title without fix_version", finding.title)
self.assertFalse(finding.fix_available)
self.assertIsNone(finding.fix_version)

def test_parse_csv_with_fix_version(self):
with (get_unit_tests_scans_path("generic") / "generic_report_fix_version.csv").open(encoding="utf-8") as file:
parser = GenericParser()
findings = parser.get_findings(file, self.test)
self.validate_locations(findings)
self.assertEqual(2, len(findings))

finding = findings[0]
self.assertEqual("Test finding with fix_version", finding.title)
self.assertEqual("2.1.3", finding.fix_version)

finding = findings[1]
self.assertEqual("Test finding without fix_version", finding.title)