You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by mi...@apache.org on 2019/11/18 08:02:56 UTC

[olingo-odata4] branch master updated: [OLINGO-1409] Better XML parsing

This is an automated email from the ASF dual-hosted git repository.

mibo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/olingo-odata4.git


The following commit(s) were added to refs/heads/master by this push:
     new c3f982d  [OLINGO-1409] Better XML parsing
     new 3cf93d5  Merge pull request #59 from artem-smotrakov/better-xml-parsing
c3f982d is described below

commit c3f982db3d97e395d313ae8f231202bb2139882c
Author: Artem Smotrakov <ar...@sap.com>
AuthorDate: Wed Nov 13 10:55:48 2019 +0100

    [OLINGO-1409] Better XML parsing
---
 .../org/apache/olingo/fit/metadata/Metadata.java   |   4 +
 .../olingo/fit/utils/XMLEventReaderWrapper.java    |   4 +
 .../org/apache/olingo/fit/utils/XMLUtilities.java  |   4 +
 .../apache/olingo/fit/metadata/MetadataTest.java   | 168 +++++++++++++++++++++
 .../apache/olingo/fit/metadata/TestHttpServer.java |  79 ++++++++++
 .../core/serialization/AtomDeserializer.java       |   9 +-
 .../apache/olingo/server/core/MetadataParser.java  |   2 +-
 .../deserializer/xml/ODataXmlDeserializer.java     |   2 +-
 8 files changed, 268 insertions(+), 4 deletions(-)

diff --git a/fit/src/main/java/org/apache/olingo/fit/metadata/Metadata.java b/fit/src/main/java/org/apache/olingo/fit/metadata/Metadata.java
index 0d88b1d..940d692 100644
--- a/fit/src/main/java/org/apache/olingo/fit/metadata/Metadata.java
+++ b/fit/src/main/java/org/apache/olingo/fit/metadata/Metadata.java
@@ -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 {
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/XMLEventReaderWrapper.java b/fit/src/main/java/org/apache/olingo/fit/utils/XMLEventReaderWrapper.java
index 14936fc..61803e3 100644
--- a/fit/src/main/java/org/apache/olingo/fit/utils/XMLEventReaderWrapper.java
+++ b/fit/src/main/java/org/apache/olingo/fit/utils/XMLEventReaderWrapper.java
@@ -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
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/XMLUtilities.java b/fit/src/main/java/org/apache/olingo/fit/utils/XMLUtilities.java
index c7930f0..293d631 100644
--- a/fit/src/main/java/org/apache/olingo/fit/utils/XMLUtilities.java
+++ b/fit/src/main/java/org/apache/olingo/fit/utils/XMLUtilities.java
@@ -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));
diff --git a/fit/src/test/java/org/apache/olingo/fit/metadata/MetadataTest.java b/fit/src/test/java/org/apache/olingo/fit/metadata/MetadataTest.java
new file mode 100644
index 0000000..3f4824d
--- /dev/null
+++ b/fit/src/test/java/org/apache/olingo/fit/metadata/MetadataTest.java
@@ -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!");
+        }
+    }
+
+}
diff --git a/fit/src/test/java/org/apache/olingo/fit/metadata/TestHttpServer.java b/fit/src/test/java/org/apache/olingo/fit/metadata/TestHttpServer.java
new file mode 100644
index 0000000..2acbda5
--- /dev/null
+++ b/fit/src/test/java/org/apache/olingo/fit/metadata/TestHttpServer.java
@@ -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);
+    }
+}
\ No newline at end of file
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/AtomDeserializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/AtomDeserializer.java
index a1b25a5..0136a10 100644
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/AtomDeserializer.java
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/AtomDeserializer.java
@@ -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);
   }
 
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/MetadataParser.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/MetadataParser.java
index 3eeaef3..5cfbfd8 100644
--- a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/MetadataParser.java
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/MetadataParser.java
@@ -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;
   }
 
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/xml/ODataXmlDeserializer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/xml/ODataXmlDeserializer.java
index 8356cba..3854875 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/xml/ODataXmlDeserializer.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/xml/ODataXmlDeserializer.java
@@ -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);
   }