You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by fo...@apache.org on 2017/02/24 13:35:15 UTC

camel git commit: CAMEL-10894: DTD handling in the XML Validator corrected

Repository: camel
Updated Branches:
  refs/heads/master 927244de6 -> 8afc5d175


CAMEL-10894:  DTD handling in the XML Validator corrected

Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/8afc5d17
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/8afc5d17
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/8afc5d17

Branch: refs/heads/master
Commit: 8afc5d1757795fde715902067360af5d90f046da
Parents: 927244d
Author: Franz Forsthofer <fr...@sap.com>
Authored: Fri Feb 24 13:57:10 2017 +0100
Committer: Franz Forsthofer <fr...@sap.com>
Committed: Fri Feb 24 14:34:34 2017 +0100

----------------------------------------------------------------------
 .../processor/validation/SchemaReader.java      | 15 +++-
 .../ValidatorDtdAccessAbstractTest.java         | 86 ++++++++++++++++++++
 .../validator/ValidatorDtdAccessOffTest.java    | 61 ++++++++++++++
 .../validator/ValidatorDtdAccessOnTest.java     | 61 ++++++++++++++
 4 files changed, 221 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/8afc5d17/camel-core/src/main/java/org/apache/camel/processor/validation/SchemaReader.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/processor/validation/SchemaReader.java b/camel-core/src/main/java/org/apache/camel/processor/validation/SchemaReader.java
index 68ea309..354acd4 100644
--- a/camel-core/src/main/java/org/apache/camel/processor/validation/SchemaReader.java
+++ b/camel-core/src/main/java/org/apache/camel/processor/validation/SchemaReader.java
@@ -30,7 +30,6 @@ import javax.xml.validation.SchemaFactory;
 
 import org.w3c.dom.ls.LSResourceResolver;
 import org.xml.sax.SAXException;
-
 import org.apache.camel.CamelContext;
 import org.apache.camel.converter.IOConverter;
 import org.apache.camel.util.IOHelper;
@@ -46,8 +45,12 @@ import org.slf4j.LoggerFactory;
  */
 public class SchemaReader {
     
+    /** Key of the global option to switch either off or on  the access to external DTDs in the XML Validator for StreamSources. 
+     * Only effective, if not a custom schema factory is used.*/
+    public static final String ACCESS_EXTERNAL_DTD = "CamelXmlValidatorAccessExternalDTD";
+    
     private static final Logger LOG = LoggerFactory.getLogger(SchemaReader.class);
-
+    
     private String schemaLanguage = XMLConstants.W3C_XML_SCHEMA_NS_URI;
     // must be volatile because is accessed from different threads see ValidatorEndpoint.clearCachedSchema
     private volatile Schema schema;
@@ -169,6 +172,14 @@ public class SchemaReader {
         SchemaFactory factory = SchemaFactory.newInstance(schemaLanguage);
         if (getResourceResolver() != null) {
             factory.setResourceResolver(getResourceResolver());
+        }  
+        if (!Boolean.parseBoolean(camelContext.getGlobalOptions().get(ACCESS_EXTERNAL_DTD))) {
+            try {
+                factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
+            } catch (SAXException e) {
+                LOG.error(e.getMessage(), e);
+                throw new IllegalStateException(e);
+            } 
         }
         return factory;
     }

http://git-wip-us.apache.org/repos/asf/camel/blob/8afc5d17/camel-core/src/test/java/org/apache/camel/component/validator/ValidatorDtdAccessAbstractTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/component/validator/ValidatorDtdAccessAbstractTest.java b/camel-core/src/test/java/org/apache/camel/component/validator/ValidatorDtdAccessAbstractTest.java
new file mode 100644
index 0000000..cc83594
--- /dev/null
+++ b/camel-core/src/test/java/org/apache/camel/component/validator/ValidatorDtdAccessAbstractTest.java
@@ -0,0 +1,86 @@
+/**
+ * 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.camel.component.validator;
+
+import java.net.UnknownHostException;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.ValidationException;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.processor.validation.SchemaReader;
+
+public abstract class ValidatorDtdAccessAbstractTest extends ContextTestSupport {
+    
+    protected MockEndpoint finallyEndpoint;
+    protected MockEndpoint invalidEndpoint;
+    protected MockEndpoint unknownHostExceptionEndpoint;
+    protected MockEndpoint validEndpoint;
+    
+    protected String payloud = getPayloudPart("Hello world!");
+    
+    protected String ssrfPayloud = "<!DOCTYPE roottag PUBLIC \"-//VSR//PENTEST//EN\" \"http://notexisting/test\">\n" + payloud;
+    
+    protected String xxePayloud = "<!DOCTYPE updateProfile [<!ENTITY file SYSTEM \"http://notexistinghost/test\">]>\n" + getPayloudPart("&file;");
+
+    private final boolean accessExternalDTD;
+    
+    public ValidatorDtdAccessAbstractTest(boolean accessExternalDTD) {
+        this.accessExternalDTD = accessExternalDTD;
+    }
+    
+    
+    private String getPayloudPart(String bodyValue) {
+        return "<mail xmlns='http://foo.com/bar'><subject>Hey</subject><body>" + bodyValue + "</body></mail>";
+    }
+    
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        validEndpoint = resolveMandatoryEndpoint("mock:valid", MockEndpoint.class);
+        invalidEndpoint = resolveMandatoryEndpoint("mock:invalid", MockEndpoint.class);
+        unknownHostExceptionEndpoint = resolveMandatoryEndpoint("mock:unknownHostException", MockEndpoint.class);
+        finallyEndpoint = resolveMandatoryEndpoint("mock:finally", MockEndpoint.class);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+
+            @Override
+            public void configure() throws Exception {
+                // switch on DTD Access
+                if (accessExternalDTD) {
+                    getContext().getGlobalOptions().put(SchemaReader.ACCESS_EXTERNAL_DTD, "true");
+                }
+                from("direct:start")
+                    .doTry()
+                        .to("validator:org/apache/camel/component/validator/schema.xsd")
+                        .to("mock:valid")
+                    .doCatch(ValidationException.class)
+                        .to("mock:invalid")
+                    .doCatch(UnknownHostException.class)
+                        .to("mock:unknownHostException")
+                    .doFinally()
+                        .to("mock:finally").end();
+            }
+        };
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/8afc5d17/camel-core/src/test/java/org/apache/camel/component/validator/ValidatorDtdAccessOffTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/component/validator/ValidatorDtdAccessOffTest.java b/camel-core/src/test/java/org/apache/camel/component/validator/ValidatorDtdAccessOffTest.java
new file mode 100644
index 0000000..37f0aee
--- /dev/null
+++ b/camel-core/src/test/java/org/apache/camel/component/validator/ValidatorDtdAccessOffTest.java
@@ -0,0 +1,61 @@
+/**
+ * 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.camel.component.validator;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.camel.component.mock.MockEndpoint;
+
+public class ValidatorDtdAccessOffTest extends ValidatorDtdAccessAbstractTest {
+    
+    public ValidatorDtdAccessOffTest() {
+        super(false);
+    }
+     
+    /** Tests that no external DTD call is executed for StringSource. */
+    public void testInvalidMessageWithExternalDTDStringSource() throws Exception {
+        invalidEndpoint.expectedMessageCount(1);
+        finallyEndpoint.expectedMessageCount(1);
+
+        template.sendBody("direct:start",  ssrfPayloud);
+
+        MockEndpoint.assertIsSatisfied(validEndpoint, unknownHostExceptionEndpoint, finallyEndpoint);
+    }
+
+    /** Tests that external DTD call is not executed  for StreamSource. */
+    public void testInvalidMessageWithExternalDTDStreamSource() throws Exception {
+        invalidEndpoint.expectedMessageCount(1);
+        finallyEndpoint.expectedMessageCount(1);
+        InputStream is = new ByteArrayInputStream(ssrfPayloud.getBytes(StandardCharsets.UTF_8));
+        template.sendBody("direct:start", is);
+
+        MockEndpoint.assertIsSatisfied(validEndpoint, unknownHostExceptionEndpoint, finallyEndpoint);
+    }
+    
+    /** Tests that XXE is not possible for StreamSource. */
+    public void testInvalidMessageXXESourceStream() throws Exception {
+        invalidEndpoint.expectedMessageCount(1);
+        finallyEndpoint.expectedMessageCount(1);
+        InputStream is = new ByteArrayInputStream(xxePayloud.getBytes(StandardCharsets.UTF_8));
+        template.sendBody("direct:start", is);
+
+        MockEndpoint.assertIsSatisfied(validEndpoint, unknownHostExceptionEndpoint, finallyEndpoint);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/8afc5d17/camel-core/src/test/java/org/apache/camel/component/validator/ValidatorDtdAccessOnTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/component/validator/ValidatorDtdAccessOnTest.java b/camel-core/src/test/java/org/apache/camel/component/validator/ValidatorDtdAccessOnTest.java
new file mode 100644
index 0000000..b57303d
--- /dev/null
+++ b/camel-core/src/test/java/org/apache/camel/component/validator/ValidatorDtdAccessOnTest.java
@@ -0,0 +1,61 @@
+/**
+ * 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.camel.component.validator;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.camel.component.mock.MockEndpoint;
+
+public class ValidatorDtdAccessOnTest extends ValidatorDtdAccessAbstractTest {
+    
+    public ValidatorDtdAccessOnTest() {
+        super(true);
+    }
+     
+    /** Tests that external DTD call is executed for StringSource by expecting an UnkonwHostException. */
+    public void testInvalidMessageWithExternalDTDStringSource() throws Exception {
+        unknownHostExceptionEndpoint.expectedMessageCount(1);
+        finallyEndpoint.expectedMessageCount(1);
+
+        template.sendBody("direct:start",  ssrfPayloud);
+
+        MockEndpoint.assertIsSatisfied(validEndpoint, unknownHostExceptionEndpoint, finallyEndpoint);
+    }
+
+    /** Tests that external DTD call is executed  for StreamSourceby expecting an UnkonwHostException. */
+    public void testInvalidMessageWithExternalDTDStreamSource() throws Exception {
+        unknownHostExceptionEndpoint.expectedMessageCount(1);
+        finallyEndpoint.expectedMessageCount(1);
+        InputStream is = new ByteArrayInputStream(ssrfPayloud.getBytes(StandardCharsets.UTF_8));
+        template.sendBody("direct:start", is);
+
+        MockEndpoint.assertIsSatisfied(validEndpoint, unknownHostExceptionEndpoint, finallyEndpoint);
+    }
+    
+    /** Tests that XXE is possible for StreamSource by expecting an UnkonwHostException. */
+    public void testInvalidMessageXXESourceStream() throws Exception {
+        unknownHostExceptionEndpoint.expectedMessageCount(1);
+        finallyEndpoint.expectedMessageCount(1);
+        InputStream is = new ByteArrayInputStream(xxePayloud.getBytes(StandardCharsets.UTF_8));
+        template.sendBody("direct:start", is);
+
+        MockEndpoint.assertIsSatisfied(validEndpoint, unknownHostExceptionEndpoint, finallyEndpoint);
+    }
+
+}