From 7a5e30c1579ebaba8cf426c2192c187401248af0 Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Sun, 7 Aug 2022 10:40:51 +0200 Subject: [PATCH] Update POM file with new version: ctfd-safe-4 --- Dockerfile.web | 8 +- ctf-instructions.md | 37 ++++++ .../wrongsecrets/RuntimeEnvironment.java | 19 +++- .../canaries/CanariesController.java | 5 - .../challenges/ChallengesAPIController.java | 10 +- .../challenges/cloud/Challenge10.java | 2 +- .../challenges/cloud/Challenge11.java | 41 ++++--- .../challenges/cloud/Challenge9.java | 2 +- src/main/resources/application.properties | 18 +++ src/main/resources/templates/challenge.html | 2 +- src/main/resources/templates/welcome.html | 30 +++-- .../ChallengesControllerCTFModeTest.java | 19 +++- ...ollerCTFModeWithPresetCloudValuesTest.java | 105 ++++++++++++++++++ 13 files changed, 250 insertions(+), 48 deletions(-) create mode 100644 ctf-instructions.md create mode 100644 src/test/java/org/owasp/wrongsecrets/ChallengesControllerCTFModeWithPresetCloudValuesTest.java diff --git a/Dockerfile.web b/Dockerfile.web index 1fdd7dbed..3c95c0c8e 100644 --- a/Dockerfile.web +++ b/Dockerfile.web @@ -1,4 +1,4 @@ -FROM jeroenwillemsen/wrongsecrets:ctfd-safe-3-no-vault +FROM jeroenwillemsen/wrongsecrets:ctfd-safe-4-no-vault ARG argBasedVersion="1.5.0" ARG CANARY_URLS="http://canarytokens.com/terms/about/s7cfbdakys13246ewd8ivuvku/post.jsp,http://canarytokens.com/terms/about/y0all60b627gzp19ahqh7rl6j/post.jsp" @@ -9,8 +9,7 @@ ARG CTF_KEY=TRwzkRJnHOTckssAeyJbysWgP!Qc2T ARG CHALLENGE_5_VALUE=if_you_see_this_please_use_k8s ARG CHALLENGE_6_VALUE=if_you_see_this_please_use_k8s ARG CHALLENGE_7_VALUE=if_you_see_this_please_use_K8S_and_Vault -#TODO: CONTINUE ON THE 3 CHALLENGES BELOW AND TEST THEM! (both with and without CTF!) -ARG CHALLENGE_9_VALUE=if_you_see_this_please_use +ARG CHALLENGE_9_VALUE=if_you_see_this_please_use_AWS_Setup ARG CHALLENGE_10_VALUE=if_you_see_this_please_use ARG CHALLENGE_11_VALUE=if_you_see_this_please_use ENV APP_VERSION=$argBasedVersion @@ -24,6 +23,9 @@ ENV keepasspath="/var/helpers/alibabacreds.kdbx" ENV SPECIAL_K8S_SECRET=$CHALLENGE_5_VALUE ENV SPECIAL_SPECIAL_K8S_SECRET=$CHALLENGE_6_VALUE ENV vaultpassword=$CHALLENGE_7_VALUE +ENV default_aws_value_challenge_9=$CHALLENGE_9_VALUE +ENV default_aws_value_challenge_10=$CHALLENGE_10_VALUE +ENV default_aws_value_challenge_11=$CHALLENGE_11_VALUE COPY .github/scripts/ /var/helpers COPY src/test/resources/alibabacreds.kdbx /var/helpers CMD java -Xms128m -Xmx128m -Xss512k -jar -Dserver.port=$PORT -XX:MaxRAMPercentage=75 -XX:MinRAMPercentage=25 -Dspring.profiles.active=without-vault application.jar diff --git a/ctf-instructions.md b/ctf-instructions.md new file mode 100644 index 000000000..298240ac4 --- /dev/null +++ b/ctf-instructions.md @@ -0,0 +1,37 @@ +# CTF Instructions + +So you want to play a CTF with WrongSecrets? This is the place to read up all about it. +Our CTF setup makes use of the [Juice Shop CTF CLI extension](https://github.com/juice-shop/juice-shop-ctf), which you +can read all about at [here](https://pwning.owasp-juice.shop/part1/ctf.html). + +The difference between Juiceshop and WrongSecrets, is that WrongSecrets is more of a secrets-hunter game. Thiss means +that your contestants will try to find the CTF key soon after a few challenges. That is why we should separate out the +actual container for which the CTF scores are generated, from the container where the challenges live in. + +You can see this practice already here in our repository: Our standard [Dockerfile](/Dockerfile) does not contain any +CTF entries, our Heroku [Dockerfile.web](/Dockerfile.web) does contain them. +So make sure you host your actual scoring Dockerfile.web at a place where your contestants cannot enter the container ( +image) in order to extract the CTF key. + +## Setting up CTFs + +There are 3 flavors of CTF to be setup: Docker/Heroku, K8S, Cloud based. + +### Docker or Heroku CTF + +When doing a Docker or Heroku based CTF, you can follow +the [instructions in the readme](https://github.com/commjoen/wrongsecrets#ctfd-support). +If you want to use your own CTF key, you can build a container with the following +arguments `CTF_ENABLED=true,HINTS_ENABLED=false,CTF_KEY=`. Just make sure you provide the same key +to `juice-shop-ctf` when you run it. + +Want to make it a little more exciting? Override the Dockerfile with your preferred values, so that copying from online +hosted solutions no longer works! + +### K8s based CTF - TODO + +TODO + +### Cloud based CTF - TODO + + diff --git a/src/main/java/org/owasp/wrongsecrets/RuntimeEnvironment.java b/src/main/java/org/owasp/wrongsecrets/RuntimeEnvironment.java index a47fb3c49..7f5f92f36 100644 --- a/src/main/java/org/owasp/wrongsecrets/RuntimeEnvironment.java +++ b/src/main/java/org/owasp/wrongsecrets/RuntimeEnvironment.java @@ -26,7 +26,10 @@ public class RuntimeEnvironment { private boolean ctfModeEnabled; @Value("${SPECIAL_K8S_SECRET}") - private String challenge5Value; //used to determine if they are overriden; + private String challenge5Value; //used to determine if k8s/vault challenges are overriden; + + @Value("${default_aws_value_challenge_9}") + private String defaultChallenge9Value; //used to determine if the cloud challenge values are overriden private static final Map> envToOverlappingEnvs = Map.of( HEROKU_DOCKER, List.of(DOCKER, HEROKU_DOCKER), @@ -60,6 +63,11 @@ private boolean isK8sUnlockedInCTFMode() { return ctfModeEnabled && !challenge5Value.equals(defaultValueChallenge5); } + private boolean isCloudUnlockedInCTFMode() { + String defaultValueAWSValue = "if_you_see_this_please_use_AWS_Setup"; + return ctfModeEnabled && !defaultChallenge9Value.equals(defaultValueAWSValue); + } + @Autowired public RuntimeEnvironment(@Value("${K8S_ENV}") String currentRuntimeEnvironment) { this.runtimeEnvironment = Environment.fromId(currentRuntimeEnvironment); @@ -70,9 +78,14 @@ public RuntimeEnvironment(Environment runtimeEnvironment) { } public boolean canRun(Challenge challenge) { + if (isCloudUnlockedInCTFMode()) { + return true; + } if (isK8sUnlockedInCTFMode()) { - return challenge.supportedRuntimeEnvironments().contains(runtimeEnvironment) || challenge.supportedRuntimeEnvironments().contains(K8S) || challenge.supportedRuntimeEnvironments().contains(VAULT); - } //TODO: WRITE TEST FOR THIS & UPDATE UI THEN! + return challenge.supportedRuntimeEnvironments().contains(runtimeEnvironment) || + challenge.supportedRuntimeEnvironments().contains(DOCKER) || challenge.supportedRuntimeEnvironments().contains(K8S) || + challenge.supportedRuntimeEnvironments().contains(VAULT); + } return challenge.supportedRuntimeEnvironments().contains(runtimeEnvironment) || !Collections.disjoint(envToOverlappingEnvs.get(runtimeEnvironment), challenge.supportedRuntimeEnvironments()); } diff --git a/src/main/java/org/owasp/wrongsecrets/canaries/CanariesController.java b/src/main/java/org/owasp/wrongsecrets/canaries/CanariesController.java index 3fc476c88..bb62b4b1d 100644 --- a/src/main/java/org/owasp/wrongsecrets/canaries/CanariesController.java +++ b/src/main/java/org/owasp/wrongsecrets/canaries/CanariesController.java @@ -30,11 +30,6 @@ public ResponseEntity processCanaryToken(@RequestBody CanaryToken canary } log.info("Canarytoken called, with manage_url {}", canaryToken.getManageUrl()); log.info("Total number of canary callback calls: {}", canaryCounter.getTotalCount()); - /* - todo: - - follow 3 of baeldung.com/spring-server-sent-events, but make sure you register the emitter per connection - - and in a map lookup which emiter you can use for the given connection to send the event. - */ return new ResponseEntity<>("all good", HttpStatus.ACCEPTED); } diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/ChallengesAPIController.java b/src/main/java/org/owasp/wrongsecrets/challenges/ChallengesAPIController.java index 409d6e305..ec9175d83 100644 --- a/src/main/java/org/owasp/wrongsecrets/challenges/ChallengesAPIController.java +++ b/src/main/java/org/owasp/wrongsecrets/challenges/ChallengesAPIController.java @@ -8,19 +8,16 @@ import org.owasp.wrongsecrets.RuntimeEnvironment; import org.owasp.wrongsecrets.ScoreCard; import org.owasp.wrongsecrets.asciidoc.TemplateGenerator; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.util.ResourceUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; -import org.yaml.snakeyaml.Yaml; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; -import java.util.Locale; @Slf4j @RestController @@ -35,11 +32,14 @@ public class ChallengesAPIController { private final TemplateGenerator templateGenerator; + private final RuntimeEnvironment runtimeEnvironment; + public ChallengesAPIController(ScoreCard scoreCard, List challenges, RuntimeEnvironment runtimeEnvironment, TemplateGenerator templateGenerator) { this.scoreCard = scoreCard; this.challenges = challenges; this.descriptions = new ArrayList<>(); this.hints = new ArrayList<>(); + this.runtimeEnvironment = runtimeEnvironment; this.templateGenerator = templateGenerator; } @@ -117,8 +117,8 @@ private String extractResource(String resourceName) { } private String getDisabledEnv(ChallengeUI challenge) { - if (!challenge.getChallenge().supportedRuntimeEnvironments().contains(RuntimeEnvironment.Environment.DOCKER)) { - return "Docker"; + if (runtimeEnvironment.canRun(challenge.getChallenge())) { + return runtimeEnvironment.getRuntimeEnvironment().name(); } return null; } diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/cloud/Challenge10.java b/src/main/java/org/owasp/wrongsecrets/challenges/cloud/Challenge10.java index b0965ce91..8b8188294 100644 --- a/src/main/java/org/owasp/wrongsecrets/challenges/cloud/Challenge10.java +++ b/src/main/java/org/owasp/wrongsecrets/challenges/cloud/Challenge10.java @@ -27,7 +27,7 @@ public class Challenge10 extends CloudChallenge { public Challenge10(ScoreCard scoreCard, @Value("${secretmountpath}") String filePath, - @Value("${default_aws_value}") String awsDefaultValue, + @Value("${default_aws_value_challenge_10}") String awsDefaultValue, RuntimeEnvironment runtimeEnvironment) { super(scoreCard, runtimeEnvironment); this.awsDefaultValue = awsDefaultValue; diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/cloud/Challenge11.java b/src/main/java/org/owasp/wrongsecrets/challenges/cloud/Challenge11.java index bc5cefc36..40ccadb59 100644 --- a/src/main/java/org/owasp/wrongsecrets/challenges/cloud/Challenge11.java +++ b/src/main/java/org/owasp/wrongsecrets/challenges/cloud/Challenge11.java @@ -46,6 +46,10 @@ public class Challenge11 extends CloudChallenge { private final String azureVaultUri; private final String azureWrongSecret3; + private final String ctfValue; + + private final boolean ctfEnabled; + public Challenge11(ScoreCard scoreCard, @Value("${AWS_ROLE_ARN}") String awsRoleArn, @Value("${AWS_WEB_IDENTITY_TOKEN_FILE}") String tokenFileLocation, @@ -56,6 +60,8 @@ public Challenge11(ScoreCard scoreCard, @Value("${azure.keyvault.uri}") String azureVaultUri, @Value("${wrongsecret-3}") String azureWrongSecret3, // Exclusively auto-wired for Azure @Value("${GCP_PROJECT_ID}") String projectId, + @Value("${default_aws_value_challenge_11}") String ctfValue, + @Value("${ctf_enabled}") boolean ctfEnabled, RuntimeEnvironment runtimeEnvironment) { super(scoreCard, runtimeEnvironment); this.awsRoleArn = awsRoleArn; @@ -67,6 +73,8 @@ public Challenge11(ScoreCard scoreCard, this.projectId = projectId; this.azureVaultUri = azureVaultUri; this.azureWrongSecret3 = azureWrongSecret3; + this.ctfValue = ctfValue; + this.ctfEnabled = ctfEnabled; this.challengeAnswer = getChallenge11Value(runtimeEnvironment); } @@ -96,6 +104,9 @@ public String getTech() { private String getChallenge11Value(RuntimeEnvironment runtimeEnvironment) { if (runtimeEnvironment != null && runtimeEnvironment.getRuntimeEnvironment() != null) { + if (ctfEnabled && ctfValue != awsDefaultValue) { + return ctfValue; + } return switch (runtimeEnvironment.getRuntimeEnvironment()) { case AWS -> getAWSChallenge11Value(); case GCP -> getGCPChallenge11Value(); @@ -112,27 +123,27 @@ private String getAWSChallenge11Value() { try { //based on https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/example_code/sts/src/main/java/com/example/sts String webIDentityToken = Files.readString(Paths.get(tokenFileLocation)); StsClient stsClient = StsClient.builder() - .region(Region.of(awsRegion)) - .build(); + .region(Region.of(awsRegion)) + .build(); AssumeRoleWithWebIdentityRequest webIdentityRequest = AssumeRoleWithWebIdentityRequest.builder() - .roleArn(awsRoleArn) - .roleSessionName("WrongsecretsApp") - .webIdentityToken(webIDentityToken) - .build(); + .roleArn(awsRoleArn) + .roleSessionName("WrongsecretsApp") + .webIdentityToken(webIDentityToken) + .build(); AssumeRoleWithWebIdentityResponse tokenResponse = stsClient.assumeRoleWithWebIdentity(webIdentityRequest); log.info("The token value is " + tokenResponse.credentials().sessionToken()); SsmClient ssmClient = SsmClient.builder() - .region(Region.of(awsRegion)) - .credentialsProvider(StsAssumeRoleWithWebIdentityCredentialsProvider.builder() - .stsClient(stsClient) - .refreshRequest(webIdentityRequest) - .build()) - .build(); + .region(Region.of(awsRegion)) + .credentialsProvider(StsAssumeRoleWithWebIdentityCredentialsProvider.builder() + .stsClient(stsClient) + .refreshRequest(webIdentityRequest) + .build()) + .build(); GetParameterRequest parameterRequest = GetParameterRequest.builder() - .name("wrongsecretvalue") - .withDecryption(true) - .build(); + .name("wrongsecretvalue") + .withDecryption(true) + .build(); GetParameterResponse parameterResponse = ssmClient.getParameter(parameterRequest); log.info("The parameter value is " + parameterResponse.parameter().value()); ssmClient.close(); diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/cloud/Challenge9.java b/src/main/java/org/owasp/wrongsecrets/challenges/cloud/Challenge9.java index 7b486a9e6..1eb4456b7 100644 --- a/src/main/java/org/owasp/wrongsecrets/challenges/cloud/Challenge9.java +++ b/src/main/java/org/owasp/wrongsecrets/challenges/cloud/Challenge9.java @@ -27,7 +27,7 @@ public class Challenge9 extends CloudChallenge { public Challenge9(ScoreCard scoreCard, @Value("${secretmountpath}") String filePath, - @Value("${default_aws_value}") String awsDefaultValue, + @Value("${default_aws_value_challenge_9}") String awsDefaultValue, RuntimeEnvironment runtimeEnvironment) { super(scoreCard, runtimeEnvironment); this.awsDefaultValue = awsDefaultValue; diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 6c50dd230..f4a7671a2 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -8,6 +8,9 @@ ARG_BASED_PASSWORD=if_you_see_this_please_use_docker_instead DOCKER_ENV_PASSWORD=if_you_see_this_please_use_docker_instead vaultpassword=if_you_see_this_please_use_K8S_and_Vault default_aws_value=if_you_see_this_please_use_AWS_Setup +default_aws_value_challenge_9=if_you_see_this_please_use_AWS_Setup +default_aws_value_challenge_10=if_you_see_this_please_use_AWS_Setup +default_aws_value_challenge_11=if_you_see_this_please_use_AWS_Setup default_gcp_value=if_you_see_this_please_use_GCP_Setup default_azure_value=if_you_see_this_please_use_Azure_Setup AWS_ROLE_ARN=if_you_see_this_please_use_AWS_Setup @@ -68,3 +71,18 @@ spring.config.activate.on-profile=without-vault wrongsecretvalue=wrongsecret spring.cloud.vault.enabled=false asciidoctor.enabled=false +#--- +spring.config.activate.on-profile=without-vault-ctf-emulation +wrongsecretvalue=wrongsecret +spring.cloud.vault.enabled=false +asciidoctor.enabled=false +ctf_enabled=true +ctf_key=randomtextforkey +vaultpassword=ACTUAL_ANSWER_CHALLENGE7 +secretmountpath=nothere +SPECIAL_K8S_SECRET=ACTUAL_ANSWER_CHALLENGE5 +SPECIAL_SPECIAL_K8S_SECRET=ACTUAL_ANSWER_CHALLENGE6 +default_aws_value_challenge_9=ACTUAL_ANSWER_CHALLENGE9 +default_aws_value_challenge_10=ACTUAL_ANSWER_CHALLENGE10 +default_aws_value_challenge_11=ACTUAL_ANSWER_CHALLENGE_11 +K8S_ENV=Heroku(Docker) diff --git a/src/main/resources/templates/challenge.html b/src/main/resources/templates/challenge.html index 483fbd694..e6f0996e6 100644 --- a/src/main/resources/templates/challenge.html +++ b/src/main/resources/templates/challenge.html @@ -84,7 +84,7 @@ diff --git a/src/main/resources/templates/welcome.html b/src/main/resources/templates/welcome.html index 25e717245..408236a42 100644 --- a/src/main/resources/templates/welcome.html +++ b/src/main/resources/templates/welcome.html @@ -20,20 +20,28 @@ + - - - + + + + + @@ -44,12 +52,10 @@ + th:if="${challenge.requiredEnv} == 'AWS' or ${challenge.requiredEnv} == 'GCP' or ${challenge.requiredEnv} == 'AZURE'"> - +
#  Challenge      Focus   DifficultyDifficulty       
  + + + Docker K8s or Minikube with Vault AWS, GCP, Azure - -
@@ -118,7 +124,9 @@
  • OWASP Secretsmanagement Cheatsheet
  • -
  • Open CRE on Secrets Management
  • +
  • Open + CRE on Secrets Management
  • @@ -142,7 +150,9 @@
    - Want to see if your tool of choice detects all the secrets available in this project? Check the instructions in the README. + Want to see if your tool of choice detects all the secrets available in this project? Check + the instructions in the README.
    diff --git a/src/test/java/org/owasp/wrongsecrets/ChallengesControllerCTFModeTest.java b/src/test/java/org/owasp/wrongsecrets/ChallengesControllerCTFModeTest.java index 0f715a176..3617941d0 100644 --- a/src/test/java/org/owasp/wrongsecrets/ChallengesControllerCTFModeTest.java +++ b/src/test/java/org/owasp/wrongsecrets/ChallengesControllerCTFModeTest.java @@ -70,16 +70,27 @@ void shouldEnableK8sExercises() throws Exception{ @Test void shouldStillDissableTestsIfNotPreconfigured() throws Exception { - testChallenge("/challenge/5"); - testChallenge("/challenge/6"); - testChallenge("/challenge/7"); + testK8sChallenge("/challenge/5"); + testK8sChallenge("/challenge/6"); + testK8sChallenge("/challenge/7"); + testForCloudCluster("/challenge/9"); + testForCloudCluster("/challenge/10"); + testForCloudCluster("/challenge/11"); } - private void testChallenge(String url) throws Exception { + private void testK8sChallenge(String url) throws Exception { mvc.perform(get(url) .contentType(MediaType.APPLICATION_FORM_URLENCODED) .with(csrf())) .andExpect(status().isOk()) .andExpect(content().string(containsString("We are running outside a K8s cluster"))); } + + private void testForCloudCluster(String url) throws Exception { + mvc.perform(get(url) + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .with(csrf())) + .andExpect(status().isOk()) + .andExpect(content().string(containsString("We are running outside a properly configured Cloud environment."))); + } } diff --git a/src/test/java/org/owasp/wrongsecrets/ChallengesControllerCTFModeWithPresetCloudValuesTest.java b/src/test/java/org/owasp/wrongsecrets/ChallengesControllerCTFModeWithPresetCloudValuesTest.java new file mode 100644 index 000000000..3ab9d9488 --- /dev/null +++ b/src/test/java/org/owasp/wrongsecrets/ChallengesControllerCTFModeWithPresetCloudValuesTest.java @@ -0,0 +1,105 @@ +package org.owasp.wrongsecrets; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.owasp.wrongsecrets.challenges.cloud.Challenge10; +import org.owasp.wrongsecrets.challenges.cloud.Challenge11; +import org.owasp.wrongsecrets.challenges.cloud.Challenge9; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; + +import static org.hamcrest.Matchers.containsString; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ExtendWith(SpringExtension.class) +@SpringBootTest( + properties = {"ctf_enabled=true", "ctf_key=randomtextforkey", + "SPECIAL_K8S_SECRET=test5", "SPECIAL_SPECIAL_K8S_SECRET=test6", "vaultpassword=test7", + "secretmountpath=nothere", "default_aws_value_challenge_9=ACTUAL_ANSWER_CHALLENGE9", + "default_aws_value_challenge_10=ACTUAL_ANSWER_CHALLENGE10", "default_aws_value_challenge_11=ACTUAL_ANSWER_CHALLENGE_11"}, + classes = WrongSecretsApplication.class +) +@AutoConfigureMockMvc +class ChallengesControllerCTFModeWithPresetCloudValuesTest { + + @Autowired + private MockMvc mvc; + + + @Test + void shouldNotSpoilWhenInCTFMode() throws Exception { + mvc.perform(get("/spoil-9")) + .andExpect(status().isOk()) + .andExpect(content().string(containsString("Spoils are disabled in CTF mode"))); + + } + + @Test + void shouldShowFlagWhenRespondingWithSuccessInCTFModeChallenge9() throws Exception { + var spoil = new Challenge9(new InMemoryScoreCard(1), null, "ACTUAL_ANSWER_CHALLENGE9", new RuntimeEnvironment(RuntimeEnvironment.Environment.HEROKU_DOCKER)).spoiler().solution(); + mvc.perform(post("/challenge/9") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .param("solution", spoil) + .param("action", "submit") + .with(csrf())) + .andExpect(status().isOk()) + .andExpect(content().string(containsString("70d75bf845890b2419bd8795c"))); + } + + @Test + void shouldShowFlagWhenRespondingWithSuccessInCTFModeChallenge10() throws Exception { + var spoil = new Challenge10(new InMemoryScoreCard(1), null, "ACTUAL_ANSWER_CHALLENGE10", new RuntimeEnvironment(RuntimeEnvironment.Environment.HEROKU_DOCKER)).spoiler().solution(); + mvc.perform(post("/challenge/10") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .param("solution", spoil) + .param("action", "submit") + .with(csrf())) + .andExpect(status().isOk()) + .andExpect(content().string(containsString("176e937a2cafea3b0da3"))); + } + + @Test + void shouldShowFlagWhenRespondingWithSuccessInCTFModeChallenge11() throws Exception { + var spoil = new Challenge11(new InMemoryScoreCard(1), + "awsRoleArn", "tokenFileLocation", + "awsRegion", "gcpDefualtValue", "awsDefaultValue", + "azureDefaultValue", "azureVaultUri", "azureWrongSecret3", + "projectId", "ACTUAL_ANSWER_CHALLENGE_11", true, + new RuntimeEnvironment(RuntimeEnvironment.Environment.HEROKU_DOCKER)).spoiler().solution(); + + mvc.perform(post("/challenge/11") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .param("solution", spoil) + .param("action", "submit") + .with(csrf())) + .andExpect(status().isOk()) + .andExpect(content().string(containsString("89aeb4b29d4a0bc13bd"))); + } + + @Test + void shouldEnableCloudExercises() throws Exception { + mvc.perform(get("/")) + .andExpect(status().isOk()) + .andExpect(content().string(containsString(" Challenge 9"))) + .andExpect(content().string(containsString(" Challenge 10"))) + .andExpect(content().string(containsString(" Challenge 11"))); + } + + @Test + void shouldEnableK8sExercises() throws Exception{ + mvc.perform(get("/")) + .andExpect(status().isOk()) + .andExpect(content().string(containsString(" Challenge 5"))) + .andExpect(content().string(containsString(" Challenge 6"))) + .andExpect(content().string(containsString(" Challenge 7"))); + } + +}