package org.jboss.resteasy.test.xxe; import java.io.File; import javax.ws.rs.client.Entity; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; 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.ExternalParameterEntityResource; import org.jboss.resteasy.test.xxe.resource.ExternalParameterEntityWrapper; import org.jboss.resteasy.util.HttpResponseCodes; 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 static org.jboss.resteasy.utils.PortProviderUtil.generateURL; /** * @tpSubChapter XXE * @tpChapter Integration tests * @tpTestCaseDetails Regression test for RESTEASY-1073. * External parameter entities should be disabled when the resteasy.document.expand.entity.references parameter was set to false. * A remote attacker able to send XML requests to a RESTEasy endpoint could use this flaw to read files accessible to the user * running the application server, and potentially perform other more advanced XXE attacks. * @tpSince RESTEasy 3.0.16 */ @RunWith(Arquillian.class) @RunAsClient public class ExternalParameterEntityTest { protected final Logger logger = LogManager.getLogger(ExternalParameterEntityTest.class.getName()); static ResteasyClient client; private static final String EXPAND = "war_expand"; private static final String NO_EXPAND = "war_no_expand"; private String passwdFile = new File(TestUtil.getResourcePath(ExternalParameterEntityTest.class, "ExternalParameterEntityPasswd")).getAbsolutePath(); private String dtdFile = new File(TestUtil.getResourcePath(ExternalParameterEntityTest.class, "ExternalParameterEntity.dtd")).getAbsolutePath(); private String request = "<!DOCTYPE foo [\r" + " <!ENTITY % file SYSTEM \"" + passwdFile + "\">\r" + " <!ENTITY % start \"<![CDATA[\">\r" + " <!ENTITY % end \"]]>\">\r" + " <!ENTITY % dtd SYSTEM \"" + dtdFile + "\">\r" + "%dtd;\r" + "]>\r" + "<externalParameterEntityWrapper><name>&xxe;</name></externalParameterEntityWrapper>"; @Deployment(name = EXPAND, order = 1) public static Archive<?> createTestArchive1() { WebArchive war = TestUtil.prepareArchive(EXPAND); war.addClass(ExternalParameterEntityWrapper.class); war.addAsWebInfResource(ExternalParameterEntityTest.class.getPackage(), "ExternalParameterEntityExpandWeb.xml", "web.xml"); return TestUtil.finishContainerPrepare(war, null, ExternalParameterEntityResource.class); } @Deployment(name = NO_EXPAND, order = 2) public static Archive<?> createTestArchive2() { WebArchive war = TestUtil.prepareArchive(NO_EXPAND); war.addClass(ExternalParameterEntityWrapper.class); war.addAsWebInfResource(ExternalParameterEntityTest.class.getPackage(), "ExternalParameterEntityNoExpandWeb.xml", "web.xml"); return TestUtil.finishContainerPrepare(war, null, ExternalParameterEntityResource.class); } @Before public void init() { client = new ResteasyClientBuilder().build(); } @After public void after() throws Exception { client.close(); } /** * @tpTestDetails "resteasy.document.expand.entity.references" is set to "true" * @tpPassCrit Passwd file should be returned by request. * @tpSince RESTEasy 3.0.16 */ @Test public void testExternalParameterEntityExpand() throws Exception { logger.info(String.format("Request body: %s", this.request.replace('\r', '\n'))); Response response = client.target(generateURL("/test", EXPAND)).request() .post(Entity.entity(this.request, MediaType.APPLICATION_XML)); Assert.assertEquals(HttpResponseCodes.SC_OK, response.getStatus()); String entity = response.readEntity(String.class); logger.info(String.format("Result: \"%s\"", entity.replace('\r', '\n'))); Assert.assertEquals("root:x:0:0:root:/root:/bin/bash", entity.trim()); } /** * @tpTestDetails "resteasy.document.expand.entity.references" is set to "false" * @tpPassCrit Passwd file should not be returned by request. * @tpSince RESTEasy 3.0.16 */ @Test public void testExternalParameterEntityNoExpand() throws Exception { logger.info(String.format("Request body: %s", this.request.replace('\r', '\n'))); Response response = client.target(generateURL("/test", NO_EXPAND)).request() .post(Entity.entity(this.request, MediaType.APPLICATION_XML)); Assert.assertEquals(HttpResponseCodes.SC_OK, response.getStatus()); String entity = response.readEntity(String.class); logger.info(String.format("Result: \"%s\"", entity.replace('\r', '\n'))); Assert.assertEquals("", entity.trim()); } }