diff --git a/src/main/java/org/sasanlabs/service/vulnerability/sqlInjection/CarInformation.java b/src/main/java/org/sasanlabs/service/vulnerability/sqlInjection/CarInformation.java index 98de8e70..44636ed5 100644 --- a/src/main/java/org/sasanlabs/service/vulnerability/sqlInjection/CarInformation.java +++ b/src/main/java/org/sasanlabs/service/vulnerability/sqlInjection/CarInformation.java @@ -1,9 +1,21 @@ package org.sasanlabs.service.vulnerability.sqlInjection; +import javax.persistence.*; + /** @author preetkaran20@gmail.com KSASAN */ +@Access(AccessType.FIELD) +@Entity +@Table(name = "cars") +@NamedQuery(name = "findById", query = "select c from CarInformation c where c.id=:id") public class CarInformation { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; + private String name; + + @Column(name = "image") private String imagePath; public CarInformation() {} @@ -38,4 +50,8 @@ public String getImagePath() { public void setImagePath(String imagePath) { this.imagePath = imagePath; } + + public void setImage(String imagePath) { + this.imagePath = imagePath; + } } diff --git a/src/main/java/org/sasanlabs/service/vulnerability/sqlInjection/CarInformationRepository.java b/src/main/java/org/sasanlabs/service/vulnerability/sqlInjection/CarInformationRepository.java new file mode 100644 index 00000000..6f7c0a17 --- /dev/null +++ b/src/main/java/org/sasanlabs/service/vulnerability/sqlInjection/CarInformationRepository.java @@ -0,0 +1,9 @@ +package org.sasanlabs.service.vulnerability.sqlInjection; + +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CarInformationRepository extends JpaRepository { + + Optional findById(Integer id); +} 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 b755b2c5..4d45411b 100644 --- a/src/main/java/org/sasanlabs/service/vulnerability/sqlInjection/UnionBasedSQLInjectionVulnerability.java +++ b/src/main/java/org/sasanlabs/service/vulnerability/sqlInjection/UnionBasedSQLInjectionVulnerability.java @@ -3,6 +3,12 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.Map; +import java.util.Optional; +import javax.persistence.EntityManager; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; import org.sasanlabs.internal.utility.LevelConstants; import org.sasanlabs.internal.utility.Variant; import org.sasanlabs.internal.utility.annotations.AttackVector; @@ -12,7 +18,11 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import org.springframework.jdbc.core.namedparam.SqlParameterSource; import org.springframework.web.bind.annotation.RequestParam; /** @@ -28,10 +38,19 @@ public class UnionBasedSQLInjectionVulnerability { private final JdbcTemplate applicationJdbcTemplate; + private final NamedParameterJdbcTemplate namedParameterJdbcTemplate; + private final CarInformationRepository carInformationRepository; + private final EntityManager entityManager; public UnionBasedSQLInjectionVulnerability( - @Qualifier("applicationJdbcTemplate") final JdbcTemplate applicationJdbcTemplate) { + @Qualifier("applicationJdbcTemplate") final JdbcTemplate applicationJdbcTemplate, + NamedParameterJdbcTemplate namedParameterJdbcTemplate, + CarInformationRepository carInformationRepository, + EntityManager entityManager) { this.applicationJdbcTemplate = applicationJdbcTemplate; + this.namedParameterJdbcTemplate = namedParameterJdbcTemplate; + this.carInformationRepository = carInformationRepository; + this.entityManager = entityManager; } @AttackVector( @@ -92,6 +111,83 @@ public ResponseEntity getCarInformationLevel4( this::resultSetToResponse); } + @VulnerableAppRequestMapping( + value = LevelConstants.LEVEL_5, + variant = Variant.SECURE, + htmlTemplate = "LEVEL_1/SQLInjection_Level1") + public ResponseEntity getCarInformationLevel5( + @RequestParam final Map queryParams) { + final String id = queryParams.get("id"); + SqlParameterSource namedParameters = new MapSqlParameterSource().addValue("id", id); + CarInformation s = + namedParameterJdbcTemplate.queryForObject( + "select * from cars where id=:id", + namedParameters, + new BeanPropertyRowMapper<>(CarInformation.class)); + return new ResponseEntity<>(s, HttpStatus.OK); + } + + @VulnerableAppRequestMapping( + value = LevelConstants.LEVEL_6, + variant = Variant.SECURE, + htmlTemplate = "LEVEL_1/SQLInjection_Level1") + public ResponseEntity getCarInformationLevel6( + @RequestParam final Map queryParams) { + final String id = queryParams.get("id"); + String jql = "from CarInformation where id = :id"; + TypedQuery q = + entityManager + .createQuery(jql, CarInformation.class) + .setParameter("id", Integer.valueOf(id)); + return new ResponseEntity<>(q.getSingleResult(), HttpStatus.OK); + } + + @VulnerableAppRequestMapping( + value = LevelConstants.LEVEL_7, + variant = Variant.SECURE, + htmlTemplate = "LEVEL_1/SQLInjection_Level1") + public ResponseEntity getCarInformationLevel7( + @RequestParam final Map queryParams) { + final String id = queryParams.get("id"); + + CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(CarInformation.class); + Root root = cq.from(CarInformation.class); + + cq.select(root).where(cb.equal(root.get("id"), id)); + + TypedQuery q = entityManager.createQuery(cq); + return new ResponseEntity<>(q.getSingleResult(), HttpStatus.OK); + } + + @VulnerableAppRequestMapping( + value = LevelConstants.LEVEL_8, + variant = Variant.SECURE, + htmlTemplate = "LEVEL_1/SQLInjection_Level1") + public ResponseEntity getCarInformationLevel8( + @RequestParam final Map queryParams) { + final String id = queryParams.get("id"); + TypedQuery q = + entityManager + .createNamedQuery("findById", CarInformation.class) + .setParameter("id", Integer.valueOf(id)); + return new ResponseEntity<>(q.getSingleResult(), HttpStatus.OK); + } + + @VulnerableAppRequestMapping( + value = LevelConstants.LEVEL_9, + variant = Variant.SECURE, + htmlTemplate = "LEVEL_1/SQLInjection_Level1") + public ResponseEntity getCarInformationLevel9( + @RequestParam final Map queryParams) { + final String id = queryParams.get("id"); + Optional carInformation = + carInformationRepository.findById(Integer.valueOf(id)); + return carInformation + .map(information -> new ResponseEntity<>(information, HttpStatus.OK)) + .orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND)); + } + private ResponseEntity resultSetToResponse(final ResultSet rs) throws SQLException { final CarInformation carInformation = new CarInformation(); diff --git a/src/test/java/org/sasanlabs/service/vulnerability/sqlInjection/UnionBasedSQLInjectionVulnerabilityTest.java b/src/test/java/org/sasanlabs/service/vulnerability/sqlInjection/UnionBasedSQLInjectionVulnerabilityTest.java index 5d73d7a9..46ab7263 100644 --- a/src/test/java/org/sasanlabs/service/vulnerability/sqlInjection/UnionBasedSQLInjectionVulnerabilityTest.java +++ b/src/test/java/org/sasanlabs/service/vulnerability/sqlInjection/UnionBasedSQLInjectionVulnerabilityTest.java @@ -1,29 +1,39 @@ 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.ArgumentMatchers.*; 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 java.util.Objects; +import javax.persistence.EntityManager; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentMatcher; import org.mockito.Mockito; +import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.PreparedStatementSetter; import org.springframework.jdbc.core.ResultSetExtractor; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; class UnionBasedSQLInjectionVulnerabilityTest { private UnionBasedSQLInjectionVulnerability unionBasedSQLInjectionVulnerability; private JdbcTemplate template; + private NamedParameterJdbcTemplate namedParameterJdbcTemplate; + private CarInformationRepository carInformationRepository; + private EntityManager entityManager; @BeforeEach - void setUp() throws IOException { + void setUp() { template = Mockito.mock(JdbcTemplate.class); + namedParameterJdbcTemplate = Mockito.mock(NamedParameterJdbcTemplate.class); + carInformationRepository = Mockito.mock(CarInformationRepository.class); + entityManager = Mockito.mock(EntityManager.class); // mock database doReturn(null) @@ -36,11 +46,16 @@ void setUp() throws IOException { (PreparedStatementSetter) any(), (ResultSetExtractor) any()); - unionBasedSQLInjectionVulnerability = new UnionBasedSQLInjectionVulnerability(template); + unionBasedSQLInjectionVulnerability = + new UnionBasedSQLInjectionVulnerability( + template, + namedParameterJdbcTemplate, + carInformationRepository, + entityManager); } @Test - void getCarInformationLevel1_ExpectParamInjected() throws IOException { + void getCarInformationLevel1_ExpectParamInjected() { // Act final Map params = Collections.singletonMap("id", "1 UNION SELECT * FROM cars;"); @@ -54,7 +69,7 @@ void getCarInformationLevel1_ExpectParamInjected() throws IOException { } @Test - void getCarInformationLevel2_ExpectParamInjected() throws IOException { + void getCarInformationLevel2_ExpectParamInjected() { // Act final Map params = Collections.singletonMap("id", "1' UNION SELECT * FROM cars; --"); @@ -68,7 +83,7 @@ void getCarInformationLevel2_ExpectParamInjected() throws IOException { } @Test - void getCarInformationLevel3_ExpectParamEscaped() throws IOException { + void getCarInformationLevel3_ExpectParamEscaped() { // Act final Map params = Collections.singletonMap("id", "1' UNION SELECT * FROM cars; --"); @@ -82,7 +97,7 @@ void getCarInformationLevel3_ExpectParamEscaped() throws IOException { } @Test - void getCarInformationLevel4_ExpecParamEscaped() throws IOException { + void getCarInformationLevel4_ExpectParamEscaped() { // Act final Map params = Collections.singletonMap("id", "1' UNION SELECT * FROM cars; --"); @@ -95,4 +110,27 @@ void getCarInformationLevel4_ExpecParamEscaped() throws IOException { (PreparedStatementSetter) any(), (ResultSetExtractor) any()); } + + @Test + void getCarInformationLevel5_ExpectParamEscaped() { + // Act + final Map params = + Collections.singletonMap("id", "1' UNION SELECT * FROM cars; --"); + final String id = "1' UNION SELECT * FROM cars; --"; + unionBasedSQLInjectionVulnerability.getCarInformationLevel5(params); + // Assert + ArgumentMatcher argumentMatcher = + sqlParameterSource -> + Objects.requireNonNull(sqlParameterSource.getValue("id").equals(id)); + verify(namedParameterJdbcTemplate) + .queryForObject( + eq("select * from cars where id=:id"), + argThat(argumentMatcher), + (RowMapper) + argThat( + val -> + ((BeanPropertyRowMapper) val) + .getMappedClass() + .equals(CarInformation.class))); + } }