From 22442b2385b1000312aec3d19e510131d595a5fc Mon Sep 17 00:00:00 2001 From: "Christopher L. Shannon (cshannon)" Date: Mon, 23 Oct 2023 18:24:38 -0400 Subject: [PATCH] AMQ-9370 - Openwire marshaller should validate Throwable class type (cherry picked from commit 3eaf3107f4fb9a3ce7ab45c175bfaeac7e866d5b) (cherry picked from commit d0ccdd31544ada83185554c87c7aa141064020f0) --- activemq-client/pom.xml | 11 ++ .../activemq/openwire/OpenWireUtil.java | 32 ++++ .../openwire/v1/BaseDataStreamMarshaller.java | 4 + .../v10/BaseDataStreamMarshaller.java | 4 + .../v11/BaseDataStreamMarshaller.java | 4 + .../v12/BaseDataStreamMarshaller.java | 4 + .../openwire/v9/BaseDataStreamMarshaller.java | 4 + .../openwire/OpenWireValidationTest.java | 166 ++++++++++++++++++ activemq-openwire-legacy/pom.xml | 12 ++ .../openwire/v2/BaseDataStreamMarshaller.java | 4 + .../openwire/v3/BaseDataStreamMarshaller.java | 4 + .../openwire/v4/BaseDataStreamMarshaller.java | 4 + .../openwire/v5/BaseDataStreamMarshaller.java | 4 + .../openwire/v6/BaseDataStreamMarshaller.java | 4 + .../openwire/v7/BaseDataStreamMarshaller.java | 4 + .../openwire/v8/BaseDataStreamMarshaller.java | 4 + .../OpenWireLegacyValidationTest.java | 129 ++++++++++++++ pom.xml | 7 + 18 files changed, 405 insertions(+) create mode 100644 activemq-client/src/main/java/org/apache/activemq/openwire/OpenWireUtil.java create mode 100644 activemq-client/src/test/java/org/apache/activemq/openwire/OpenWireValidationTest.java create mode 100644 activemq-openwire-legacy/src/test/java/org/apache/activemq/openwire/OpenWireLegacyValidationTest.java diff --git a/activemq-client/pom.xml b/activemq-client/pom.xml index ba5de326a48..52e1ac1e0c1 100644 --- a/activemq-client/pom.xml +++ b/activemq-client/pom.xml @@ -268,6 +268,17 @@ + + + maven-jar-plugin + + + + test-jar + + + + diff --git a/activemq-client/src/main/java/org/apache/activemq/openwire/OpenWireUtil.java b/activemq-client/src/main/java/org/apache/activemq/openwire/OpenWireUtil.java new file mode 100644 index 00000000000..f52e6c3e084 --- /dev/null +++ b/activemq-client/src/main/java/org/apache/activemq/openwire/OpenWireUtil.java @@ -0,0 +1,32 @@ +/** + * 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.activemq.openwire; + +public class OpenWireUtil { + + /** + * Verify that the provided class extends {@link Throwable} and throw an + * {@link IllegalArgumentException} if it does not. + * + * @param clazz + */ + public static void validateIsThrowable(Class clazz) { + if (!Throwable.class.isAssignableFrom(clazz)) { + throw new IllegalArgumentException("Class " + clazz + " is not assignable to Throwable"); + } + } +} diff --git a/activemq-client/src/main/java/org/apache/activemq/openwire/v1/BaseDataStreamMarshaller.java b/activemq-client/src/main/java/org/apache/activemq/openwire/v1/BaseDataStreamMarshaller.java index b2c997ea736..cfe2716a9d6 100644 --- a/activemq-client/src/main/java/org/apache/activemq/openwire/v1/BaseDataStreamMarshaller.java +++ b/activemq-client/src/main/java/org/apache/activemq/openwire/v1/BaseDataStreamMarshaller.java @@ -25,6 +25,7 @@ import org.apache.activemq.openwire.BooleanStream; import org.apache.activemq.openwire.DataStreamMarshaller; import org.apache.activemq.openwire.OpenWireFormat; +import org.apache.activemq.openwire.OpenWireUtil; import org.apache.activemq.util.ByteSequence; public abstract class BaseDataStreamMarshaller implements DataStreamMarshaller { @@ -229,8 +230,11 @@ protected Throwable tightUnmarsalThrowable(OpenWireFormat wireFormat, DataInput private Throwable createThrowable(String className, String message) { try { Class clazz = Class.forName(className, false, BaseDataStreamMarshaller.class.getClassLoader()); + OpenWireUtil.validateIsThrowable(clazz); Constructor constructor = clazz.getConstructor(new Class[] {String.class}); return (Throwable)constructor.newInstance(new Object[] {message}); + } catch (IllegalArgumentException e) { + return e; } catch (Throwable e) { return new Throwable(className + ": " + message); } diff --git a/activemq-client/src/main/java/org/apache/activemq/openwire/v10/BaseDataStreamMarshaller.java b/activemq-client/src/main/java/org/apache/activemq/openwire/v10/BaseDataStreamMarshaller.java index a570d3d17d0..6dc87a29a53 100644 --- a/activemq-client/src/main/java/org/apache/activemq/openwire/v10/BaseDataStreamMarshaller.java +++ b/activemq-client/src/main/java/org/apache/activemq/openwire/v10/BaseDataStreamMarshaller.java @@ -24,6 +24,7 @@ import org.apache.activemq.openwire.BooleanStream; import org.apache.activemq.openwire.DataStreamMarshaller; import org.apache.activemq.openwire.OpenWireFormat; +import org.apache.activemq.openwire.OpenWireUtil; import org.apache.activemq.util.ByteSequence; public abstract class BaseDataStreamMarshaller implements DataStreamMarshaller { @@ -228,8 +229,11 @@ protected Throwable tightUnmarsalThrowable(OpenWireFormat wireFormat, DataInput private Throwable createThrowable(String className, String message) { try { Class clazz = Class.forName(className, false, BaseDataStreamMarshaller.class.getClassLoader()); + OpenWireUtil.validateIsThrowable(clazz); Constructor constructor = clazz.getConstructor(new Class[] {String.class}); return (Throwable)constructor.newInstance(new Object[] {message}); + } catch (IllegalArgumentException e) { + return e; } catch (Throwable e) { return new Throwable(className + ": " + message); } diff --git a/activemq-client/src/main/java/org/apache/activemq/openwire/v11/BaseDataStreamMarshaller.java b/activemq-client/src/main/java/org/apache/activemq/openwire/v11/BaseDataStreamMarshaller.java index c61d16fa32b..4bcf109445d 100644 --- a/activemq-client/src/main/java/org/apache/activemq/openwire/v11/BaseDataStreamMarshaller.java +++ b/activemq-client/src/main/java/org/apache/activemq/openwire/v11/BaseDataStreamMarshaller.java @@ -24,6 +24,7 @@ import org.apache.activemq.openwire.BooleanStream; import org.apache.activemq.openwire.DataStreamMarshaller; import org.apache.activemq.openwire.OpenWireFormat; +import org.apache.activemq.openwire.OpenWireUtil; import org.apache.activemq.util.ByteSequence; public abstract class BaseDataStreamMarshaller implements DataStreamMarshaller { @@ -227,8 +228,11 @@ protected Throwable tightUnmarsalThrowable(OpenWireFormat wireFormat, DataInput private Throwable createThrowable(String className, String message) { try { Class clazz = Class.forName(className, false, BaseDataStreamMarshaller.class.getClassLoader()); + OpenWireUtil.validateIsThrowable(clazz); Constructor constructor = clazz.getConstructor(new Class[] {String.class}); return (Throwable)constructor.newInstance(new Object[] {message}); + } catch (IllegalArgumentException e) { + return e; } catch (Throwable e) { return new Throwable(className + ": " + message); } diff --git a/activemq-client/src/main/java/org/apache/activemq/openwire/v12/BaseDataStreamMarshaller.java b/activemq-client/src/main/java/org/apache/activemq/openwire/v12/BaseDataStreamMarshaller.java index 820d9a632c8..10fdebcbcd5 100644 --- a/activemq-client/src/main/java/org/apache/activemq/openwire/v12/BaseDataStreamMarshaller.java +++ b/activemq-client/src/main/java/org/apache/activemq/openwire/v12/BaseDataStreamMarshaller.java @@ -24,6 +24,7 @@ import org.apache.activemq.openwire.BooleanStream; import org.apache.activemq.openwire.DataStreamMarshaller; import org.apache.activemq.openwire.OpenWireFormat; +import org.apache.activemq.openwire.OpenWireUtil; import org.apache.activemq.util.ByteSequence; public abstract class BaseDataStreamMarshaller implements DataStreamMarshaller { @@ -227,8 +228,11 @@ protected Throwable tightUnmarsalThrowable(OpenWireFormat wireFormat, DataInput private Throwable createThrowable(String className, String message) { try { Class clazz = Class.forName(className, false, BaseDataStreamMarshaller.class.getClassLoader()); + OpenWireUtil.validateIsThrowable(clazz); Constructor constructor = clazz.getConstructor(new Class[] {String.class}); return (Throwable)constructor.newInstance(new Object[] {message}); + } catch (IllegalArgumentException e) { + return e; } catch (Throwable e) { return new Throwable(className + ": " + message); } diff --git a/activemq-client/src/main/java/org/apache/activemq/openwire/v9/BaseDataStreamMarshaller.java b/activemq-client/src/main/java/org/apache/activemq/openwire/v9/BaseDataStreamMarshaller.java index 80abd32dc3c..1f99292d5b6 100644 --- a/activemq-client/src/main/java/org/apache/activemq/openwire/v9/BaseDataStreamMarshaller.java +++ b/activemq-client/src/main/java/org/apache/activemq/openwire/v9/BaseDataStreamMarshaller.java @@ -24,6 +24,7 @@ import org.apache.activemq.openwire.BooleanStream; import org.apache.activemq.openwire.DataStreamMarshaller; import org.apache.activemq.openwire.OpenWireFormat; +import org.apache.activemq.openwire.OpenWireUtil; import org.apache.activemq.util.ByteSequence; public abstract class BaseDataStreamMarshaller implements DataStreamMarshaller { @@ -227,8 +228,11 @@ protected Throwable tightUnmarsalThrowable(OpenWireFormat wireFormat, DataInput private Throwable createThrowable(String className, String message) { try { Class clazz = Class.forName(className, false, BaseDataStreamMarshaller.class.getClassLoader()); + OpenWireUtil.validateIsThrowable(clazz); Constructor constructor = clazz.getConstructor(new Class[] {String.class}); return (Throwable)constructor.newInstance(new Object[] {message}); + } catch (IllegalArgumentException e) { + return e; } catch (Throwable e) { return new Throwable(className + ": " + message); } diff --git a/activemq-client/src/test/java/org/apache/activemq/openwire/OpenWireValidationTest.java b/activemq-client/src/test/java/org/apache/activemq/openwire/OpenWireValidationTest.java new file mode 100644 index 00000000000..a7a6a4f7ce9 --- /dev/null +++ b/activemq-client/src/test/java/org/apache/activemq/openwire/OpenWireValidationTest.java @@ -0,0 +1,166 @@ +/** + * 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.activemq.openwire; + +import static org.junit.Assert.assertTrue; + +import java.io.DataOutput; +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import org.apache.activemq.command.CommandTypes; +import org.apache.activemq.command.ExceptionResponse; +import org.apache.activemq.util.ByteSequence; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +/** + * Test that Openwire marshalling will validate Throwable types during + * unmarshalling commands that contain a Throwable + */ +@RunWith(Parameterized.class) +public class OpenWireValidationTest { + + protected final int version; + + @Parameters(name = "version={0}") + public static Collection data() { + List versions = List.of(1, 9, 10, 11, 12); + List versionObjs = new ArrayList<>(); + for (int i : versions) { + versionObjs.add(new Object[]{i}); + } + + // Sanity check to make sure the latest generated version is contained in the list + // This will make sure that we don't forget to update this test to include + // any future versions that are generated + assertTrue("List of Openwire versions does not include latest version", + versions.contains((int)CommandTypes.PROTOCOL_VERSION)); + + return versionObjs; + } + + public OpenWireValidationTest(int version) { + this.version = version; + } + + @Test + public void testOpenwireThrowableValidation() throws Exception { + // Create a format which will use loose encoding by default + // The code for handling exception creation is shared between both + // tight/loose encoding so only need to test 1 + OpenWireFormat format = new OpenWireFormat(); + + // Override the marshaller map with a custom impl to purposely marshal a class type that is + // not a Throwable for testing the unmarshaller + Class marshallerFactory = getMarshallerFactory(); + Method createMarshallerMap = marshallerFactory.getMethod("createMarshallerMap", OpenWireFormat.class); + DataStreamMarshaller[] map = (DataStreamMarshaller[]) createMarshallerMap.invoke(marshallerFactory, format); + map[ExceptionResponse.DATA_STRUCTURE_TYPE] = getExceptionMarshaller(); + // This will trigger updating the marshaller from the marshaller map with the right version + format.setVersion(version); + + // Build the response and try to unmarshal which should give an IllegalArgumentExeption on unmarshall + // as the test marshaller should have encoded a class type that is not a Throwable + ExceptionResponse r = new ExceptionResponse(); + r.setException(new Exception()); + ByteSequence bss = format.marshal(r); + ExceptionResponse response = (ExceptionResponse) format.unmarshal(bss); + + assertTrue(response.getException() instanceof IllegalArgumentException); + assertTrue(response.getException().getMessage().contains("is not assignable to Throwable")); + } + + static class NotAThrowable { + private String message; + + public NotAThrowable(String message) { + this.message = message; + } + + public NotAThrowable() { + } + } + + private Class getMarshallerFactory() throws ClassNotFoundException { + return Class.forName("org.apache.activemq.openwire.v" + version + ".MarshallerFactory"); + } + + // Create test marshallers for all non-legacy versions that will encode NotAThrowable + // instead of the exception type for testing purposes + protected DataStreamMarshaller getExceptionMarshaller() { + switch (version) { + case 12: + return new org.apache.activemq.openwire.v12.ExceptionResponseMarshaller() { + @Override + protected void looseMarshalThrowable(OpenWireFormat wireFormat, Throwable o, + DataOutput dataOut) throws IOException { + dataOut.writeBoolean(o != null); + looseMarshalString(NotAThrowable.class.getName(), dataOut); + looseMarshalString(o.getMessage(), dataOut); + } + }; + case 11: + return new org.apache.activemq.openwire.v11.ExceptionResponseMarshaller() { + @Override + protected void looseMarshalThrowable(OpenWireFormat wireFormat, Throwable o, + DataOutput dataOut) throws IOException { + dataOut.writeBoolean(o != null); + looseMarshalString(NotAThrowable.class.getName(), dataOut); + looseMarshalString(o.getMessage(), dataOut); + } + }; + case 10: + return new org.apache.activemq.openwire.v10.ExceptionResponseMarshaller() { + @Override + protected void looseMarshalThrowable(OpenWireFormat wireFormat, Throwable o, + DataOutput dataOut) throws IOException { + dataOut.writeBoolean(o != null); + looseMarshalString(NotAThrowable.class.getName(), dataOut); + looseMarshalString(o.getMessage(), dataOut); + } + }; + case 9: + return new org.apache.activemq.openwire.v9.ExceptionResponseMarshaller() { + @Override + protected void looseMarshalThrowable(OpenWireFormat wireFormat, Throwable o, + DataOutput dataOut) throws IOException { + dataOut.writeBoolean(o != null); + looseMarshalString(NotAThrowable.class.getName(), dataOut); + looseMarshalString(o.getMessage(), dataOut); + } + }; + case 1: + return new org.apache.activemq.openwire.v1.ExceptionResponseMarshaller() { + @Override + protected void looseMarshalThrowable(OpenWireFormat wireFormat, Throwable o, + DataOutput dataOut) throws IOException { + dataOut.writeBoolean(o != null); + looseMarshalString(NotAThrowable.class.getName(), dataOut); + looseMarshalString(o.getMessage(), dataOut); + } + }; + default: + throw new IllegalArgumentException("Unknown openwire version of " + version); + } + } + +} diff --git a/activemq-openwire-legacy/pom.xml b/activemq-openwire-legacy/pom.xml index 77f40c8a456..6d10ca22cee 100644 --- a/activemq-openwire-legacy/pom.xml +++ b/activemq-openwire-legacy/pom.xml @@ -35,6 +35,18 @@ org.apache.activemq activemq-client + + + org.apache.activemq + activemq-client + test-jar + test + + + junit + junit + test + diff --git a/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v2/BaseDataStreamMarshaller.java b/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v2/BaseDataStreamMarshaller.java index b05a9912fe6..ed8c9044577 100644 --- a/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v2/BaseDataStreamMarshaller.java +++ b/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v2/BaseDataStreamMarshaller.java @@ -25,6 +25,7 @@ import org.apache.activemq.openwire.BooleanStream; import org.apache.activemq.openwire.DataStreamMarshaller; import org.apache.activemq.openwire.OpenWireFormat; +import org.apache.activemq.openwire.OpenWireUtil; import org.apache.activemq.util.ByteSequence; public abstract class BaseDataStreamMarshaller implements DataStreamMarshaller { @@ -228,8 +229,11 @@ protected Throwable tightUnmarsalThrowable(OpenWireFormat wireFormat, DataInput private Throwable createThrowable(String className, String message) { try { Class clazz = Class.forName(className, false, BaseDataStreamMarshaller.class.getClassLoader()); + OpenWireUtil.validateIsThrowable(clazz); Constructor constructor = clazz.getConstructor(new Class[] {String.class}); return (Throwable)constructor.newInstance(new Object[] {message}); + } catch (IllegalArgumentException e) { + return e; } catch (Throwable e) { return new Throwable(className + ": " + message); } diff --git a/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v3/BaseDataStreamMarshaller.java b/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v3/BaseDataStreamMarshaller.java index 872f8eaf07e..4199157a115 100644 --- a/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v3/BaseDataStreamMarshaller.java +++ b/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v3/BaseDataStreamMarshaller.java @@ -25,6 +25,7 @@ import org.apache.activemq.openwire.BooleanStream; import org.apache.activemq.openwire.DataStreamMarshaller; import org.apache.activemq.openwire.OpenWireFormat; +import org.apache.activemq.openwire.OpenWireUtil; import org.apache.activemq.util.ByteSequence; public abstract class BaseDataStreamMarshaller implements DataStreamMarshaller { @@ -228,8 +229,11 @@ protected Throwable tightUnmarsalThrowable(OpenWireFormat wireFormat, DataInput private Throwable createThrowable(String className, String message) { try { Class clazz = Class.forName(className, false, BaseDataStreamMarshaller.class.getClassLoader()); + OpenWireUtil.validateIsThrowable(clazz); Constructor constructor = clazz.getConstructor(new Class[] {String.class}); return (Throwable)constructor.newInstance(new Object[] {message}); + } catch (IllegalArgumentException e) { + return e; } catch (Throwable e) { return new Throwable(className + ": " + message); } diff --git a/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v4/BaseDataStreamMarshaller.java b/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v4/BaseDataStreamMarshaller.java index 798b94a2442..2c9720512c1 100644 --- a/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v4/BaseDataStreamMarshaller.java +++ b/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v4/BaseDataStreamMarshaller.java @@ -25,6 +25,7 @@ import org.apache.activemq.openwire.BooleanStream; import org.apache.activemq.openwire.DataStreamMarshaller; import org.apache.activemq.openwire.OpenWireFormat; +import org.apache.activemq.openwire.OpenWireUtil; import org.apache.activemq.util.ByteSequence; public abstract class BaseDataStreamMarshaller implements DataStreamMarshaller { @@ -228,8 +229,11 @@ protected Throwable tightUnmarsalThrowable(OpenWireFormat wireFormat, DataInput private Throwable createThrowable(String className, String message) { try { Class clazz = Class.forName(className, false, BaseDataStreamMarshaller.class.getClassLoader()); + OpenWireUtil.validateIsThrowable(clazz); Constructor constructor = clazz.getConstructor(new Class[] {String.class}); return (Throwable)constructor.newInstance(new Object[] {message}); + } catch (IllegalArgumentException e) { + return e; } catch (Throwable e) { return new Throwable(className + ": " + message); } diff --git a/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v5/BaseDataStreamMarshaller.java b/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v5/BaseDataStreamMarshaller.java index 180d9c759f9..264f260a7d5 100644 --- a/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v5/BaseDataStreamMarshaller.java +++ b/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v5/BaseDataStreamMarshaller.java @@ -25,6 +25,7 @@ import org.apache.activemq.openwire.BooleanStream; import org.apache.activemq.openwire.DataStreamMarshaller; import org.apache.activemq.openwire.OpenWireFormat; +import org.apache.activemq.openwire.OpenWireUtil; import org.apache.activemq.util.ByteSequence; public abstract class BaseDataStreamMarshaller implements DataStreamMarshaller { @@ -228,8 +229,11 @@ protected Throwable tightUnmarsalThrowable(OpenWireFormat wireFormat, DataInput private Throwable createThrowable(String className, String message) { try { Class clazz = Class.forName(className, false, BaseDataStreamMarshaller.class.getClassLoader()); + OpenWireUtil.validateIsThrowable(clazz); Constructor constructor = clazz.getConstructor(new Class[] {String.class}); return (Throwable)constructor.newInstance(new Object[] {message}); + } catch (IllegalArgumentException e) { + return e; } catch (Throwable e) { return new Throwable(className + ": " + message); } diff --git a/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v6/BaseDataStreamMarshaller.java b/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v6/BaseDataStreamMarshaller.java index a0d066a4af3..fee3c0e4b53 100644 --- a/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v6/BaseDataStreamMarshaller.java +++ b/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v6/BaseDataStreamMarshaller.java @@ -25,6 +25,7 @@ import org.apache.activemq.openwire.BooleanStream; import org.apache.activemq.openwire.DataStreamMarshaller; import org.apache.activemq.openwire.OpenWireFormat; +import org.apache.activemq.openwire.OpenWireUtil; import org.apache.activemq.util.ByteSequence; public abstract class BaseDataStreamMarshaller implements DataStreamMarshaller { @@ -228,8 +229,11 @@ protected Throwable tightUnmarsalThrowable(OpenWireFormat wireFormat, DataInput private Throwable createThrowable(String className, String message) { try { Class clazz = Class.forName(className, false, BaseDataStreamMarshaller.class.getClassLoader()); + OpenWireUtil.validateIsThrowable(clazz); Constructor constructor = clazz.getConstructor(new Class[] {String.class}); return (Throwable)constructor.newInstance(new Object[] {message}); + } catch (IllegalArgumentException e) { + return e; } catch (Throwable e) { return new Throwable(className + ": " + message); } diff --git a/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v7/BaseDataStreamMarshaller.java b/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v7/BaseDataStreamMarshaller.java index 99dae64209d..db8aeb73cc2 100644 --- a/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v7/BaseDataStreamMarshaller.java +++ b/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v7/BaseDataStreamMarshaller.java @@ -24,6 +24,7 @@ import org.apache.activemq.openwire.BooleanStream; import org.apache.activemq.openwire.DataStreamMarshaller; import org.apache.activemq.openwire.OpenWireFormat; +import org.apache.activemq.openwire.OpenWireUtil; import org.apache.activemq.util.ByteSequence; public abstract class BaseDataStreamMarshaller implements DataStreamMarshaller { @@ -227,8 +228,11 @@ protected Throwable tightUnmarsalThrowable(OpenWireFormat wireFormat, DataInput private Throwable createThrowable(String className, String message) { try { Class clazz = Class.forName(className, false, BaseDataStreamMarshaller.class.getClassLoader()); + OpenWireUtil.validateIsThrowable(clazz); Constructor constructor = clazz.getConstructor(new Class[] {String.class}); return (Throwable)constructor.newInstance(new Object[] {message}); + } catch (IllegalArgumentException e) { + return e; } catch (Throwable e) { return new Throwable(className + ": " + message); } diff --git a/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v8/BaseDataStreamMarshaller.java b/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v8/BaseDataStreamMarshaller.java index 62c84731658..47a0165ae52 100644 --- a/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v8/BaseDataStreamMarshaller.java +++ b/activemq-openwire-legacy/src/main/java/org/apache/activemq/openwire/v8/BaseDataStreamMarshaller.java @@ -24,6 +24,7 @@ import org.apache.activemq.openwire.BooleanStream; import org.apache.activemq.openwire.DataStreamMarshaller; import org.apache.activemq.openwire.OpenWireFormat; +import org.apache.activemq.openwire.OpenWireUtil; import org.apache.activemq.util.ByteSequence; public abstract class BaseDataStreamMarshaller implements DataStreamMarshaller { @@ -227,8 +228,11 @@ protected Throwable tightUnmarsalThrowable(OpenWireFormat wireFormat, DataInput private Throwable createThrowable(String className, String message) { try { Class clazz = Class.forName(className, false, BaseDataStreamMarshaller.class.getClassLoader()); + OpenWireUtil.validateIsThrowable(clazz); Constructor constructor = clazz.getConstructor(new Class[] {String.class}); return (Throwable)constructor.newInstance(new Object[] {message}); + } catch (IllegalArgumentException e) { + return e; } catch (Throwable e) { return new Throwable(className + ": " + message); } diff --git a/activemq-openwire-legacy/src/test/java/org/apache/activemq/openwire/OpenWireLegacyValidationTest.java b/activemq-openwire-legacy/src/test/java/org/apache/activemq/openwire/OpenWireLegacyValidationTest.java new file mode 100644 index 00000000000..daee1344b31 --- /dev/null +++ b/activemq-openwire-legacy/src/test/java/org/apache/activemq/openwire/OpenWireLegacyValidationTest.java @@ -0,0 +1,129 @@ +/** + * 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.activemq.openwire; + +import java.io.DataOutput; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +/** + * Test that Openwire marshalling for legacy versions will validate Throwable types during + * unmarshalling commands that contain a Throwable + */ +@RunWith(Parameterized.class) +public class OpenWireLegacyValidationTest extends OpenWireValidationTest { + + + // Run through version 2 - 8 which are legacy + @Parameters(name = "version={0}") + public static Collection data() { + List versions = new ArrayList<>(); + for (int i = 2; i <= 8; i++) { + versions.add(new Object[]{i}); + } + return versions; + } + + public OpenWireLegacyValidationTest(int version) { + super(version); + } + + // Create test marshallers for all legacy versions that will encode NotAThrowable + // instead of the exception type for testing purposes + protected DataStreamMarshaller getExceptionMarshaller() { + switch (version) { + case 2: + return new org.apache.activemq.openwire.v2.ExceptionResponseMarshaller() { + @Override + protected void looseMarshalThrowable(OpenWireFormat wireFormat, Throwable o, + DataOutput dataOut) throws IOException { + dataOut.writeBoolean(o != null); + looseMarshalString(NotAThrowable.class.getName(), dataOut); + looseMarshalString(o.getMessage(), dataOut); + } + }; + case 3: + return new org.apache.activemq.openwire.v3.ExceptionResponseMarshaller() { + @Override + protected void looseMarshalThrowable(OpenWireFormat wireFormat, Throwable o, + DataOutput dataOut) throws IOException { + dataOut.writeBoolean(o != null); + looseMarshalString(NotAThrowable.class.getName(), dataOut); + looseMarshalString(o.getMessage(), dataOut); + } + }; + case 4: + return new org.apache.activemq.openwire.v4.ExceptionResponseMarshaller() { + @Override + protected void looseMarshalThrowable(OpenWireFormat wireFormat, Throwable o, + DataOutput dataOut) throws IOException { + dataOut.writeBoolean(o != null); + looseMarshalString(NotAThrowable.class.getName(), dataOut); + looseMarshalString(o.getMessage(), dataOut); + } + }; + case 5: + return new org.apache.activemq.openwire.v5.ExceptionResponseMarshaller() { + @Override + protected void looseMarshalThrowable(OpenWireFormat wireFormat, Throwable o, + DataOutput dataOut) throws IOException { + dataOut.writeBoolean(o != null); + looseMarshalString(NotAThrowable.class.getName(), dataOut); + looseMarshalString(o.getMessage(), dataOut); + } + }; + case 6: + return new org.apache.activemq.openwire.v6.ExceptionResponseMarshaller() { + @Override + protected void looseMarshalThrowable(OpenWireFormat wireFormat, Throwable o, + DataOutput dataOut) throws IOException { + dataOut.writeBoolean(o != null); + looseMarshalString(NotAThrowable.class.getName(), dataOut); + looseMarshalString(o.getMessage(), dataOut); + } + }; + case 7: + return new org.apache.activemq.openwire.v7.ExceptionResponseMarshaller() { + @Override + protected void looseMarshalThrowable(OpenWireFormat wireFormat, Throwable o, + DataOutput dataOut) throws IOException { + dataOut.writeBoolean(o != null); + looseMarshalString(NotAThrowable.class.getName(), dataOut); + looseMarshalString(o.getMessage(), dataOut); + } + }; + case 8: + return new org.apache.activemq.openwire.v8.ExceptionResponseMarshaller() { + @Override + protected void looseMarshalThrowable(OpenWireFormat wireFormat, Throwable o, + DataOutput dataOut) throws IOException { + dataOut.writeBoolean(o != null); + looseMarshalString(NotAThrowable.class.getName(), dataOut); + looseMarshalString(o.getMessage(), dataOut); + } + }; + default: + throw new IllegalArgumentException("Unknown openwire version of " + version); + } + } + +} diff --git a/pom.xml b/pom.xml index 0b2639c9d3b..6d41dc5c866 100644 --- a/pom.xml +++ b/pom.xml @@ -313,6 +313,13 @@ activemq-client ${project.version} + + org.apache.activemq + activemq-client + ${project.version} + test-jar + test + org.apache.activemq activemq-openwire-legacy