Skip to content

Commit 595c226

Browse files
feat(parsers): add fix_version support to Generic Findings Import (#14307)
The fix_version field exists on the Finding model and is already handled by the reimporter (default_reimporter.py lines 683, 803), but was missing from the Generic Findings Import parser for both JSON and CSV formats. Changes: - json_parser.py: add fix_version to allowed fields set - csv_parser.py: add fix_version field parsing - Add unit tests for both JSON and CSV - Update documentation Co-authored-by: vladimir <stolzyya@gmail.com>
1 parent 656bbe6 commit 595c226

6 files changed

Lines changed: 67 additions & 0 deletions

File tree

docs/content/supported_tools/parsers/file/generic.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Generic Findings Import can be used to import any report in CSV or JSON format.
3131
- known_exploited: Indicator if the finding is listed in Known Exploited List. Must be TRUE, or FALSE
3232
- ransomware_used: Indicator if the finding is used in Ransomware. Must be TRUE, or FALSE
3333
- fix_available: Indicator if fix available for the finding. Must be TRUE, or FALSE
34+
- fix_version: Version where fix is available. String value.
3435
- kev_date: Date the finding was added to Known Exploited Vulnerabilities list in mm/dd/yyyy format or ISO format.
3536

3637
The CSV expects a header row with the names of the attributes.
@@ -94,6 +95,7 @@ The list of supported fields in JSON format:
9495
- known_exploited: Bool
9596
- ransomware_used: Bool
9697
- fix_available: Bool
98+
- fix_version: String
9799

98100
### Example JSON
99101

@@ -114,6 +116,7 @@ The list of supported fields in JSON format:
114116
"known_exploited": true,
115117
"ransomware_used": true,
116118
"fix_available": true,
119+
"fix_version": "0.0.00",
117120
"kev_date": "2024-05-01",
118121
"file_path": "src/first.cpp",
119122
"line": 13,

dojo/tools/generic/csv_parser.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ def _get_findings_csv(self, filename):
103103
if "fix_available" in row:
104104
finding.fix_available = bool(row["fix_available"])
105105

106+
if "fix_version" in row:
107+
finding.fix_version = row["fix_version"]
108+
106109
# manage endpoints
107110
if row.get("Url"):
108111
if settings.V3_FEATURE_LOCATIONS:

dojo/tools/generic/json_parser.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ def _get_test_json(self, data):
110110
"known_exploited",
111111
"ransomware_used",
112112
"fix_available",
113+
"fix_version",
113114
}.union(required)
114115
not_allowed = sorted(set(item).difference(allowed))
115116
if not_allowed:
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Date,Title,CweId,Url,Severity,Description,Mitigation,Impact,References,Active,Verified,fix_available,fix_version
2+
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
3+
01/15/2025,Test finding without fix_version,611,,Medium,Vulnerability without fix version,,,,TRUE,FALSE,FALSE,
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"findings": [
3+
{
4+
"title": "test title with fix_version",
5+
"description": "Vulnerability with fix version available",
6+
"severity": "High",
7+
"date": "2025-01-15",
8+
"cve": "CVE-2025-12345",
9+
"cwe": 502,
10+
"fix_available": true,
11+
"fix_version": "2.1.3",
12+
"component_name": "spring-core",
13+
"component_version": "6.1.0"
14+
},
15+
{
16+
"title": "test title without fix_version",
17+
"description": "Vulnerability without fix version",
18+
"severity": "Medium",
19+
"date": "2025-01-15",
20+
"cve": "CVE-2025-67890",
21+
"cwe": 611,
22+
"fix_available": false
23+
}
24+
]
25+
}

unittests/tools/test_generic_parser.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,3 +684,35 @@ def test_parse_json_custom_test_with_meta(self):
684684
self.assertEqual(test.description, "The contents of this report is from a tool that gathers vulnerabilities both statically and dynamically")
685685
self.assertEqual(test.dynamic_tool, True)
686686
self.assertEqual(test.static_tool, True)
687+
688+
def test_parse_json_with_fix_version(self):
689+
with (get_unit_tests_scans_path("generic") / "generic_report_fix_version.json").open(encoding="utf-8") as file:
690+
parser = GenericParser()
691+
findings = parser.get_findings(file, self.test)
692+
self.validate_locations(findings)
693+
self.assertEqual(2, len(findings))
694+
695+
finding = findings[0]
696+
self.assertEqual("test title with fix_version", finding.title)
697+
self.assertTrue(finding.fix_available)
698+
self.assertEqual("2.1.3", finding.fix_version)
699+
self.assertEqual("spring-core", finding.component_name)
700+
701+
finding = findings[1]
702+
self.assertEqual("test title without fix_version", finding.title)
703+
self.assertFalse(finding.fix_available)
704+
self.assertIsNone(finding.fix_version)
705+
706+
def test_parse_csv_with_fix_version(self):
707+
with (get_unit_tests_scans_path("generic") / "generic_report_fix_version.csv").open(encoding="utf-8") as file:
708+
parser = GenericParser()
709+
findings = parser.get_findings(file, self.test)
710+
self.validate_locations(findings)
711+
self.assertEqual(2, len(findings))
712+
713+
finding = findings[0]
714+
self.assertEqual("Test finding with fix_version", finding.title)
715+
self.assertEqual("2.1.3", finding.fix_version)
716+
717+
finding = findings[1]
718+
self.assertEqual("Test finding without fix_version", finding.title)

0 commit comments

Comments
 (0)