package org.jboss.resteasy.test.xxe; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.container.test.api.RunAsClient; import org.jboss.arquillian.junit.Arquillian; import org.jboss.resteasy.client.jaxrs.ResteasyClient; import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; import org.jboss.resteasy.test.xxe.resource.XxeSecureProcessingFavoriteMovieXmlRootElement; import org.jboss.resteasy.test.xxe.resource.XxeSecureProcessingMovieResource; import org.jboss.resteasy.utils.PortProviderUtil; import org.jboss.resteasy.utils.TestUtil; import org.jboss.shrinkwrap.api.Archive; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import javax.ws.rs.client.Entity; import javax.ws.rs.core.Response; import java.util.HashMap; import java.util.Map; /** * @tpSubChapter XXE * @tpChapter Integration tests * @tpTestCaseDetails Regression test for RESTEASY-869 * @tpSince RESTEasy 3.0.16 */ @RunWith(Arquillian.class) @RunAsClient public class XxeSecureProcessingTest { private ResteasyClient client; public final Logger logger = LogManager.getLogger(XxeSecureProcessingTest.class.getName()); String doctype = "<!DOCTYPE foodocument [" + "<!ENTITY foo 'foo'>" + "<!ENTITY foo1 '&foo;&foo;&foo;&foo;&foo;&foo;&foo;&foo;&foo;&foo;'>" + "<!ENTITY foo2 '&foo1;&foo1;&foo1;&foo1;&foo1;&foo1;&foo1;&foo1;&foo1;&foo1;'>" + "<!ENTITY foo3 '&foo2;&foo2;&foo2;&foo2;&foo2;&foo2;&foo2;&foo2;&foo2;&foo2;'>" + "<!ENTITY foo4 '&foo3;&foo3;&foo3;&foo3;&foo3;&foo3;&foo3;&foo3;&foo3;&foo3;'>" + "<!ENTITY foo5 '&foo4;&foo4;&foo4;&foo4;&foo4;&foo4;&foo4;&foo4;&foo4;&foo4;'>" + "<!ENTITY foo6 '&foo5;&foo5;&foo5;&foo5;&foo5;&foo5;&foo5;&foo5;&foo5;&foo5;'>" + "<!ENTITY foo7 '&foo6;&foo6;&foo6;&foo6;&foo6;&foo6;&foo6;&foo6;&foo6;&foo6;'>" + "<!ENTITY foo8 '&foo7;&foo7;&foo7;&foo7;&foo7;&foo7;&foo7;&foo7;&foo7;&foo7;'>" + "<!ENTITY foo9 '&foo8;&foo8;&foo8;&foo8;&foo8;&foo8;&foo8;&foo8;&foo8;&foo8;'>" + "]>"; String small = doctype + "<xxeSecureProcessingFavoriteMovieXmlRootElement><title>&foo4;</title></xxeSecureProcessingFavoriteMovieXmlRootElement>"; String big = doctype + "<xxeSecureProcessingFavoriteMovieXmlRootElement><title>&foo5;</title></xxeSecureProcessingFavoriteMovieXmlRootElement>"; private static final String T_DEFAULT = "default"; private static final String T_TRUE = "true"; private static final String T_FALSE = "false"; public static Archive<?> deploy(String expandEntityReferences) { WebArchive war = TestUtil.prepareArchive(expandEntityReferences); Map<String, String> contextParam = new HashMap<>(); contextParam.put("resteasy.document.secure.disableDTDs", "false"); if (expandEntityReferences != null) { contextParam.put("resteasy.document.expand.entity.references", expandEntityReferences); } war.addClass(XxeSecureProcessingFavoriteMovieXmlRootElement.class); return TestUtil.finishContainerPrepare(war, contextParam, XxeSecureProcessingMovieResource.class); } @Before public void init() { client = new ResteasyClientBuilder().build(); } @After public void after() throws Exception { client.close(); } @Deployment(name = T_DEFAULT) public static Archive<?> deployDefault() { return deploy(null); } @Deployment(name = T_TRUE) public static Archive<?> deployTrue() { return deploy("true"); } @Deployment(name = T_FALSE) public static Archive<?> deployFalse() { return deploy("false"); } /** * @tpTestDetails Small request in XML root element. "resteasy.document.expand.entity.references" property is not set. * @tpSince RESTEasy 3.0.16 */ @Test public void testXmlRootElementDefaultSmall() throws Exception { Response response = client.target(PortProviderUtil.generateURL("/xmlRootElement", null)).request().post(Entity.entity(small, "application/xml")); Assert.assertEquals(200, response.getStatus()); String entity = response.readEntity(String.class); logger.debug("Result: " + entity.substring(0, 30)); Assert.assertEquals(10000, countFoos(entity)); response.close(); } /** * @tpTestDetails Big request in XML root element. "resteasy.document.expand.entity.references" property is not set. * @tpSince RESTEasy 3.0.16 */ @Test public void testXmlRootElementDefaultBig() throws Exception { Response response = client.target(PortProviderUtil.generateURL("/xmlRootElement", null)).request().post(Entity.entity(big, "application/xml")); Assert.assertEquals(400, response.getStatus()); String entity = response.readEntity(String.class); logger.debug("Result: " + entity); Assert.assertTrue(entity.contains("javax.xml.bind.UnmarshalException")); response.close(); } /** * @tpTestDetails Small request in XML root element. "resteasy.document.expand.entity.references" property is set to false. * @tpSince RESTEasy 3.0.16 */ @Test public void testXmlRootElementWithoutExternalExpansionSmall() throws Exception { Response response = client.target(PortProviderUtil.generateURL("/xmlRootElement", T_FALSE)).request() .post(Entity.entity(small, "application/xml")); Assert.assertEquals(200, response.getStatus()); String entity = response.readEntity(String.class); logger.debug("Result: " + entity.substring(0, 30)); Assert.assertEquals(10000, countFoos(entity)); response.close(); } /** * @tpTestDetails Big request in XML root element. "resteasy.document.expand.entity.references" property is set to false. * @tpSince RESTEasy 3.0.16 */ @Test public void testXmlRootElementWithoutExternalExpansionBig() throws Exception { Response response = client.target(PortProviderUtil.generateURL("/xmlRootElement", T_FALSE)).request() .post(Entity.entity(big, "application/xml")); Assert.assertEquals(400, response.getStatus()); String entity = response.readEntity(String.class); logger.debug("Result: " + entity); Assert.assertTrue(entity.contains("javax.xml.bind.UnmarshalException")); response.close(); } /** * @tpTestDetails Small request in XML root element. "resteasy.document.expand.entity.references" property is set to true. * @tpSince RESTEasy 3.0.16 */ @Test public void testXmlRootElementWithExternalExpansionSmall() throws Exception { Response response = client.target(PortProviderUtil.generateURL("/xmlRootElement", T_TRUE)).request() .post(Entity.entity(small, "application/xml")); Assert.assertEquals(200, response.getStatus()); String entity = response.readEntity(String.class); logger.debug("Result: " + entity.substring(0, 30)); Assert.assertEquals(10000, countFoos(entity)); response.close(); } /** * @tpTestDetails Big request in XML root element. "resteasy.document.expand.entity.references" property is set to true. * @tpSince RESTEasy 3.0.16 */ @Test public void testXmlRootElementWithExternalExpansionBig() throws Exception { Response response = client.target(PortProviderUtil.generateURL("/xmlRootElement", T_TRUE)).request() .post(Entity.entity(big, "application/xml")); Assert.assertEquals(400, response.getStatus()); String entity = response.readEntity(String.class); logger.debug("Result: " + entity); Assert.assertTrue(entity.contains("javax.xml.bind.UnmarshalException")); response.close(); } private int countFoos(String s) { int count = 0; int pos = 0; while (pos >= 0) { pos = s.indexOf("foo", pos); if (pos >= 0) { count++; pos += 3; } } return count; } }