package servlets.api; import static org.junit.jupiter.api.Assertions.fail; import dbProcs.Setter; import jakarta.servlet.ServletException; import java.io.IOException; import java.sql.SQLException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.json.JSONArray; import org.json.JSONObject; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockServletConfig; import testUtils.TestProperties; import utils.ModulePlan; public class LevelsIT { private static String lang = "en_GB"; private static final Logger log = LogManager.getLogger(LevelsIT.class); private static String applicationRoot = new String(); private MockHttpServletRequest request; private MockHttpServletResponse response; /** Creates DB or Restores DB to Factory Defaults before running tests */ @BeforeAll public static void resetDatabase() throws IOException, SQLException { TestProperties.setTestPropertiesFileDirectory(log); TestProperties.createMysqlResource(); TestProperties.ensureSchemaReady(log); TestProperties.reseedTestData(); } @BeforeEach public void setup() { log.debug("Setting Up Blank Request and Response"); request = new MockHttpServletRequest(); response = new MockHttpServletResponse(); // Open All modules if (!Setter.openAllModules(applicationRoot, false)) { fail("Could not Mark All Modules As Open"); } // Default to CTF Mode ModulePlan.setIncrementalFloor(); if (!ModulePlan.isIncrementalFloor()) { String message = "Could not enable CTF Mode"; log.fatal(message); fail(message); } } /** * Method to Simulate the interaction with the getModule servlet. * * @param moduleId The ID of the Module to Search For * @param csrfToken The CSRF Token of the User * @return The Content of the Response (Which is supposed to be the location of the module) * @throws ServletException * @throws IOException * @throws Exception */ public String getModuleDoGet(String csrfToken) throws ServletException, IOException { int expectedResponseCode = 302; String servletName = "Levels"; log.debug("Creating " + servletName + " Servlet Instance"); Levels servlet = new Levels(); servlet.init(new MockServletConfig(servletName)); // Adding Correct CSRF Token (Token Submitted) request.addParameter("csrfToken", csrfToken); log.debug("Running doGet"); servlet.doGet(request, response); if (response.getStatus() != expectedResponseCode) { fail( "GetModule Servlet Returned " + response.getStatus() + " Code. " + expectedResponseCode + " Expected"); } else { log.debug("302 OK Detected"); log.debug( "Servlet Successful, returning location retrieved: " + response.getContentAsString()); return (response.getContentAsString()); } return null; } @Test public void testCtfModeNoLevelsComplete() throws SQLException, ServletException, IOException { String moduleId = new String("0dbea4cb5811fff0527184f99bd5034ca9286f11"); // Insecure Direct Object References // Module Id (First Challenge) String userName = "ctfModeNoLevels"; // Verify User Exists in DB TestProperties.verifyTestUser(log, applicationRoot, userName, userName); // Sign in as Normal User log.debug("Signing in as " + userName + " Through LoginServlet"); TestProperties.loginDoPost(log, request, response, userName, userName, null, lang); log.debug("Login Servlet Complete, Getting CSRF Token"); if (response.getCookie("token") == null) { fail("No CSRF Token Was Returned from Login Servlet"); } String csrfToken = response.getCookie("token").getValue(); if (csrfToken.isEmpty()) { String message = new String("No CSRF token returned from Login Servlet"); log.fatal(message); fail(message); } else { // Add Cookies from Response to outgoing request request.setCookies(response.getCookies()); String jsonString = getModuleDoGet(csrfToken); JSONArray theResponse = new JSONArray(jsonString); log.debug("Response Returned, checking mode"); if (!theResponse.getJSONObject(0).get("levelMode").toString().equalsIgnoreCase("ctf")) { String message = "CTF Mode not Detected in JSON Response"; log.debug(message); fail(message); } else { log.debug("Extracting Modules from JSON response"); // Is First Object Marked as Open, and is it the Module We Expect? JSONArray theModules = theResponse.getJSONObject(1).getJSONArray("modules"); JSONObject module = theModules.getJSONObject(0); log.debug("Inspecting First Level: " + module); if (!module.get("moduleId").toString().equalsIgnoreCase(moduleId)) { String message = "Received " + module.get("moduleId") + " as first module instead of " + moduleId; log.debug(message); fail(message); } else if (!module.getBoolean("moduleOpen")) { String message = "Module " + module.get("moduleId") + " returned false for moduleOpen Status when it should be true"; log.debug(message); fail(message); } else if (module.getBoolean("moduleCompleted")) { String message = "Module " + module.get("moduleId") + " is marked as completed when no module should be marked as complete for this" + " user"; log.debug(message); fail(message); } else { // First Level is Ok module = theModules.getJSONObject(1); log.debug("Inspecting Second Level: " + module); if (module.getBoolean("moduleOpen")) { String message = "Module " + module.get("moduleId") + " returned true for moduleOpen Status when it should be false"; log.debug(message); fail(message); } else if (module.getBoolean("moduleCompleted")) { String message = "Module " + module.get("moduleId") + " is marked as completed when no module should be marked as complete for this" + " user"; log.debug(message); fail(message); } else { log.debug("Test Complete"); } } } } } @Test public void testCtfModeLevelComplete() throws SQLException, ServletException, IOException { String moduleId = new String("0dbea4cb5811fff0527184f99bd5034ca9286f11"); // Insecure Direct Object References // Module Id (First Challenge) String userName = "ctfModeWithLevel"; // Verify User Exists in DB TestProperties.verifyTestUser(log, applicationRoot, userName, userName); TestProperties.completeModuleForUser(log, userName, userName, moduleId, "anything", ""); // Sign in as Normal User log.debug("Signing in as " + userName + " Through LoginServlet"); TestProperties.loginDoPost(log, request, response, userName, userName, null, lang); log.debug("Login Servlet Complete, Getting CSRF Token"); if (response.getCookie("token") == null) { fail("No CSRF Token Was Returned from Login Servlet"); } String csrfToken = response.getCookie("token").getValue(); if (csrfToken.isEmpty()) { String message = new String("No CSRF token returned from Login Servlet"); log.fatal(message); fail(message); } else { // Add Cookies from Response to outgoing request request.setCookies(response.getCookies()); String jsonString = getModuleDoGet(csrfToken); JSONArray theResponse = new JSONArray(jsonString); log.debug("Response Returned, checking mode"); if (!theResponse.getJSONObject(0).get("levelMode").toString().equalsIgnoreCase("ctf")) { String message = "CTF Mode not Detected in JSON Response"; log.debug(message); fail(message); } else { log.debug("Extracting Modules from JSON response"); // Is First Object Marked as Open, and is it the Module We Expect? JSONArray theModules = theResponse.getJSONObject(1).getJSONArray("modules"); JSONObject module = theModules.getJSONObject(0); log.debug("Inspecting First Level: " + module); if (!module.get("moduleId").toString().equalsIgnoreCase(moduleId)) { String message = "Received " + module.get("moduleId") + " as first module instead of " + moduleId; log.debug(message); fail(message); } else if (!module.getBoolean("moduleOpen")) { String message = "Module " + module.get("moduleId") + " returned false for moduleOpen Status when it should be true"; log.debug(message); fail(message); } else if (!module.getBoolean("moduleCompleted")) { String message = "Module " + module.get("moduleId") + " is marked as uncompleted when this module should be marked as complete for" + " this user"; log.debug(message); fail(message); } else if (!(module.getInt("scoredPoints") == 15)) // Max points for first to complete first // challenge { String message = "Expected 15 Points to be earned from completing level. Got " + module.getInt("scoredPoints"); log.debug(message); fail(message); } else if (!module.getString("medalEarned").equalsIgnoreCase("gold")) { String message = "Expected gold medal to be earned from completing level. Got " + module.getInt("medalEarned"); log.debug(message); fail(message); } else { // First Level is Ok module = theModules.getJSONObject(1); log.debug("Inspecting Second Level: " + module); if (!module.getBoolean("moduleOpen")) { String message = "Module " + module.get("moduleId") + " returned false for moduleOpen Status when it should be true"; log.debug(message); fail(message); } else if (module.getBoolean("moduleCompleted")) { String message = "Module " + module.get("moduleId") + " is marked as completed when it should not be marked as complete for this" + " user"; log.debug(message); fail(message); } else { log.debug("Test Complete"); } } } } } }