@@ -67,8 +67,11 @@ public class SqlInjectionReportingServiceIntegrationTest extends BaseLoanIntegra
6767 private RequestSpecification requestSpec ;
6868 private ResponseSpecification responseSpec ;
6969 private Long testReportId = null ;
70+ private Long booleanReportId = null ;
7071 private static final String TEST_REPORT_NAME = "SQL_Injection_Test_Report" ;
7172 private static final String TEST_REPORT_SQL = "SELECT 1 as test_column, 'Test Data' as test_name" ;
73+ private static final String BOOLEAN_REPORT_SQL = "SELECT CAST(1 AS BOOLEAN) AS active" ;
74+ private String booleanReportName ;
7275
7376 @ BeforeEach
7477 public void setup () {
@@ -92,6 +95,13 @@ public void cleanup() {
9295 log .warn ("Failed to clean up test report: " + e .getMessage ());
9396 }
9497 }
98+ if (booleanReportId != null ) {
99+ try {
100+ deleteBooleanReport ();
101+ } catch (Exception e ) {
102+ log .warn ("Failed to clean up boolean test report: " + e .getMessage ());
103+ }
104+ }
95105 }
96106
97107 private void createTestReportIfNotExists () {
@@ -161,6 +171,31 @@ private void createTestReportIfNotExists() {
161171 }
162172 }
163173
174+ private void createBooleanReport () {
175+ booleanReportName = "BOOLEAN_Runreports_Test_Report_" + System .currentTimeMillis ();
176+
177+ String reportJson = "{" + "\" reportName\" : \" " + booleanReportName + "\" ," + "\" reportType\" : \" Table\" ,"
178+ + "\" reportCategory\" : \" Client\" ," + "\" reportSql\" : \" " + BOOLEAN_REPORT_SQL + "\" ,"
179+ + "\" description\" : \" Test report for BOOLEAN runreports support\" ," + "\" useReport\" : true" + "}" ;
180+
181+ Response postResponse = given ().spec (requestSpec ).contentType (ContentType .JSON ).body (reportJson ).when ()
182+ .post ("/fineract-provider/api/v1/reports" );
183+
184+ if (postResponse .getStatusCode () == 200 || postResponse .getStatusCode () == 201 ) {
185+ String response = postResponse .asString ();
186+ if (response .contains ("resourceId" )) {
187+ String idStr = response .replaceAll (".*\" resourceId\" :(\\ d+).*" , "$1" );
188+ booleanReportId = Long .parseLong (idStr );
189+ log .info ("Created BOOLEAN test report with ID: {}" , booleanReportId );
190+ } else {
191+ throw new RuntimeException ("BOOLEAN test report creation failed - no resourceId in response: " + response );
192+ }
193+ } else {
194+ throw new RuntimeException (
195+ "BOOLEAN test report creation failed with status " + postResponse .getStatusCode () + ": " + postResponse .asString ());
196+ }
197+ }
198+
164199 private void deleteTestReport () {
165200 if (testReportId != null ) {
166201 try {
@@ -172,6 +207,17 @@ private void deleteTestReport() {
172207 }
173208 }
174209
210+ private void deleteBooleanReport () {
211+ if (booleanReportId != null ) {
212+ try {
213+ Utils .performServerDelete (requestSpec , responseSpec , "/fineract-provider/api/v1/reports/" + booleanReportId , "" );
214+ log .info ("Deleted BOOLEAN test report with ID: {}" , booleanReportId );
215+ } catch (Exception e ) {
216+ log .warn ("Failed to delete BOOLEAN test report: " + e .getMessage ());
217+ }
218+ }
219+ }
220+
175221 /**
176222 * UC1: Test legitimate report execution works correctly Validates that the SQL injection prevention doesn't break
177223 * normal functionality
@@ -215,7 +261,7 @@ void uc2_testParameterInjectionPrevention() {
215261 // This should either succeed with empty/safe results or fail with validation error
216262 // but NOT with SQL syntax errors
217263 try {
218- String response = Utils .performServerGet (requestSpec , responseSpec , "/fineract-provider/api/v1/runreports/" + TEST_REPORT_NAME
264+ Utils .performServerGet (requestSpec , responseSpec , "/fineract-provider/api/v1/runreports/" + TEST_REPORT_NAME
219265 + "?genericResultSet=false&" + toQueryString (maliciousParams ), null );
220266
221267 // If we get here, the SQL injection was prevented and handled safely
@@ -240,7 +286,7 @@ void uc2_testParameterInjectionPrevention() {
240286 * whitelist implementation for report types
241287 */
242288 @ ParameterizedTest (name = "Report Type Validation: {0}" )
243- @ ValueSource (strings = { "report" , "parameter" })
289+ @ ValueSource (strings = {"report" , "parameter" })
244290 void uc3_testValidReportTypes (String validType ) {
245291 log .info ("Testing valid report type: {}" , validType );
246292
@@ -249,7 +295,7 @@ void uc3_testValidReportTypes(String validType) {
249295
250296 // Test that valid report types work through the API
251297 try {
252- String response = Utils .performServerGet (requestSpec , responseSpec ,
298+ Utils .performServerGet (requestSpec , responseSpec ,
253299 "/runreports/TestReport?reportType=" + validType + "&genericResultSet=false&" + toQueryString (queryParams ), null );
254300 // Should get a proper response or 404 (report not found), not validation error
255301 } catch (AssertionError e ) {
@@ -262,7 +308,7 @@ void uc3_testValidReportTypes(String validType) {
262308 * UC4: Test invalid report types that should be rejected by whitelist
263309 */
264310 @ ParameterizedTest (name = "Invalid Report Type: {0}" )
265- @ ValueSource (strings = { "table" , "view" , "procedure" , "function" , "schema" , "database" , "admin" , "user" , "system" , "config" })
311+ @ ValueSource (strings = {"table" , "view" , "procedure" , "function" , "schema" , "database" , "admin" , "user" , "system" , "config" })
266312 void uc4_testInvalidReportTypes (String invalidType ) {
267313 log .info ("Testing invalid report type: {}" , invalidType );
268314
@@ -438,8 +484,8 @@ void uc8_testComplexParameterInjection() {
438484 * UC9: Test legitimate reports with various parameter types
439485 */
440486 @ ParameterizedTest (name = "Parameter Type: {0}" )
441- @ CsvSource (delimiterString = " | " , value = { "R_officeId | 1 | Numeric parameter" , "R_startDate | 2023-01-01 | Date parameter" ,
442- "R_endDate | 2023-12-31 | Date parameter" , "R_currencyId | USD | String parameter" , "R_loanProductId | 1 | Numeric parameter" })
487+ @ CsvSource (delimiterString = " | " , value = {"R_officeId | 1 | Numeric parameter" , "R_startDate | 2023-01-01 | Date parameter" ,
488+ "R_endDate | 2023-12-31 | Date parameter" , "R_currencyId | USD | String parameter" , "R_loanProductId | 1 | Numeric parameter" })
443489 void uc9_testLegitimateParameterTypes (String paramName , String paramValue , String description ) {
444490 log .info ("Testing legitimate parameter: {} = {} ({})" , paramName , paramValue , description );
445491
@@ -496,6 +542,21 @@ void uc10_testCrossDatabaseCompatibility() {
496542 }
497543 }
498544
545+ @ Test
546+ void shouldExecuteReportSuccessfullyWhenReportContainsBooleanColumn () {
547+ createBooleanReport ();
548+
549+ String response = Utils .performServerGet (requestSpec , responseSpec ,
550+ "/fineract-provider/api/v1/runreports/" + URLEncoder .encode (booleanReportName , StandardCharsets .UTF_8 )
551+ + "?genericResultSet=false" ,
552+ null );
553+
554+ assertNotNull (response );
555+ assertFalse (response .isBlank ());
556+ assertFalse (response .contains ("Data type 'BOOLEAN' is not supported" ));
557+ assertTrue (response .contains ("columnHeaders" ) || response .contains ("data" ));
558+ }
559+
499560 /**
500561 * Helper method to convert parameters map to query string
501562 */
@@ -512,4 +573,4 @@ private String toQueryString(Map<String, String> params) {
512573 }
513574 return sb .toString ();
514575 }
515- }
576+ }
0 commit comments