/* (c) 2014 - 2015 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.wfs;
import static org.junit.Assert.assertTrue;
import java.io.File;
import org.apache.commons.io.FileUtils;
import org.custommonkey.xmlunit.XMLUnit;
import org.custommonkey.xmlunit.XpathEngine;
import org.geoserver.config.GeoServerInfo;
import org.geoserver.data.test.MockData;
import org.geotools.xml.PreventLocalEntityResolver;
import org.junit.Assert;
import org.junit.Test;
import org.w3c.dom.Document;
public class ExternalEntitiesTest extends WFSTestSupport {
private static final String WFS_1_0_0_REQUEST = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" +
"<!DOCTYPE wfs:GetFeature [\r\n" +
"<!ENTITY c SYSTEM \"FILE:///this/file/does/not/exist?.XSD\">\r\n" +
"]>\r\n" +
"<wfs:GetFeature service=\"WFS\" version=\"1.0.0\" \r\n" +
" outputFormat=\"GML2\"\r\n" +
" xmlns:cdf=\"http://www.opengis.net/cite/data\"\r\n" +
" xmlns:wfs=\"http://www.opengis.net/wfs\"\r\n" +
" xmlns:ogc=\"http://www.opengis.net/ogc\"\r\n" +
" xmlns:gml=\"http://www.opengis.net/gml\"\r\n" +
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n" +
" xsi:schemaLocation=\"http://www.opengis.net/wfs\r\n" +
" http://schemas.opengis.net/wfs/1.0.0/WFS-basic.xsd\">\r\n" +
" <wfs:Query typeName=\"cdf:Fifteen\" handle=\"test\">\r\n" +
" <ogc:Literal>&c;</ogc:Literal>\r\n" +
" <ogc:Filter>\r\n" +
" <ogc:BBOX>\r\n" +
" <ogc:PropertyName>the_geom</ogc:PropertyName>\r\n" +
" <gml:Box srsName=\"http://www.opengis.net/gml/srs/epsg.xml#4326\">\r\n" +
" <gml:coordinates>-75.102613,40.212597 -72.361859,41.512517</gml:coordinates>\r\n" +
" </gml:Box>\r\n" +
" </ogc:BBOX>\r\n" +
" </ogc:Filter>\r\n" +
" </wfs:Query>\r\n" +
"</wfs:GetFeature>";
private static final String WFS_1_1_0_REQUEST = "<!DOCTYPE wfs:GetFeature [\r\n" +
"<!ELEMENT wfs:GetFeature (wfs:Query*)>\r\n" +
"<!ATTLIST wfs:GetFeature\r\n" +
" service CDATA #FIXED \"WFS\"\r\n" +
" version CDATA #FIXED \"1.1.0\"\r\n" +
" xmlns:wfs CDATA #FIXED \"http://www.opengis.net/wfs\"\r\n" +
" xmlns:ogc CDATA #FIXED \"http://www.opengis.net/ogc\">\r\n" +
"<!ELEMENT wfs:Query (wfs:PropertyName*,ogc:Filter?)>\r\n" +
"<!ATTLIST wfs:Query typeName CDATA #FIXED \"cdf:Fifteen\">\r\n" +
"<!ELEMENT wfs:PropertyName (#PCDATA) >\r\n" +
"<!ELEMENT ogc:Filter (ogc:FeatureId*)>\r\n" +
"<!ELEMENT ogc:FeatureId EMPTY>\r\n" +
"<!ATTLIST ogc:FeatureId fid CDATA #FIXED \"states.3\">\r\n" +
"\r\n" +
"<!ENTITY passwd SYSTEM \"FILE:///this/file/does/not/exist?.XSD\">]>\r\n" +
"<wfs:GetFeature service=\"WFS\" version=\"1.1.0\" \r\n" +
" xmlns:wfs=\"http://www.opengis.net/wfs\"\r\n" +
" xmlns:ogc=\"http://www.opengis.net/ogc\">\r\n" +
" <wfs:Query typeName=\"cdf:Fifteen\">\r\n" +
" <wfs:PropertyName>&passwd;</wfs:PropertyName>\r\n" +
" <ogc:Filter>\r\n" +
" <ogc:FeatureId fid=\"states.3\"/>\r\n" +
" </ogc:Filter>\r\n" +
" </wfs:Query>\r\n" +
"</wfs:GetFeature>";
private static final String WFS_2_0_0_REQUEST = "<?xml version=\"1.0\" ?>\r\n" +
"<!DOCTYPE wfs:GetFeature [\r\n" +
"<!ELEMENT wfs:GetFeature (wfs:Query*)>\r\n" +
"<!ATTLIST wfs:GetFeature\r\n" +
" service CDATA #FIXED \"WFS\"\r\n" +
" version CDATA #FIXED \"2.0.0\"\r\n" +
" outputFormat CDATA #FIXED \"application/gml+xml; version=3.2\"\r\n" +
" xmlns:wfs CDATA #FIXED \"http://www.opengis.net/wfs\"\r\n" +
" xmlns:ogc CDATA #FIXED \"http://www.opengis.net/ogc\"\r\n" +
" xmlns:fes CDATA #FIXED \"http://www.opengis.net/fes/2.0\">\r\n" +
"<!ELEMENT wfs:Query (wfs:PropertyName*,ogc:Filter?)>\r\n" +
"<!ATTLIST wfs:Query typeName CDATA #FIXED \"cdf:Fifteen\">\r\n" +
"<!ELEMENT wfs:PropertyName (#PCDATA) >\r\n" +
"<!ELEMENT ogc:Filter (fes:ResourceId*)>\r\n" +
"<!ELEMENT fes:ResourceId EMPTY>\r\n" +
"<!ATTLIST fes:ResourceId rid CDATA #FIXED \"states.3\">\r\n" +
"\r\n" +
"<!ENTITY passwd SYSTEM \"FILE:///thisfiledoesnotexist?.XSD\">\r\n" +
"]>\r\n" +
"<wfs:GetFeature service=\"WFS\" version=\"2.0.0\" outputFormat=\"application/gml+xml; version=3.2\"\r\n" +
" xmlns:wfs=\"http://www.opengis.net/wfs/2.0\"\r\n" +
" xmlns:fes=\"http://www.opengis.net/fes/2.0\">\r\n" +
" <wfs:Query typeName=\"cdf:Fifteen\">\r\n" +
" <wfs:PropertyName>&passwd;</wfs:PropertyName>\r\n" +
" <fes:Filter>\r\n" +
" <fes:ResourceId rid=\"states.3\"/>\r\n" +
" </fes:Filter>\r\n" +
" </wfs:Query>\r\n" +
"</wfs:GetFeature>";
@Test
public void testWfs1_0() throws Exception {
GeoServerInfo cfg = getGeoServer().getGlobal();
try {
// enable entity parsing
cfg.setXmlExternalEntitiesEnabled(true);
getGeoServer().save(cfg);
String output = string(post("wfs", WFS_1_0_0_REQUEST));
// the server tried to read a file on local file system
Assert.assertTrue(output.indexOf("java.io.FileNotFoundException") > -1);
// disable entity parsing
cfg.setXmlExternalEntitiesEnabled(false);
getGeoServer().save(cfg);
output = string(post("wfs", WFS_1_0_0_REQUEST));
Assert.assertTrue(output.indexOf("Entity resolution disallowed") > -1);
// set default (entity parsing disabled);
cfg.setXmlExternalEntitiesEnabled(null);
getGeoServer().save(cfg);
output = string(post("wfs", WFS_1_0_0_REQUEST));
Assert.assertTrue(output.indexOf("Entity resolution disallowed") > -1);
} finally {
cfg.setXmlExternalEntitiesEnabled(null);
getGeoServer().save(cfg);
}
}
@Test
public void testWfs1_1() throws Exception {
GeoServerInfo cfg = getGeoServer().getGlobal();
try {
// enable entity parsing
cfg.setXmlExternalEntitiesEnabled(true);
getGeoServer().save(cfg);
String output = string(post("wfs", WFS_1_1_0_REQUEST));
// the server tried to read a file on local file system
Assert.assertTrue(output.indexOf("java.io.FileNotFoundException") > -1);
// disable entity parsing
cfg.setXmlExternalEntitiesEnabled(false);
getGeoServer().save(cfg);
output = string(post("wfs", WFS_1_1_0_REQUEST));
Assert.assertTrue(output.indexOf("Entity resolution disallowed") > -1);
// set default (entity parsing disabled);
cfg.setXmlExternalEntitiesEnabled(null);
getGeoServer().save(cfg);
output = string(post("wfs", WFS_1_1_0_REQUEST));
Assert.assertTrue(output.indexOf("Entity resolution disallowed") > -1);
} finally {
cfg.setXmlExternalEntitiesEnabled(null);
getGeoServer().save(cfg);
}
}
@Test
public void testWfs2_0() throws Exception {
GeoServerInfo cfg = getGeoServer().getGlobal();
try {
// enable entity parsing
cfg.setXmlExternalEntitiesEnabled(true);
getGeoServer().save(cfg);
String output = string(post("wfs", WFS_2_0_0_REQUEST));
// the server tried to read a file on local file system
Assert.assertTrue(output.indexOf("thisfiledoesnotexist") > -1);
// disable entity parsing
cfg.setXmlExternalEntitiesEnabled(false);
getGeoServer().save(cfg);
output = string(post("wfs", WFS_2_0_0_REQUEST));
System.out.println(output);
Assert.assertTrue(output.indexOf("Request parsing failed") > -1);
Assert.assertTrue(output
.contains(PreventLocalEntityResolver.ERROR_MESSAGE_BASE));
// set default (entity parsing disabled);
cfg.setXmlExternalEntitiesEnabled(null);
getGeoServer().save(cfg);
output = string(post("wfs", WFS_2_0_0_REQUEST));
Assert.assertTrue(output.indexOf("Request parsing failed") > -1);
Assert.assertTrue(output
.contains(PreventLocalEntityResolver.ERROR_MESSAGE_BASE));
} finally {
cfg.setXmlExternalEntitiesEnabled(null);
getGeoServer().save(cfg);
}
}
@Test
public void testKvpEntityExpansion() throws Exception {
// prepare the file to be expanded
File messageFile = new File("./target/message.txt");
FileUtils.writeStringToFile(messageFile, "broken!");
String filePath = messageFile.getCanonicalPath().replace('\\', '/');
// filter with entity expansion to a ./message.txt file
String filter = "%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22ISO-8859-1%22%3F%3E%20%3C!DOCTYPE%20foo%20%5B%20%3C!ENTITY%20xxe%20SYSTEM%20%22file%3A%2F%2F"
+ filePath.replace("/", "%2F")
+ "%22%20%3E%5D%3E%3CFilter%20%3E%3E%3CPropertyIsEqualTo%3E%3CPropertyName%3E%26xxe%3B%3C%2FPropertyName%3E%3CLiteral%3EUtrecht%3C%2FLiteral%3E%3C%2FPropertyIsEqualTo%3E%3C%2FFilter%3E";
String request = "wfs?request=GetFeature&SERVICE=WFS&VERSION=1.0.0&TYPENAME="
+ getLayerId(MockData.FIFTEEN) + "&FILTER=" + filter;
Document doc = getAsDOM(request);
XpathEngine xp = XMLUnit.newXpathEngine();
String errorMessage = xp.evaluate("//ogc:ServiceException", doc);
// print(doc);
assertTrue(errorMessage.contains(PreventLocalEntityResolver.ERROR_MESSAGE_BASE));
}
}