diff --git a/src/main/java/org/sasanlabs/service/vulnerability/sampleVulnerability/SampleVulnerability.java b/src/main/java/org/sasanlabs/service/vulnerability/sampleVulnerability/SampleVulnerability.java new file mode 100644 index 00000000..3f15c150 --- /dev/null +++ b/src/main/java/org/sasanlabs/service/vulnerability/sampleVulnerability/SampleVulnerability.java @@ -0,0 +1,108 @@ +package org.sasanlabs.service.vulnerability.sampleVulnerability; + +import org.sasanlabs.internal.utility.LevelConstants; +import org.sasanlabs.internal.utility.Variant; +import org.sasanlabs.internal.utility.annotations.AttackVector; +import org.sasanlabs.internal.utility.annotations.VulnerableAppRequestMapping; +import org.sasanlabs.internal.utility.annotations.VulnerableAppRestController; +import org.sasanlabs.service.vulnerability.bean.GenericVulnerabilityResponseBean; +import org.sasanlabs.vulnerability.types.VulnerabilityType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * This is a sample vulnerability for helping developers in adding a new Vulnerability for + * VulnerableApp + * + * @author KSASAN preetkaran20@gmail.com + */ +/** + * {@code VulnerableAppRestController} annotation is similar to {@link + * org.springframework.stereotype.Controller} Annotation + */ +@VulnerableAppRestController( + /** + * "descriptionLabel" parameter of annotation is i18n label stored in {@link + * /VulnerableApp/src/main/resources/i18n/}. This descriptionLabel + * will be shown in the UI as the description of the Vulnerability. It helps students to + * learn about the vulnerability and can also include some of the useful references etc. + */ + descriptionLabel = "SAMPLE_VULNERABILITY", + /** + * "value" parameter of annotation is used to create the request mapping. e.g. for the below + * parameter value, /VulnerableApp/SampleVulnerability will be created as URI Path. + */ + value = "SampleVulnerability") +public class SampleVulnerability { + + /** + * {@code AttackVector} annotation is used to create the Hints section in the User Interface. + * This annotation can be mentioned multiple times in case the same vulnerability level + */ + @AttackVector( + /** + * "vulnerabilityExposed" parameter is used to depict the Vulnerability exposed by the + * level. For example say a level is exposing SQL_INJECTION. + */ + vulnerabilityExposed = VulnerabilityType.SAMPLE_VULNERABILITY, + /** + * "description" parameter of annotation is i18n label stored in {@link + * /VulnerableApp/src/main/resources/i18n/}. This description + * will be shown in the UI as hint to give some indication on how the level is handling + * input to help user to crack the level. + */ + description = "SAMPLE_VULNERABILITY_USER_INPUT_HANDLING_INJECTION", + + /** + * "payload" parameter of annotation is i18n label stored in {@link + * /VulnerableApp/src/main/resources/attackvectors/*.properties}. This payload will be + * shown in UI to help users find/exploit the vulnerability + */ + payload = "NOT_APPLICABLE") + /** + * This annotation is similar to {@link RequestMapping} SpringBoot annotation. It will map the + * endpoint to /VulnerableApp/SampleVulnerability/LEVEL_1 where LEVEL_1 is coming from the value + * parameter. + */ + @VulnerableAppRequestMapping( + /** + * "value" parameter is used to map the level to URI path + * /VulnerableApp/SampleVulnerability/${value}. + */ + value = LevelConstants.LEVEL_1, + + /** + * "htmlTemplate" is used to load the UI for the level for taking input from the user. + * It points to files in directory + * src/main/resource/static/templates/${VulnerabilityName} e.g. + * src/main/resource/static/templates/SampleVulnerability as ${htmlTemplate}.js, + * ${htmlTemplate}.css, ${htmlTemplate}.html. e.g. in this case it will be: + * src/main/resource/static/templates/SampleVulnerability/LEVEL_1/SampleVulnerability_Level1.js + * etc + * + *

CSS, JS and HTML are all loaded to render the UI. + */ + htmlTemplate = "LEVEL_1/SampleVulnerability") + public GenericVulnerabilityResponseBean sampleUnsecuredLevel(@RequestParam("name") String key) { + /** Add Business logic here */ + return new GenericVulnerabilityResponseBean<>("Not Implemented", true); + } + + /** For secured level there is no need for {@link AttackVector} annotation. */ + @VulnerableAppRequestMapping( + value = LevelConstants.LEVEL_2, + + // Can reuse the same UI template in case it doesn't change between levels + htmlTemplate = "LEVEL_1/SampleVulnerability", + /** + * "variant" parameter defines whether the level is secure or not and same is depicted + * in the UI as a closed lock and open lock icon. Default value of the variant is + * UNSECURE so in case a secure level is added, please add the variant as {@link + * Variant#SECURE} + */ + variant = Variant.SECURE) + public GenericVulnerabilityResponseBean sampleSecuredLevel(@RequestParam("name") String key) { + /** Add Business logic here */ + return new GenericVulnerabilityResponseBean<>("Not Implemented", true); + } +} \ No newline at end of file diff --git a/src/main/resources/static/templates/SampleVulnerability/LEVEL_1/SampleVulnerability.css b/src/main/resources/static/templates/SampleVulnerability/LEVEL_1/SampleVulnerability.css new file mode 100644 index 00000000..e5d57b25 --- /dev/null +++ b/src/main/resources/static/templates/SampleVulnerability/LEVEL_1/SampleVulnerability.css @@ -0,0 +1,16 @@ +#SampleVulnerability { + color: black; + text-align: center; +} + +#fetchDetails { + background: blueviolet; + display: inline-block; + padding: 8px 8px; + margin: 10px; + border: 2px solid transparent; + border-radius: 3px; + transition: 0.2s opacity; + color: #FFF; + font-size: 12px; +} \ No newline at end of file diff --git a/src/main/resources/static/templates/SampleVulnerability/LEVEL_1/SampleVulnerability.html b/src/main/resources/static/templates/SampleVulnerability/LEVEL_1/SampleVulnerability.html new file mode 100644 index 00000000..dddeea7d --- /dev/null +++ b/src/main/resources/static/templates/SampleVulnerability/LEVEL_1/SampleVulnerability.html @@ -0,0 +1,9 @@ +

+
+
+ This is a Sample Vulnerability. please add the UI components here. +
+ +
+
+
\ No newline at end of file diff --git a/src/main/resources/static/templates/SampleVulnerability/LEVEL_1/SampleVulnerability.js b/src/main/resources/static/templates/SampleVulnerability/LEVEL_1/SampleVulnerability.js new file mode 100644 index 00000000..78641721 --- /dev/null +++ b/src/main/resources/static/templates/SampleVulnerability/LEVEL_1/SampleVulnerability.js @@ -0,0 +1,23 @@ +function addingEventListenerToFetchData() { + document + .getElementById("fetchDetails") + .addEventListener("click", function () { + /** + * getUrlForVulnerabilityLevel() method provides url to call the Vulnerability Level + * of Sample Vulnerability. + * e.g. /VulnerableApp/SampleVulnerability/LEVEL_1 for LEVEL_1 + */ + let url = getUrlForVulnerabilityLevel(); + /** + * doGetAjaxCall() method is used to do the ajax get call to the Vulnerability Level + */ + doGetAjaxCall(fetchDataCallback, url + "?name=dummyInput", true); + }); +} +// Used to register event on the button or any other component +addingEventListenerToFetchData(); + +//Callback function to handle the response and render in the UI +function fetchDataCallback(data) { + document.getElementById("response").innerHTML = data.content; +} diff --git a/src/test/java/org/sasanlabs/service/vulnerability/sqlInjection/ErrorBasedSQLInjectionVulnerabilityTest.java b/src/test/java/org/sasanlabs/service/vulnerability/sqlInjection/ErrorBasedSQLInjectionVulnerabilityTest.java new file mode 100644 index 00000000..74079531 --- /dev/null +++ b/src/test/java/org/sasanlabs/service/vulnerability/sqlInjection/ErrorBasedSQLInjectionVulnerabilityTest.java @@ -0,0 +1,143 @@ +package org.sasanlabs.service.vulnerability.sqlInjection; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.sasanlabs.vulnerability.utils.Constants; +import org.springframework.http.HttpStatus; +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; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.verify; + +class ErrorBasedSQLInjectionVulnerabilityTest { + + private ErrorBasedSQLInjectionVulnerability errorBasedSQLInjectionVulnerability; + private JdbcTemplate template; + + @BeforeEach + void setUp() { + 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()); + + errorBasedSQLInjectionVulnerability = new ErrorBasedSQLInjectionVulnerability(template); + } + + @Test + void doesCarInformationExistsLevel1_ExpectParamEscaped() { + // Act + final Map queryParams = Collections.singletonMap("id", "1"); + errorBasedSQLInjectionVulnerability.doesCarInformationExistsLevel1(queryParams); + + // Assert + verify(template) + .query( + eq("select * from cars where id=1"), + (ResultSetExtractor) any()); + } + + @Test + void doesCarInformationExistsLevel2_ExpectParamEscaped() { + // Act + final Map queryParams = Collections.singletonMap("id", "1"); + errorBasedSQLInjectionVulnerability.doesCarInformationExistsLevel2(queryParams); + + // Assert + verify(template) + .query( + eq("select * from cars where id='1'"), + (ResultSetExtractor) any()); + } + + @Test + void doesCarInformationExistsLevel3_ExpectParamEscaped() { + // Act + final Map queryParams = Collections.singletonMap("id", "1'"); + errorBasedSQLInjectionVulnerability.doesCarInformationExistsLevel3(queryParams); + + // Assert + verify(template) + .query( + eq("select * from cars where id='1'"), + (ResultSetExtractor) any()); + } + + @Test + void doesCarInformationExistsLevel4_ExpectValidResponse() { + // Arrange + Map queryParams = new HashMap<>(); + queryParams.put(Constants.ID, "1'"); + + // Mock the response entity + ResponseEntity mockResponseEntity = ResponseEntity.status(HttpStatus.OK).body("Sample response"); + doReturn(mockResponseEntity) + .when(template) + .query( + Mockito.any(PreparedStatementCreator.class), + Mockito.any(PreparedStatementSetter.class), + Mockito.any(ResultSetExtractor.class)); + + // Act + ResponseEntity response = errorBasedSQLInjectionVulnerability.doesCarInformationExistsLevel4(queryParams); + + // Assert + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals("Sample response", response.getBody()); + verify(template) + .query( + Mockito.any(PreparedStatementCreator.class), + Mockito.any(PreparedStatementSetter.class), + Mockito.any(ResultSetExtractor.class)); + } + + @Test + void doesCarInformationExistsLevel5_ExpectValidResponse() { + // Arrange + Map queryParams = new HashMap<>(); + queryParams.put(Constants.ID, "1"); + + // Mock the response entity + ResponseEntity mockResponseEntity = ResponseEntity.status(HttpStatus.OK).body("Sample response"); + doReturn(mockResponseEntity) + .when(template) + .query( + Mockito.any(PreparedStatementCreator.class), + Mockito.any(PreparedStatementSetter.class), + Mockito.any(ResultSetExtractor.class)); + + // Act + ResponseEntity response = errorBasedSQLInjectionVulnerability.doesCarInformationExistsLevel5(queryParams); + + // Assert + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals("Sample response", response.getBody()); + verify(template) + .query( + Mockito.any(PreparedStatementCreator.class), + Mockito.any(PreparedStatementSetter.class), + Mockito.any(ResultSetExtractor.class)); + } + +} \ No newline at end of file