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 <[email protected]> Authored: Fri Feb 24 13:57:10 2017 +0100 Committer: Franz Forsthofer <[email protected]> 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); + } + +}
