Merge pull request #59 from artem-smotrakov/better-xml-parsing

Better XML parsing
This commit is contained in:
mibo 2019-11-18 09:02:48 +01:00 committed by GitHub
commit 3cf93d5e06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 268 additions and 4 deletions

View File

@ -40,6 +40,8 @@ import org.apache.olingo.fit.utils.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static javax.xml.stream.XMLInputFactory.*;
public class Metadata extends AbstractMetadataElement {
/**
@ -57,6 +59,8 @@ public class Metadata extends AbstractMetadataElement {
try {
final XMLInputFactory ifactory = XMLInputFactory.newInstance();
ifactory.setProperty(SUPPORT_DTD, false);
ifactory.setProperty(IS_SUPPORTING_EXTERNAL_ENTITIES, false);
final XMLEventReader reader = ifactory.createXMLEventReader(is, org.apache.olingo.commons.api.Constants.UTF8);
try {

View File

@ -31,6 +31,8 @@ import javax.xml.stream.events.XMLEvent;
import org.apache.commons.io.IOUtils;
import static javax.xml.stream.XMLInputFactory.*;
public class XMLEventReaderWrapper implements XMLEventReader {
private static final Charset ENCODING = Charset.forName(org.apache.olingo.commons.api.Constants.UTF8);
@ -58,6 +60,8 @@ public class XMLEventReaderWrapper implements XMLEventReader {
CONTENT_STAG = startBuilder.toString();
final XMLInputFactory factory = XMLInputFactory.newInstance();
factory.setProperty(SUPPORT_DTD, false);
factory.setProperty(IS_SUPPORTING_EXTERNAL_ENTITIES, false);
final InputStreamReader reader = new InputStreamReader(
new ByteArrayInputStream((CONTENT_STAG

View File

@ -60,6 +60,8 @@ import org.apache.commons.vfs2.FileSystemException;
import org.apache.olingo.fit.metadata.Metadata;
import org.apache.olingo.fit.metadata.NavigationProperty;
import static javax.xml.stream.XMLInputFactory.*;
public class XMLUtilities extends AbstractUtilities {
private static final Pattern ENTITY_URI_PATTERN = Pattern.compile(".*\\/.*\\(.*\\)");
@ -80,6 +82,8 @@ public class XMLUtilities extends AbstractUtilities {
protected XMLEventReader getEventReader(final InputStream is) throws XMLStreamException {
if (ifactory == null) {
ifactory = XMLInputFactory.newInstance();
ifactory.setProperty(SUPPORT_DTD, false);
ifactory.setProperty(IS_SUPPORTING_EXTERNAL_ENTITIES, false);
}
return ifactory.createXMLEventReader(new InputStreamReader(is, Constants.DECODER));

View File

@ -0,0 +1,168 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.olingo.fit.metadata;
import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import static org.junit.Assert.fail;
public class MetadataTest {
@Test
public void testExternalEntity() throws IOException {
TestHttpServer server = new TestHttpServer("secret");
try {
String xml = String.format(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<!DOCTYPE oops [\n"
+ " <!ENTITY foo SYSTEM \"%s\" >\n"
+ "]>\n"
+ "<oops>&foo;</oops>",
server.url());
new Metadata(new ByteArrayInputStream(xml.getBytes()));
} catch (Exception e) {
e.printStackTrace(System.out);
} finally {
server.close();
}
if (server.accepted()) {
fail("Oops! The server has been reached!");
}
}
@Test
public void testExternalSchema() throws IOException {
TestHttpServer server = new TestHttpServer("secret");
try {
String xml = String.format(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<!DOCTYPE oops SYSTEM “%s”>\n"
+ "<oops>&foo;</oops>",
server.url());
new Metadata(new ByteArrayInputStream(xml.getBytes()));
} catch (Exception e) {
e.printStackTrace(System.out);
} finally {
server.close();
}
if (server.accepted()) {
fail("Oops! The server has been reached!");
}
}
@Test
public void testExternalEntityParameter() throws IOException {
TestHttpServer server = new TestHttpServer("secret");
try {
String xml = String.format(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<!DOCTYPE oops [\n"
+ " <!ENTITY %% sp SYSTEM \"%s\">\n"
+ "%%sp;"
+ "]>\n"
+ "<oops></oops>",
server.url());
new Metadata(new ByteArrayInputStream(xml.getBytes()));
} catch (Exception e) {
e.printStackTrace(System.out);
} finally {
server.close();
}
if (server.accepted()) {
fail("Oops! The server has been reached!");
}
}
@Test
public void billionLaughs() {
String xml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<!DOCTYPE lolz [\n" +
" <!ENTITY lol \"lol\">\n" +
" <!ELEMENT lolz (#PCDATA)>\n" +
" <!ENTITY lol1 \"&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;\">\n" +
" <!ENTITY lol2 \"&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;\">\n" +
" <!ENTITY lol3 \"&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;\">\n" +
" <!ENTITY lol4 \"&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;\">\n" +
" <!ENTITY lol5 \"&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;\">\n" +
" <!ENTITY lol6 \"&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;\">\n" +
" <!ENTITY lol7 \"&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;\">\n" +
" <!ENTITY lol8 \"&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;\">\n" +
" <!ENTITY lol9 \"&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;\">\n" +
"]>\n" +
"<lolz>&lol9;</lolz>";
new Metadata(new ByteArrayInputStream(xml.getBytes()));
}
@Test
public void testExternalXInclude() throws IOException {
TestHttpServer server = new TestHttpServer("secret");
try {
String xml = String.format(
"<root xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n" +
" <xi:include href=\"%s\" parse=\"text\" />\n" +
"</root>",
server.url());
new Metadata(new ByteArrayInputStream(xml.getBytes()));
} catch (Exception e) {
e.printStackTrace(System.out);
} finally {
server.close();
}
if (server.accepted()) {
fail("Oops! The server has been reached!");
}
}
@Test
public void testExternalSchemaLocation() throws IOException {
TestHttpServer server = new TestHttpServer("secret");
try {
String xml = String.format(
"<ead xmlns=\"urn:isbn:1-931666-22-9\"\n" +
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
" xsi:schemaLocation=\"urn:isbn:1-931666-22-9 %s\">\n" +
"</ead>",
server.url());
new Metadata(new ByteArrayInputStream(xml.getBytes()));
} catch (Exception e) {
e.printStackTrace(System.out);
} finally {
server.close();
}
if (server.accepted()) {
fail("Oops! The server has been reached!");
}
}
}

View File

@ -0,0 +1,79 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.olingo.fit.metadata;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
/**
* Simple HTTP server.
*/
public class TestHttpServer implements HttpHandler, AutoCloseable {
private final String content;
private final HttpServer server;
/**
* This flag indicates if the server accepted a connection.
*/
private boolean accepted = false;
public TestHttpServer(String content) throws IOException {
server = HttpServer.create(new InetSocketAddress(0), 0);
server.createContext("/test", this);
server.setExecutor(null);
server.start();
this.content = content;
}
public String url() {
return String.format("http://localhost:%d/test", server.getAddress().getPort());
}
@Override
public void handle(HttpExchange t) throws IOException {
System.out.println("server: accepted a request");
synchronized (this) {
accepted = true;
}
byte[] response = content.getBytes();
t.sendResponseHeaders(200, response.length);
try (OutputStream os = t.getResponseBody()) {
os.write(response);
}
}
/**
* @return True if the server accepted a connection, false otherwise.
*/
public boolean accepted() {
synchronized (this) {
return accepted;
}
}
@Override
public void close() {
server.stop(0);
}
}

View File

@ -66,6 +66,8 @@ import org.apache.olingo.commons.core.edm.EdmTypeInfo;
import com.fasterxml.aalto.stax.InputFactoryImpl;
import org.apache.olingo.commons.api.ex.ODataErrorDetail;
import static javax.xml.stream.XMLInputFactory.*;
public class AtomDeserializer implements ODataDeserializer {
protected static final QName etagQName = new QName(Constants.NS_METADATA, Constants.ATOM_ATTR_ETAG);
@ -92,12 +94,15 @@ public class AtomDeserializer implements ODataDeserializer {
new QName(Constants.NS_ATOM_TOMBSTONE, Constants.ATOM_ELEM_DELETED_ENTRY);
protected static final XMLInputFactory FACTORY = new InputFactoryImpl();
static {
FACTORY.setProperty(IS_SUPPORTING_EXTERNAL_ENTITIES, false);
FACTORY.setProperty(SUPPORT_DTD, false);
FACTORY.setProperty(IS_REPLACING_ENTITY_REFERENCES, false);
}
private final AtomGeoValueDeserializer geoDeserializer;
protected XMLEventReader getReader(final InputStream input) throws XMLStreamException {
FACTORY.setProperty("javax.xml.stream.isSupportingExternalEntities", false);
FACTORY.setProperty("javax.xml.stream.isReplacingEntityReferences", false);
return FACTORY.createXMLEventReader(input);
}

View File

@ -254,7 +254,7 @@ public class MetadataParser {
private XMLInputFactory createXmlInputFactory() {
XMLInputFactory factory = XMLInputFactory.newInstance();
factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
factory.setProperty("javax.xml.stream.isSupportingExternalEntities", false);
factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
return factory;
}

View File

@ -95,7 +95,7 @@ public class ODataXmlDeserializer implements ODataDeserializer {
protected XMLEventReader getReader(final InputStream input) throws XMLStreamException {
FACTORY.setProperty(XMLInputFactory.SUPPORT_DTD, false);
FACTORY.setProperty("javax.xml.stream.isSupportingExternalEntities", false);
FACTORY.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
return FACTORY.createXMLEventReader(input);
}