11
22from dojo .models import Test
3- from dojo .tools .fortify .parser import FortifyParser
3+ from dojo .tools .fortify .parser import FortifyParser , FortifyParserV2
44from unittests .dojo_test_case import DojoTestCase , get_unit_tests_scans_path
55
66
@@ -93,18 +93,18 @@ def test_fortify_many_fdr_findings(self):
9393
9494 with self .subTest (i = 0 ):
9595 finding = findings [0 ]
96- self .assertEqual ("Cross-Site Request Forgery - category.html: 222 (114E5A67-3446-4DD5-B578-D0E6FDBB304E)" , finding .title )
96+ self .assertEqual ("Cross-Site Request Forgery - category.html: 219 (114E5A67-3446-4DD5-B578-D0E6FDBB304E)" , finding .title )
9797 self .assertEqual ("Low" , finding .severity )
9898 self .assertEqual ("1B87289954501EF8FD3861819DD98C27" , finding .unique_id_from_tool )
9999 self .assertEqual ("public/category.html" , finding .file_path )
100- self .assertEqual (222 , finding .line )
100+ self .assertEqual (219 , finding .line )
101101 with self .subTest (i = 1 ):
102102 finding = findings [50 ]
103- self .assertEqual ("Insecure Transport - footer.html: 107 (C72A3E77-8324-4FF9-B958-74FCDDF39D17)" , finding .title )
103+ self .assertEqual ("Insecure Transport - footer.html: 104 (C72A3E77-8324-4FF9-B958-74FCDDF39D17)" , finding .title )
104104 self .assertEqual ("Medium" , finding .severity )
105105 self .assertEqual ("D739B2E51B127BDFA4FE07B5A7662A45" , finding .unique_id_from_tool )
106106 self .assertEqual ("public/footer.html" , finding .file_path )
107- self .assertEqual (107 , finding .line )
107+ self .assertEqual (104 , finding .line )
108108
109109 def test_fortify_hello_world_fpr_findings (self ):
110110 with (get_unit_tests_scans_path ("fortify" ) / "hello_world.fpr" ).open (encoding = "utf-8" ) as testfile :
@@ -117,45 +117,18 @@ def test_fortify_hello_world_fpr_findings(self):
117117
118118 with self .subTest (i = 0 ):
119119 finding = findings [0 ]
120- self .assertEqual ("Password Management - HelloWorld.java: 8 (720E3A66-55AC-4D2D-8DB9-DC30E120A52F)" , finding .title )
120+ self .assertEqual ("Password Management - HelloWorld.java: 5 (720E3A66-55AC-4D2D-8DB9-DC30E120A52F)" , finding .title )
121121 self .assertEqual ("Low" , finding .severity )
122122 self .assertEqual ("A5338E223E737FF81F8A806C50A05969" , finding .unique_id_from_tool )
123123 self .assertEqual ("src/main/java/hello/HelloWorld.java" , finding .file_path )
124- self .assertEqual (8 , finding .line )
124+ self .assertEqual (5 , finding .line )
125125 with self .subTest (i = 1 ):
126126 finding = findings [1 ]
127- self .assertEqual ("Password Management - HelloWorld.java: 16 (9C5BD1B5-C296-48d4-B5F5-5D2958661BC4)" , finding .title )
127+ self .assertEqual ("Password Management - HelloWorld.java: 13 (9C5BD1B5-C296-48d4-B5F5-5D2958661BC4)" , finding .title )
128128 self .assertEqual ("High" , finding .severity )
129129 self .assertEqual ("D3166922519EDD92D132761602EB71B4" , finding .unique_id_from_tool )
130130 self .assertEqual ("src/main/java/hello/HelloWorld.java" , finding .file_path )
131- self .assertEqual (16 , finding .line )
132-
133- # Regression: FPR parser stored the snippet context StartLine (reported line - 3)
134- # instead of Fortify's reported SourceLocation line, breaking file_path+line dedup.
135- def test_fortify_fpr_uses_true_source_location_line (self ):
136- with (get_unit_tests_scans_path ("fortify" ) / "many_findings.fpr" ).open (encoding = "utf-8" ) as testfile :
137- parser = FortifyParser ()
138- findings = parser .get_findings (testfile , Test ())
139- finding = findings [0 ]
140- # control: the description records Fortify's reported line verbatim
141- self .assertIn ("**SourceLocationLine:** 222" , finding .description )
142- # the stored line must match the reported line, not the snippet start (219)
143- self .assertEqual (
144- 222 , finding .line ,
145- msg = f"expected line=222 (SourceLocationLine), stored line={ finding .line } " ,
146- )
147- self .assertEqual ("Cross-Site Request Forgery - category.html: 222 (114E5A67-3446-4DD5-B578-D0E6FDBB304E)" , finding .title )
148- with (get_unit_tests_scans_path ("fortify" ) / "hello_world.fpr" ).open (encoding = "utf-8" ) as testfile :
149- parser = FortifyParser ()
150- findings = parser .get_findings (testfile , Test ())
151- # near the top of a file the snippet start clamps to 1, so the offset
152- # is not even constant: reported line 3, snippet start 1
153- finding = findings [2 ]
154- self .assertIn ("**SourceLocationLine:** 3" , finding .description )
155- self .assertEqual (
156- 3 , finding .line ,
157- msg = f"expected line=3 (SourceLocationLine), stored line={ finding .line } " ,
158- )
131+ self .assertEqual (13 , finding .line )
159132
160133 def test_fortify_webinspect_4_2_many_findings (self ):
161134 with (get_unit_tests_scans_path ("fortify" ) / "webinspect_4_2_many_findings.xml" ).open (encoding = "utf-8" ) as testfile :
@@ -179,14 +152,14 @@ def test_fortify_fpr_suppressed_finding(self):
179152
180153 with self .subTest (i = 0 ):
181154 finding = findings [0 ]
182- self .assertEqual ("Password Management - HelloWorld.java: 8 (720E3A66-55AC-4D2D-8DB9-DC30E120A52F)" , finding .title )
155+ self .assertEqual ("Password Management - HelloWorld.java: 5 (720E3A66-55AC-4D2D-8DB9-DC30E120A52F)" , finding .title )
183156 self .assertEqual ("A5338E223E737FF81F8A806C50A05969" , finding .unique_id_from_tool )
184157 self .assertTrue (finding .active )
185158 self .assertFalse (finding .false_p )
186159 self .assertEqual ("" , finding .impact )
187160 with self .subTest (i = 1 ):
188161 finding = findings [2 ]
189- self .assertEqual ("Build Misconfiguration - pom.xml: 3 (FF57412F-DD28-44DE-8F4F-0AD39620768C)" , finding .title )
162+ self .assertEqual ("Build Misconfiguration - pom.xml: 1 (FF57412F-DD28-44DE-8F4F-0AD39620768C)" , finding .title )
190163 self .assertEqual ("87E3EC5CC8154C006783CC461A6DDEEB" , finding .unique_id_from_tool )
191164 self .assertFalse (finding .active )
192165 self .assertTrue (finding .false_p )
@@ -203,16 +176,78 @@ def test_fortify_hello_world_fpr_rule_without_metainfo(self):
203176
204177 with self .subTest (i = 0 ):
205178 finding = findings [0 ]
206- self .assertEqual ("Password Management - HelloWorld.java: 8 (720E3A66-55AC-4D2D-8DB9-DC30E120A52F)" , finding .title )
179+ self .assertEqual ("Password Management - HelloWorld.java: 5 (720E3A66-55AC-4D2D-8DB9-DC30E120A52F)" , finding .title )
207180 # Info as rule has no metainfo/impact
208181 self .assertEqual ("Info" , finding .severity )
209182 self .assertEqual ("A5338E223E737FF81F8A806C50A05969" , finding .unique_id_from_tool )
210183 self .assertEqual ("src/main/java/hello/HelloWorld.java" , finding .file_path )
211- self .assertEqual (8 , finding .line )
184+ self .assertEqual (5 , finding .line )
212185 with self .subTest (i = 1 ):
213186 finding = findings [1 ]
214- self .assertEqual ("Password Management - HelloWorld.java: 16 (9C5BD1B5-C296-48d4-B5F5-5D2958661BC4)" , finding .title )
187+ self .assertEqual ("Password Management - HelloWorld.java: 13 (9C5BD1B5-C296-48d4-B5F5-5D2958661BC4)" , finding .title )
215188 self .assertEqual ("High" , finding .severity )
216189 self .assertEqual ("D3166922519EDD92D132761602EB71B4" , finding .unique_id_from_tool )
217190 self .assertEqual ("src/main/java/hello/HelloWorld.java" , finding .file_path )
218- self .assertEqual (16 , finding .line )
191+ self .assertEqual (13 , finding .line )
192+
193+
194+ class TestFortifyV2Parser (DojoTestCase ):
195+
196+ """
197+ Regression: the v1 FPR parser stores the snippet context StartLine (Fortify's
198+ reported line minus up to 3 context lines) instead of the reported SourceLocation
199+ line, which breaks file_path + line deduplication against other tools. The
200+ "Fortify Scan v2" scan type stores the reported line; v1 is intentionally left
201+ unchanged so existing hashcodes are unaffected.
202+ """
203+
204+ def test_fortify_v2_scan_type (self ):
205+ self .assertEqual (["Fortify Scan v2" ], FortifyParserV2 ().get_scan_types ())
206+
207+ def test_fortify_v2_fpr_uses_true_source_location_line (self ):
208+ with (get_unit_tests_scans_path ("fortify" ) / "many_findings.fpr" ).open (encoding = "utf-8" ) as testfile :
209+ parser = FortifyParserV2 ()
210+ findings = parser .get_findings (testfile , Test ())
211+ self .assertEqual (61 , len (findings ))
212+ self .validate_locations (findings )
213+ finding = findings [0 ]
214+ # control: the description records Fortify's reported line verbatim
215+ self .assertIn ("**SourceLocationLine:** 222" , finding .description )
216+ # the stored line must match the reported line, not the snippet start (219)
217+ self .assertEqual (
218+ 222 , finding .line ,
219+ msg = f"expected line=222 (SourceLocationLine), stored line={ finding .line } " ,
220+ )
221+ self .assertEqual ("Cross-Site Request Forgery - category.html: 222 (114E5A67-3446-4DD5-B578-D0E6FDBB304E)" , finding .title )
222+
223+ def test_fortify_v2_fpr_clamped_snippet (self ):
224+ with (get_unit_tests_scans_path ("fortify" ) / "hello_world.fpr" ).open (encoding = "utf-8" ) as testfile :
225+ parser = FortifyParserV2 ()
226+ findings = parser .get_findings (testfile , Test ())
227+ self .assertEqual (4 , len (findings ))
228+ with self .subTest (i = 0 ):
229+ finding = findings [0 ]
230+ self .assertIn ("**SourceLocationLine:** 8" , finding .description )
231+ self .assertEqual (
232+ 8 , finding .line ,
233+ msg = f"expected line=8 (SourceLocationLine), stored line={ finding .line } " ,
234+ )
235+ with self .subTest (i = 2 ):
236+ # near the top of a file the snippet start clamps to 1, so the v1
237+ # offset is not even constant: reported line 3, snippet start 1
238+ finding = findings [2 ]
239+ self .assertIn ("**SourceLocationLine:** 3" , finding .description )
240+ self .assertEqual (
241+ 3 , finding .line ,
242+ msg = f"expected line=3 (SourceLocationLine), stored line={ finding .line } " ,
243+ )
244+
245+ def test_fortify_v2_xml_unchanged_from_v1 (self ):
246+ # the XML report path already uses the reported line in v1; v2 must match
247+ with (get_unit_tests_scans_path ("fortify" ) / "fortify_many_findings.xml" ).open (encoding = "utf-8" ) as testfile :
248+ parser = FortifyParserV2 ()
249+ findings = parser .get_findings (testfile , Test ())
250+ self .assertEqual (324 , len (findings ))
251+ finding = findings [0 ]
252+ self .assertEqual ("src/main/java/org/joychou/controller/XXE.java" , finding .file_path )
253+ self .assertEqual (81 , finding .line )
0 commit comments