diff --git a/camel-core/src/main/java/org/apache/camel/model/dataformat/XStreamDataFormat.java b/camel-core/src/main/java/org/apache/camel/model/dataformat/XStreamDataFormat.java index e1f86db232412..4d433d272c4a6 100644 --- a/camel-core/src/main/java/org/apache/camel/model/dataformat/XStreamDataFormat.java +++ b/camel-core/src/main/java/org/apache/camel/model/dataformat/XStreamDataFormat.java @@ -57,6 +57,8 @@ public class XStreamDataFormat extends DataFormatDefinition { private String driverRef; @XmlAttribute private String mode; + @XmlAttribute + private String permissions; @XmlJavaTypeAdapter(ConvertersAdapter.class) @XmlElement(name = "converters") @@ -180,6 +182,17 @@ public void setImplicitCollections(Map implicitCollections) { this.implicitCollections = implicitCollections; } + public String getPermissions() { + return permissions; + } + + /** + * Adds permissionsList + */ + public void setPermissions(String permissions) { + this.permissions = permissions; + } + @Override protected DataFormat createDataFormat(RouteContext routeContext) { if ("json".equals(this.driver)) { @@ -210,6 +223,9 @@ protected void configureDataFormat(DataFormat dataFormat, CamelContext camelCont if (this.implicitCollections != null) { setProperty(camelContext, dataFormat, "implicitCollections", this.implicitCollections); } + if (this.permissions != null) { + setProperty(camelContext, dataFormat, "permissions", this.permissions); + } if (this.mode != null) { setProperty(camelContext, dataFormat, "mode", mode); } @@ -547,5 +563,4 @@ public String toString() { return "OmitField[" + clsName + ", fields=" + Arrays.asList(this.fields) + "]"; } } - } \ No newline at end of file diff --git a/components/camel-xstream/src/main/java/org/apache/camel/dataformat/xstream/AbstractXStreamWrapper.java b/components/camel-xstream/src/main/java/org/apache/camel/dataformat/xstream/AbstractXStreamWrapper.java index b98725ebb8368..41d6d7a73326d 100644 --- a/components/camel-xstream/src/main/java/org/apache/camel/dataformat/xstream/AbstractXStreamWrapper.java +++ b/components/camel-xstream/src/main/java/org/apache/camel/dataformat/xstream/AbstractXStreamWrapper.java @@ -32,7 +32,10 @@ import com.thoughtworks.xstream.io.HierarchicalStreamDriver; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; - +import com.thoughtworks.xstream.security.AnyTypePermission; +import com.thoughtworks.xstream.security.ExplicitTypePermission; +import com.thoughtworks.xstream.security.TypePermission; +import com.thoughtworks.xstream.security.WildcardTypePermission; import org.apache.camel.CamelContext; import org.apache.camel.Exchange; import org.apache.camel.converter.jaxp.StaxConverter; @@ -47,7 +50,9 @@ * ({@link DataFormat}) interface which leverage the XStream library for XML or JSON's marshaling and unmarshaling */ public abstract class AbstractXStreamWrapper extends ServiceSupport implements DataFormat, DataFormatName { - + private static final String PERMISSIONS_PROPERTY_KEY = "org.apache.camel.xstream.permissions"; + private static final String PERMISSIONS_PROPERTY_DEFAULT = "-*,java.lang.*,java.util.*"; + private XStream xstream; private HierarchicalStreamDriver xstreamDriver; private StaxConverter staxConverter; @@ -55,6 +60,7 @@ public abstract class AbstractXStreamWrapper extends ServiceSupport implements D private Map aliases; private Map omitFields; private Map implicitCollections; + private String permissions; private String mode; public AbstractXStreamWrapper() { @@ -174,6 +180,14 @@ protected XStream createXStream(ClassResolver resolver, ClassLoader classLoader) } } + addDefaultPermissions(xstream); + if (this.permissions != null) { + // permissions ::= pterm (',' pterm)* # consits of one or more terms + // pterm ::= aod? wterm # each term preceded by an optional sign + // aod ::= '+' | '-' # indicates allow or deny where allow if omitted + // wterm ::= a class name with optional wildcard characters + addPermissions(xstream, permissions); + } } catch (Exception e) { throw new RuntimeException("Unable to build XStream instance", e); } @@ -181,6 +195,44 @@ protected XStream createXStream(ClassResolver resolver, ClassLoader classLoader) return xstream; } + private static void addPermissions(XStream xstream, String permissions) { + for (String pterm : permissions.split(",")) { + boolean aod; + pterm = pterm.trim(); + if (pterm.startsWith("-")) { + aod = false; + pterm = pterm.substring(1); + } else { + aod = true; + if (pterm.startsWith("+")) { + pterm = pterm.substring(1); + } + } + TypePermission typePermission = null; + if ("*".equals(pterm)) { + // accept or deny any + typePermission = AnyTypePermission.ANY; + } else if (pterm.indexOf('*') < 0) { + // exact type + typePermission = new ExplicitTypePermission(new String[]{pterm}); + } else if (pterm.length() > 0) { + // wildcard type + typePermission = new WildcardTypePermission(new String[]{pterm}); + } + if (typePermission != null) { + if (aod) { + xstream.addPermission(typePermission); + } else { + xstream.denyPermission(typePermission); + } + } + } + } + + private static void addDefaultPermissions(XStream xstream) { + addPermissions(xstream, System.getProperty(PERMISSIONS_PROPERTY_KEY, PERMISSIONS_PROPERTY_DEFAULT)); + } + protected int getModeFromString(String modeString) { int result; if ("NO_REFERENCES".equalsIgnoreCase(modeString)) { @@ -252,6 +304,14 @@ public void setXstreamDriver(HierarchicalStreamDriver xstreamDriver) { this.xstreamDriver = xstreamDriver; } + public String getPermissions() { + return permissions; + } + + public void setPermissions(String permissions) { + this.permissions = permissions; + } + public String getMode() { return mode; } diff --git a/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/MarshalDomainObjectTest.java b/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/MarshalDomainObjectTest.java index 2992a127b8773..63bc5344a531f 100644 --- a/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/MarshalDomainObjectTest.java +++ b/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/MarshalDomainObjectTest.java @@ -19,6 +19,8 @@ import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.AfterClass; +import org.junit.BeforeClass; import org.junit.Test; /** @@ -26,6 +28,16 @@ */ public class MarshalDomainObjectTest extends CamelTestSupport { + @BeforeClass + public static void setup() { + XStreamTestUtils.setPermissionSystemProperty(""); + } + + @AfterClass + public static void cleanup() { + XStreamTestUtils.revertPermissionSystemProperty(); + } + @Test public void testMarshalDomainObject() throws Exception { MockEndpoint mock = getMockEndpoint("mock:result"); diff --git a/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/UnmarshalThenMarshalTest.java b/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/UnmarshalThenMarshalTest.java index 6e0f5509ba5bc..6e936a50f7645 100644 --- a/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/UnmarshalThenMarshalTest.java +++ b/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/UnmarshalThenMarshalTest.java @@ -23,12 +23,25 @@ import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.AfterClass; +import org.junit.BeforeClass; import org.junit.Test; /** * @version */ public class UnmarshalThenMarshalTest extends CamelTestSupport { + + @BeforeClass + public static void setup() { + XStreamTestUtils.setPermissionSystemProperty(""); + } + + @AfterClass + public static void cleanup() { + XStreamTestUtils.revertPermissionSystemProperty(); + } + @Test public void testSendXmlAndUnmarshal() throws Exception { diff --git a/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/XStreamConcurrencyTest.java b/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/XStreamConcurrencyTest.java index 7a34f40ab512a..b0e6aa229c636 100644 --- a/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/XStreamConcurrencyTest.java +++ b/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/XStreamConcurrencyTest.java @@ -22,6 +22,8 @@ import org.apache.camel.builder.RouteBuilder; import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.AfterClass; +import org.junit.BeforeClass; import org.junit.Test; /** @@ -29,6 +31,16 @@ */ public class XStreamConcurrencyTest extends CamelTestSupport { + @BeforeClass + public static void setup() { + XStreamTestUtils.setPermissionSystemProperty(""); + } + + @AfterClass + public static void cleanup() { + XStreamTestUtils.revertPermissionSystemProperty(); + } + @Test public void testNoConcurrentProducers() throws Exception { doSendMessages(1, 1); diff --git a/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/XStreamConfigurationTest.java b/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/XStreamConfigurationTest.java index 5a9c641bdc0e6..b411abeeeae5b 100644 --- a/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/XStreamConfigurationTest.java +++ b/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/XStreamConfigurationTest.java @@ -33,6 +33,8 @@ import org.apache.camel.component.mock.MockEndpoint; import org.apache.camel.model.dataformat.XStreamDataFormat; import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.AfterClass; +import org.junit.BeforeClass; import org.junit.Test; /** @@ -42,6 +44,16 @@ public class XStreamConfigurationTest extends CamelTestSupport { private static volatile boolean constructorInjected; private static volatile boolean methodInjected; + + @BeforeClass + public static void setup() { + XStreamTestUtils.setPermissionSystemProperty(""); + } + + @AfterClass + public static void cleanup() { + XStreamTestUtils.revertPermissionSystemProperty(); + } @Override public void setUp() throws Exception { diff --git a/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/XStreamDataFormatDriverConfigTest.java b/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/XStreamDataFormatDriverConfigTest.java index c3e44fe2d13ce..207c26d352bd9 100644 --- a/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/XStreamDataFormatDriverConfigTest.java +++ b/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/XStreamDataFormatDriverConfigTest.java @@ -18,7 +18,6 @@ import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.io.json.JsonHierarchicalStreamDriver; -import org.apache.camel.impl.DefaultClassResolver; import org.apache.camel.test.junit4.CamelTestSupport; import org.junit.Test; diff --git a/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/XStreamDataFormatPermissionsSystemPropertyTest.java b/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/XStreamDataFormatPermissionsSystemPropertyTest.java new file mode 100644 index 0000000000000..8da946d6f17f8 --- /dev/null +++ b/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/XStreamDataFormatPermissionsSystemPropertyTest.java @@ -0,0 +1,47 @@ +/** + * 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.dataformat.xstream; + +import com.thoughtworks.xstream.XStream; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +public class XStreamDataFormatPermissionsSystemPropertyTest extends XStreamDataFormatPermissionsTest { + + @BeforeClass + public static void setup() { + // clear the default permissions system property + // see AbstractXStreamWrapper.PERMISSIONS_PROPERTY_DEFAULT + XStreamTestUtils.setPermissionSystemProperty(""); + } + + @AfterClass + public static void cleanup() { + XStreamTestUtils.revertPermissionSystemProperty(); + } + + @Test + @Override + public void testNone() { + XStreamDataFormat xStreamDataFormat = new XStreamDataFormat(); + XStream xStream = xStreamDataFormat.createXStream(context.getClassResolver(), context.getApplicationContextClassLoader()); + + Object po = xStream.fromXML(XML_PURCHASE_ORDER); + assertNotNull(po); + } +} diff --git a/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/XStreamDataFormatPermissionsTest.java b/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/XStreamDataFormatPermissionsTest.java new file mode 100644 index 0000000000000..3213624b67c5f --- /dev/null +++ b/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/XStreamDataFormatPermissionsTest.java @@ -0,0 +1,178 @@ +/** + * 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.dataformat.xstream; + +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.security.ForbiddenClassException; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; + +public class XStreamDataFormatPermissionsTest extends CamelTestSupport { + protected static final String XML_PURCHASE_ORDER = + "" + + "foo" + + "10.0" + + "1.0" + + ""; + protected static final String XML_PURCHASE_ORDERS_LIST = + "" + + "" + + "foo" + + "10.0" + + "1.0" + + "" + + "" + + "bar" + + "9.0" + + "2.0" + + "" + + ""; + + @Test + public void testNone() { + XStreamDataFormat xStreamDataFormat = new XStreamDataFormat(); + XStream xStream = xStreamDataFormat.createXStream(context.getClassResolver(), context.getApplicationContextClassLoader()); + + try { + xStream.fromXML(XML_PURCHASE_ORDER); + fail("should fail to unmarshall"); + } catch (ForbiddenClassException e) { + // OK + } + } + + + @Test + public void testDeny() { + XStreamDataFormat xStreamDataFormat = new XStreamDataFormat(); + xStreamDataFormat.setPermissions("-org.apache.camel.dataformat.xstream.PurchaseOrder"); + + XStream xStream = xStreamDataFormat.createXStream(context.getClassResolver(), context.getApplicationContextClassLoader()); + + try { + xStream.fromXML(XML_PURCHASE_ORDER); + fail("should fail to unmarshall"); + } catch (ForbiddenClassException e) { + // OK + } + } + + @Test + public void testAllow() { + XStreamDataFormat xStreamDataFormat = new XStreamDataFormat(); + xStreamDataFormat.setPermissions("org.apache.camel.dataformat.xstream.PurchaseOrder"); + + XStream xStream = xStreamDataFormat.createXStream(context.getClassResolver(), context.getApplicationContextClassLoader()); + + Object po = xStream.fromXML(XML_PURCHASE_ORDER); + assertNotNull(po); + + po = xStream.fromXML(XML_PURCHASE_ORDERS_LIST); + assertNotNull(po); + } + + @Test + public void testAllowAndDeny() { + XStreamDataFormat xStreamDataFormat = new XStreamDataFormat(); + xStreamDataFormat.setPermissions("org.apache.camel.dataformat.xstream.PurchaseOrder,-org.apache.camel.dataformat.xstream.*"); + + XStream xStream = xStreamDataFormat.createXStream(context.getClassResolver(), context.getApplicationContextClassLoader()); + + try { + xStream.fromXML(XML_PURCHASE_ORDER); + fail("should fail to unmarshall"); + } catch (ForbiddenClassException e) { + // OK + } + } + + @Test + public void testDenyAndAllowDeny() { + XStreamDataFormat xStreamDataFormat = new XStreamDataFormat(); + xStreamDataFormat.setPermissions("-org.apache.camel.dataformat.xstream.*,org.apache.camel.dataformat.xstream.PurchaseOrder"); + + XStream xStream = xStreamDataFormat.createXStream(context.getClassResolver(), context.getApplicationContextClassLoader()); + + Object po = xStream.fromXML(XML_PURCHASE_ORDER); + assertNotNull(po); + + po = xStream.fromXML(XML_PURCHASE_ORDERS_LIST); + assertNotNull(po); + } + + @Test + public void testAllowAny() { + XStreamDataFormat xStreamDataFormat = new XStreamDataFormat(); + xStreamDataFormat.setPermissions("*"); + + XStream xStream = xStreamDataFormat.createXStream(context.getClassResolver(), context.getApplicationContextClassLoader()); + + Object po = xStream.fromXML(XML_PURCHASE_ORDER); + assertNotNull(po); + + po = xStream.fromXML(XML_PURCHASE_ORDERS_LIST); + assertNotNull(po); + } + + @Test + public void testAllowAnyAndDeny() { + XStreamDataFormat xStreamDataFormat = new XStreamDataFormat(); + xStreamDataFormat.setPermissions("*,-org.apache.camel.dataformat.xstream.PurchaseOrder"); + + XStream xStream = xStreamDataFormat.createXStream(context.getClassResolver(), context.getApplicationContextClassLoader()); + + try { + xStream.fromXML(XML_PURCHASE_ORDER); + fail("should fail to unmarshall"); + } catch (ForbiddenClassException e) { + // OK + } + } + + @Test + public void testDenyAny() { + XStreamDataFormat xStreamDataFormat = new XStreamDataFormat(); + xStreamDataFormat.setPermissions("-*"); + + XStream xStream = xStreamDataFormat.createXStream(context.getClassResolver(), context.getApplicationContextClassLoader()); + + try { + xStream.fromXML(XML_PURCHASE_ORDER); + fail("should fail to unmarshall"); + } catch (ForbiddenClassException e) { + // OK + } + } + + @Test + public void testDenyAnyAndAllow() { + XStreamDataFormat xStreamDataFormat = new XStreamDataFormat(); + xStreamDataFormat.setPermissions("-*,org.apache.camel.dataformat.xstream.PurchaseOrder"); + + XStream xStream = xStreamDataFormat.createXStream(context.getClassResolver(), context.getApplicationContextClassLoader()); + + Object po = xStream.fromXML(XML_PURCHASE_ORDER); + assertNotNull(po); + + try { + xStream.fromXML(XML_PURCHASE_ORDERS_LIST); + fail("should fail to unmarshall"); + } catch (ForbiddenClassException e) { + // OK + } + } +} diff --git a/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/XStreamTestUtils.java b/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/XStreamTestUtils.java new file mode 100644 index 0000000000000..ff6cf35e57052 --- /dev/null +++ b/components/camel-xstream/src/test/java/org/apache/camel/dataformat/xstream/XStreamTestUtils.java @@ -0,0 +1,46 @@ +/** + * 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.dataformat.xstream; + +/** + * + */ +final class XStreamTestUtils { + static final String PERMISSIONS_PROPERTY_KEY = "org.apache.camel.xstream.permissions"; + private static String oldProperty; + + private XStreamTestUtils() { + } + + public static void setPermissionSystemProperty(String value) { + oldProperty = System.getProperty(PERMISSIONS_PROPERTY_KEY); + if (value == null) { + System.clearProperty(PERMISSIONS_PROPERTY_KEY); + } else { + System.setProperty(PERMISSIONS_PROPERTY_KEY, value); + } + } + + public static void revertPermissionSystemProperty() { + if (oldProperty == null) { + System.clearProperty(PERMISSIONS_PROPERTY_KEY); + } else { + System.setProperty(PERMISSIONS_PROPERTY_KEY, oldProperty); + } + } +} diff --git a/components/camel-xstream/src/test/resources/org/apache/camel/dataformat/xstream/SpringXStreamConfigurationTest.xml b/components/camel-xstream/src/test/resources/org/apache/camel/dataformat/xstream/SpringXStreamConfigurationTest.xml index a1710869b3a53..97a5e80f9cff9 100644 --- a/components/camel-xstream/src/test/resources/org/apache/camel/dataformat/xstream/SpringXStreamConfigurationTest.xml +++ b/components/camel-xstream/src/test/resources/org/apache/camel/dataformat/xstream/SpringXStreamConfigurationTest.xml @@ -29,7 +29,8 @@ - + diff --git a/parent/pom.xml b/parent/pom.xml index 9020b15aa2b18..a3e1933bd9587 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -576,7 +576,7 @@ org.apache.velocity.*;version="[1.6.2,2)", org.apache.xmlbeans.*;version="[2.4,3)", org.eclipse.jetty.*;version="[8.0,10)", - com.thoughtworks.xstream.*;version="[1.3,2)", + com.thoughtworks.xstream.*;version="[1.4.7,2)", org.antlr.stringtemplate.*;version="[3.0,4)", org.ccil.cowan.tagsoup.*;version="[1.2,2)", org.mortbay.cometd.*;version="[6.1,7)",