From c99b8f96638dca29926a3b88d4f42e8c8a6b960e Mon Sep 17 00:00:00 2001 From: Toby Moreno Date: Sun, 9 Aug 2026 10:02:34 -0700 Subject: [PATCH] fix: parameterize blind SQL injection level 1 --- .../BlindSQLInjectionVulnerability.java | 3 +- .../BlindSQLInjectionVulnerabilityTest.java | 61 +++++++++++++++++-- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/sasanlabs/service/vulnerability/sqlInjection/BlindSQLInjectionVulnerability.java b/src/main/java/org/sasanlabs/service/vulnerability/sqlInjection/BlindSQLInjectionVulnerability.java index c768a8593..b60295a89 100644 --- a/src/main/java/org/sasanlabs/service/vulnerability/sqlInjection/BlindSQLInjectionVulnerability.java +++ b/src/main/java/org/sasanlabs/service/vulnerability/sqlInjection/BlindSQLInjectionVulnerability.java @@ -90,7 +90,8 @@ public ResponseEntity getCarInformationLevel1( String id = queryParams.get(Constants.ID); BodyBuilder bodyBuilder = ResponseEntity.status(HttpStatus.OK); return applicationJdbcTemplate.query( - "select * from cars where id=" + id, + (connection) -> connection.prepareStatement("select * from cars where id=?"), + (preparedStatement) -> preparedStatement.setString(1, id), (rs) -> { if (rs.next()) { return bodyBuilder.body(CAR_IS_PRESENT_RESPONSE); diff --git a/src/test/java/org/sasanlabs/service/vulnerability/sqlInjection/BlindSQLInjectionVulnerabilityTest.java b/src/test/java/org/sasanlabs/service/vulnerability/sqlInjection/BlindSQLInjectionVulnerabilityTest.java index 5883c6af6..9cd0c155b 100644 --- a/src/test/java/org/sasanlabs/service/vulnerability/sqlInjection/BlindSQLInjectionVulnerabilityTest.java +++ b/src/test/java/org/sasanlabs/service/vulnerability/sqlInjection/BlindSQLInjectionVulnerabilityTest.java @@ -3,12 +3,15 @@ import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; +import java.sql.Connection; +import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -16,6 +19,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.PreparedStatementCreator; +import org.springframework.jdbc.core.PreparedStatementSetter; import org.springframework.jdbc.core.ResultSetExtractor; public class BlindSQLInjectionVulnerabilityTest { @@ -42,11 +46,14 @@ public void testGetCarInformationLevel1_CarPresent() throws SQLException { // return rse.extractData(mockResultSet); indicates that the ResultSetExtractor extracts the // data from the mockResultSet (which mocks the query result) - when(jdbcTemplate.query(anyString(), any(ResultSetExtractor.class))) + when(jdbcTemplate.query( + (PreparedStatementCreator) any(), + (PreparedStatementSetter) any(), + any(ResultSetExtractor.class))) .thenAnswer( invocation -> { ResultSetExtractor> rse = - invocation.getArgument(1); + invocation.getArgument(2); return rse.extractData(mockResultSet); }); @@ -72,11 +79,14 @@ public void testGetCarInformationLevel1_CarNotPresent() throws SQLException { // return rse.extractData(mockResultSet); indicates that the ResultSetExtractor extracts the // data from the mockResultSet (which mocks the query result) - when(jdbcTemplate.query(anyString(), any(ResultSetExtractor.class))) + when(jdbcTemplate.query( + (PreparedStatementCreator) any(), + (PreparedStatementSetter) any(), + any(ResultSetExtractor.class))) .thenAnswer( invocation -> { ResultSetExtractor> rse = - invocation.getArgument(1); + invocation.getArgument(2); return rse.extractData(mockResultSet); }); @@ -91,6 +101,49 @@ public void testGetCarInformationLevel1_CarNotPresent() throws SQLException { response.getBody()); } + @Test + public void testGetCarInformationLevel1_BindsBlindInjectionPayload() throws SQLException { + String payload = "1 OR 1=1"; + Map queryParams = new HashMap<>(); + queryParams.put("id", payload); + + ResultSet mockResultSet = mock(ResultSet.class); + when(mockResultSet.next()).thenReturn(false); + when(jdbcTemplate.query( + (PreparedStatementCreator) any(), + (PreparedStatementSetter) any(), + any(ResultSetExtractor.class))) + .thenAnswer( + invocation -> { + ResultSetExtractor> extractor = + invocation.getArgument(2); + return extractor.extractData(mockResultSet); + }); + + ResponseEntity response = + blindSQLInjectionVulnerability.getCarInformationLevel1(queryParams); + + ArgumentCaptor creator = + ArgumentCaptor.forClass(PreparedStatementCreator.class); + ArgumentCaptor setter = + ArgumentCaptor.forClass(PreparedStatementSetter.class); + verify(jdbcTemplate) + .query(creator.capture(), setter.capture(), any(ResultSetExtractor.class)); + + Connection connection = mock(Connection.class); + PreparedStatement preparedStatement = mock(PreparedStatement.class); + when(connection.prepareStatement("select * from cars where id=?")) + .thenReturn(preparedStatement); + + assertEquals(preparedStatement, creator.getValue().createPreparedStatement(connection)); + setter.getValue().setValues(preparedStatement); + verify(preparedStatement).setString(1, payload); + verify(jdbcTemplate, never()).query(contains(payload), any(ResultSetExtractor.class)); + assertEquals( + ErrorBasedSQLInjectionVulnerability.CAR_IS_NOT_PRESENT_RESPONSE, + response.getBody()); + } + @Test public void testGetCarInformationLevel2_CarPresent() throws SQLException { // Arrange