From 571108b6407eacb8c4fe24408097747d818f83f2 Mon Sep 17 00:00:00 2001 From: Paul Weber Date: Fri, 15 Sep 2023 20:25:16 +0200 Subject: [PATCH] Tests for union based sql injection (#444) * Add first version of unit tests for union based sql injection vulnerability * Added tests for the UnionBasedSQLInjction Co-authored-by: Paul Weber --- .../UnionBasedSQLInjectionVulnerability.java | 80 ++++++--------- ...ionBasedSQLInjectionVulnerabilityTest.java | 98 +++++++++++++++++++ 2 files changed, 126 insertions(+), 52 deletions(-) create mode 100644 src/test/java/org/sasanlabs/service/vulnerability/sqlInjection/UnionBasedSQLInjectionVulnerabilityTest.java diff --git a/src/main/java/org/sasanlabs/service/vulnerability/sqlInjection/UnionBasedSQLInjectionVulnerability.java b/src/main/java/org/sasanlabs/service/vulnerability/sqlInjection/UnionBasedSQLInjectionVulnerability.java index 749258ba..b755b2c5 100644 --- a/src/main/java/org/sasanlabs/service/vulnerability/sqlInjection/UnionBasedSQLInjectionVulnerability.java +++ b/src/main/java/org/sasanlabs/service/vulnerability/sqlInjection/UnionBasedSQLInjectionVulnerability.java @@ -1,5 +1,7 @@ package org.sasanlabs.service.vulnerability.sqlInjection; +import java.sql.ResultSet; +import java.sql.SQLException; import java.util.Map; import org.sasanlabs.internal.utility.LevelConstants; import org.sasanlabs.internal.utility.Variant; @@ -25,10 +27,10 @@ value = "UnionBasedSQLInjectionVulnerability") public class UnionBasedSQLInjectionVulnerability { - private JdbcTemplate applicationJdbcTemplate; + private final JdbcTemplate applicationJdbcTemplate; public UnionBasedSQLInjectionVulnerability( - @Qualifier("applicationJdbcTemplate") JdbcTemplate applicationJdbcTemplate) { + @Qualifier("applicationJdbcTemplate") final JdbcTemplate applicationJdbcTemplate) { this.applicationJdbcTemplate = applicationJdbcTemplate; } @@ -40,19 +42,10 @@ public UnionBasedSQLInjectionVulnerability( value = LevelConstants.LEVEL_1, htmlTemplate = "LEVEL_1/SQLInjection_Level1") public ResponseEntity getCarInformationLevel1( - @RequestParam Map queryParams) { - String id = queryParams.get("id"); + @RequestParam final Map queryParams) { + final String id = queryParams.get("id"); return applicationJdbcTemplate.query( - "select * from cars where id=" + id, - (rs) -> { - CarInformation carInformation = new CarInformation(); - if (rs.next()) { - carInformation.setId(rs.getInt(1)); - carInformation.setName(rs.getString(2)); - carInformation.setImagePath(rs.getString(3)); - } - return new ResponseEntity(carInformation, HttpStatus.OK); - }); + "select * from cars where id=" + id, this::resultSetToResponse); } @AttackVector( @@ -64,19 +57,10 @@ public ResponseEntity getCarInformationLevel1( value = LevelConstants.LEVEL_2, htmlTemplate = "LEVEL_1/SQLInjection_Level1") public ResponseEntity getCarInformationLevel2( - @RequestParam Map queryParams) { - String id = queryParams.get("id"); - CarInformation carInformation = new CarInformation(); + @RequestParam final Map queryParams) { + final String id = queryParams.get("id"); return applicationJdbcTemplate.query( - "select * from cars where id='" + id + "'", - (rs) -> { - if (rs.next()) { - carInformation.setId(rs.getInt(1)); - carInformation.setName(rs.getString(2)); - carInformation.setImagePath(rs.getString(3)); - } - return new ResponseEntity(carInformation, HttpStatus.OK); - }); + "select * from cars where id='" + id + "'", this::resultSetToResponse); } @AttackVector( @@ -88,19 +72,10 @@ public ResponseEntity getCarInformationLevel2( variant = Variant.SECURE, htmlTemplate = "LEVEL_1/SQLInjection_Level1") public ResponseEntity getCarInformationLevel3( - @RequestParam Map queryParams) { - String id = queryParams.get("id").replaceAll("'", ""); + @RequestParam final Map queryParams) { + final String id = queryParams.get("id").replaceAll("'", ""); return applicationJdbcTemplate.query( - "select * from cars where id='" + id + "'", - (rs) -> { - CarInformation carInformation = new CarInformation(); - if (rs.next()) { - carInformation.setId(rs.getInt(1)); - carInformation.setName(rs.getString(2)); - carInformation.setImagePath(rs.getString(3)); - } - return new ResponseEntity(carInformation, HttpStatus.OK); - }); + "select * from cars where id='" + id + "'", this::resultSetToResponse); } @VulnerableAppRequestMapping( @@ -108,22 +83,23 @@ public ResponseEntity getCarInformationLevel3( variant = Variant.SECURE, htmlTemplate = "LEVEL_1/SQLInjection_Level1") public ResponseEntity getCarInformationLevel4( - @RequestParam Map queryParams) { - String id = queryParams.get("id"); + @RequestParam final Map queryParams) { + final String id = queryParams.get("id"); return applicationJdbcTemplate.query( "select * from cars where id=?", - (prepareStatement) -> { - prepareStatement.setString(1, id); - }, - (rs) -> { - CarInformation carInformation = new CarInformation(); - if (rs.next()) { - carInformation.setId(rs.getInt(1)); - carInformation.setName(rs.getString(2)); - carInformation.setImagePath(rs.getString(3)); - } - return new ResponseEntity(carInformation, HttpStatus.OK); - }); + prepareStatement -> prepareStatement.setString(1, id), + this::resultSetToResponse); + } + + private ResponseEntity resultSetToResponse(final ResultSet rs) + throws SQLException { + final CarInformation carInformation = new CarInformation(); + if (rs.next()) { + carInformation.setId(rs.getInt(1)); + carInformation.setName(rs.getString(2)); + carInformation.setImagePath(rs.getString(3)); + } + return new ResponseEntity<>(carInformation, HttpStatus.OK); } } diff --git a/src/test/java/org/sasanlabs/service/vulnerability/sqlInjection/UnionBasedSQLInjectionVulnerabilityTest.java b/src/test/java/org/sasanlabs/service/vulnerability/sqlInjection/UnionBasedSQLInjectionVulnerabilityTest.java new file mode 100644 index 00000000..5d73d7a9 --- /dev/null +++ b/src/test/java/org/sasanlabs/service/vulnerability/sqlInjection/UnionBasedSQLInjectionVulnerabilityTest.java @@ -0,0 +1,98 @@ +package org.sasanlabs.service.vulnerability.sqlInjection; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verify; + +import java.io.IOException; +import java.util.Collections; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.PreparedStatementSetter; +import org.springframework.jdbc.core.ResultSetExtractor; + +class UnionBasedSQLInjectionVulnerabilityTest { + + private UnionBasedSQLInjectionVulnerability unionBasedSQLInjectionVulnerability; + private JdbcTemplate template; + + @BeforeEach + void setUp() throws IOException { + template = Mockito.mock(JdbcTemplate.class); + + // mock database + doReturn(null) + .when(template) + .query(anyString(), (ResultSetExtractor) any()); + doReturn(null) + .when(template) + .query( + anyString(), + (PreparedStatementSetter) any(), + (ResultSetExtractor) any()); + + unionBasedSQLInjectionVulnerability = new UnionBasedSQLInjectionVulnerability(template); + } + + @Test + void getCarInformationLevel1_ExpectParamInjected() throws IOException { + // Act + final Map params = + Collections.singletonMap("id", "1 UNION SELECT * FROM cars;"); + unionBasedSQLInjectionVulnerability.getCarInformationLevel1(params); + + // Assert + verify(template) + .query( + eq("select * from cars where id=1 UNION SELECT * FROM cars;"), + (ResultSetExtractor) any()); + } + + @Test + void getCarInformationLevel2_ExpectParamInjected() throws IOException { + // Act + final Map params = + Collections.singletonMap("id", "1' UNION SELECT * FROM cars; --"); + unionBasedSQLInjectionVulnerability.getCarInformationLevel2(params); + + // Assert + verify(template) + .query( + eq("select * from cars where id='1' UNION SELECT * FROM cars; --'"), + (ResultSetExtractor) any()); + } + + @Test + void getCarInformationLevel3_ExpectParamEscaped() throws IOException { + // Act + final Map params = + Collections.singletonMap("id", "1' UNION SELECT * FROM cars; --"); + unionBasedSQLInjectionVulnerability.getCarInformationLevel3(params); + + // Assert + verify(template) + .query( + eq("select * from cars where id='1 UNION SELECT * FROM cars; --'"), + (ResultSetExtractor) any()); + } + + @Test + void getCarInformationLevel4_ExpecParamEscaped() throws IOException { + // Act + final Map params = + Collections.singletonMap("id", "1' UNION SELECT * FROM cars; --"); + unionBasedSQLInjectionVulnerability.getCarInformationLevel4(params); + + // Assert + verify(template) + .query( + eq("select * from cars where id=?"), + (PreparedStatementSetter) any(), + (ResultSetExtractor) any()); + } +}