diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/StatusCodeChallenges.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/StatusCodeChallenges.java index fe4e5dea..2b1c9f0b 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/StatusCodeChallenges.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/StatusCodeChallenges.java @@ -64,8 +64,9 @@ public static ChallengeDefinitionData overridePostToPatchFor500(int challengeOrd "Issue a POST request on the `/heartbeat` end point and receive 500 when you override the Method Verb to a PATCH"); aChallenge.addHint("Use a normal POST Request, but add an X-HTTP-Method-Override header"); + aChallenge.addSolutionLink("Add a header 'X-HTTP-Method-Override: PATCH' to a POST /heartbeat request", "",""); - //aChallenge.addSolutionLink("Read Solution", "HREF","https://www.eviltester.com/apichallenges/howto/25-26-27-28-status-codes-405-500-501-204/"); + aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/method-override/all-method-overrides"); //aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "SGfKVFdylVI"); return aChallenge; } @@ -77,8 +78,9 @@ public static ChallengeDefinitionData overridePostToDeleteFor405(int challengeOr "Issue a POST request on the `/heartbeat` end point and receive 405 when you override the Method Verb to a DELETE"); aChallenge.addHint("Use a normal POST Request, but add an X-HTTP-Method-Override header"); + aChallenge.addSolutionLink("Add a header 'X-HTTP-Method-Override: DELETE' to a POST /heartbeat request", "",""); - //aChallenge.addSolutionLink("Read Solution", "HREF","https://www.eviltester.com/apichallenges/howto/25-26-27-28-status-codes-405-500-501-204/"); + aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/method-override/all-method-overrides"); //aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "SGfKVFdylVI"); return aChallenge; } @@ -90,9 +92,10 @@ public static ChallengeDefinitionData overridePostToTraceFor501(int challengeOrd "POST /heartbeat as Trace (501)", "Issue a POST request on the `/heartbeat` end point and receive 501 (Not Implemented) when you override the Method Verb to a TRACE"); aChallenge.addHint("Use a normal POST Request, but add an X-HTTP-Method-Override header"); + aChallenge.addSolutionLink("Add a header 'X-HTTP-Method-Override: TRACE' to a POST /heartbeat request", "",""); + aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/method-override/all-method-overrides"); -// aChallenge.addSolutionLink("Read Solution", "HREF","https://www.eviltester.com/apichallenges/howto/25-26-27-28-status-codes-405-500-501-204/"); // aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "SGfKVFdylVI"); return aChallenge; } diff --git a/challenger/src/main/resources/content/apichallenges/solutions.md b/challenger/src/main/resources/content/apichallenges/solutions.md index cfc1b3a5..7a501c7f 100644 --- a/challenger/src/main/resources/content/apichallenges/solutions.md +++ b/challenger/src/main/resources/content/apichallenges/solutions.md @@ -97,6 +97,7 @@ description: A list of all the solutions for the API Challenges. Try them yourse ## HTTP Method Override Challenges +- [Solve the /heartbeat Method Override Challenges](/apichallenges/solutions/method-overrides/all-method-overrides) - POST /heartbeat as DELETE (405) - POST /heartbeat as PATCH (500) - POST /heartbeat as Trace (501) diff --git a/challenger/src/main/resources/content/apichallenges/solutions/method-override/all-method-overrides.md b/challenger/src/main/resources/content/apichallenges/solutions/method-override/all-method-overrides.md new file mode 100644 index 00000000..c1523a5c --- /dev/null +++ b/challenger/src/main/resources/content/apichallenges/solutions/method-override/all-method-overrides.md @@ -0,0 +1,60 @@ +--- +date: 2025-01-01T14:54:00Z +title: API Challenges Solution For - Method Override Challenges +description: How to solve API challenges for Method Override DELETE, PATCH, TRACE. +--- + +# How to complete the HTTP Method Override Challenges + +All of the method override challenges use the same mechanism so we can cover them all in this solution. + +Sometimes tools and libraries will not issue TRACE or PATCH requests. There is a specific HTTP header we can use to try and have POST requests treated as other verbs. + +The header "X-HTTP-Method-Override" is not guaranteed to work on every server, but some HTTP servers will take this header and treat the request using the value in the header: + +`X-HTTP-Method-Override: DELETE` + +This is worth understanding because it might also be used to bypass validation, or trigger functionality that the user is not authorized to trigger. + + +## POST /heartbeat + +> Issue a `POST` request to `/heartbeat` with an `X-HTTP-Method-Override` header specifying the verb you actually want + +- `POST` request can be sent by all tools +- We need to add the header `X-HTTP-Method-Override` to the request and the value should be the verb we want to send e.g. `TRACE` + + +## Basic Instructions + +Each challenge requires a different verb, but the process is the same for each, the only difference is the value of the `X-HTTP-Method-Override` header + +- Issue a POST request to end point "/heartbeat" +- The request should have an `X-HTTP-Method-Override` with the value associated with the challenge i.e. `DELETE`, `PATCH`, `TRACE` +- The request should have an `X-CHALLENGER` header to track challenge completion +- The response status code should match the value for teh challenge overridden verb + - for `DELETE` be `405` + - for `TRACE` be `501` + - for `PATCH` be `500` as the API is simulating a server error + +NOTE: This header feature is normally implemented by the HTTP server so often development teams are not even aware that this is possible. Depending on how requests are validated in code it might be possible for someone, who has amend access using `POST` but who does not have `DELETE` access, to be able to use this header approach to delete something. + +NOTE: As an additional exercise, you might want to see if you can DELETE todos using a POST and the `X-HTTP-Method-Override` header. Experiment and see what you can achieve using this approach. + +## Example Request + +~~~~~~~~ +> POST /todos/3 HTTP/1.1 +> Host: {{}} +> User-Agent: rest-client +> X-HTTP-Method-Override: DELETE +> X-CHALLENGER: x-challenger-guid +> Content-Type: application/json +> Accept: */* +> Content-Length: 108 +~~~~~~~~ + + + + + diff --git a/challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/_16_http_method_override_challenges/CXXXextraDeleteExistingTodo200Test.java b/challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/_16_http_method_override_challenges/CXXXextraDeleteExistingTodo200Test.java new file mode 100644 index 00000000..00e72be6 --- /dev/null +++ b/challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/_16_http_method_override_challenges/CXXXextraDeleteExistingTodo200Test.java @@ -0,0 +1,40 @@ +package uk.co.compendiumdev.challenger.restassured._16_http_method_override_challenges; + +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import org.junit.jupiter.api.Test; +import uk.co.compendiumdev.challenger.payloads.Todo; +import uk.co.compendiumdev.challenger.restassured.api.RestAssuredBaseTest; +import uk.co.compendiumdev.challenger.restassured.api.TodosApi; + +public class CXXXextraDeleteExistingTodo200Test extends RestAssuredBaseTest { + + @Test + void canDeleteATodoItem(){ + + TodosApi api = new TodosApi(); + + Todo created = api.createTodo("my new todo", + "my description", + true); + + RestAssured. + given(). + header("X-CHALLENGER", xChallenger). + header("X-HTTP-Method-Override", "DELETE"). + accept("application/json"). + post(apiPath( "/todos/" + created.id)). + then(). + statusCode(200). + contentType(ContentType.JSON); + + // check it was actually deleted + RestAssured. + given(). + accept("application/json"). + get(apiPath( "/todos/" + created.id)). + then(). + statusCode(404); + } + +}