Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,21 @@ public class ClassLoadingAwareObjectInputStream extends ObjectInputStream {
private static final ClassLoader FALLBACK_CLASS_LOADER =
ClassLoadingAwareObjectInputStream.class.getClassLoader();

public static final Set<Class<?>> ALLOWED_JDK_TYPES = Set.of(
Boolean.class, Short.class, Integer.class, Long.class,
Float.class, Double.class, String.class, Character.class, Byte.class,
Throwable.class, Exception.class, StackTraceElement.class);

public static final String DEFAULT_SERIALIZABLE_PACKAGES = "org.apache.activemq,org.fusesource.hawtbuf,com.thoughtworks.xstream.mapper";
public static final String[] serializablePackages;

private List<String> trustedPackages = new ArrayList<String>();
private List<String> trustedPackages = new ArrayList<>();
private boolean trustAllPackages = false;

private final ClassLoader inLoader;

static {
serializablePackages = System.getProperty("org.apache.activemq.SERIALIZABLE_PACKAGES","java.lang,org.apache.activemq,org.fusesource.hawtbuf,com.thoughtworks.xstream.mapper").split(",");
serializablePackages = System.getProperty("org.apache.activemq.SERIALIZABLE_PACKAGES", DEFAULT_SERIALIZABLE_PACKAGES).split(",");
}

public ClassLoadingAwareObjectInputStream(InputStream in) throws IOException {
Expand Down Expand Up @@ -98,7 +104,7 @@ private boolean trustAllPackages() {
}

private void checkSecurity(Class clazz) throws ClassNotFoundException {
if (trustAllPackages() || clazz.isPrimitive()) {
if (trustAllPackages() || clazz.isPrimitive() || ALLOWED_JDK_TYPES.contains(clazz)) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good change to permanently support these core types

return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,17 @@

public class XStreamSupport {

private static final Class<?>[] ALLOWED_JDK_TYPES =
ClassLoadingAwareObjectInputStream.ALLOWED_JDK_TYPES.toArray(new Class[0]);

public static XStream createXStream() {
XStream stream = new XStream();
stream.addPermission(NoTypePermission.NONE);
stream.addPermission(PrimitiveTypePermission.PRIMITIVES);
stream.addPermission(ArrayTypePermission.ARRAYS);
stream.allowTypeHierarchy(Collection.class);
stream.allowTypeHierarchy(Map.class);
stream.allowTypes(new Class[]{String.class});
stream.allowTypes(ALLOWED_JDK_TYPES);
if (ClassLoadingAwareObjectInputStream.isAllAllowed()) {
stream.addPermission(AnyTypePermission.ANY);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Arrays;
import java.util.Set;
import java.util.UUID;
import java.util.Vector;

Expand Down Expand Up @@ -206,8 +207,23 @@ public void testPrimitveCharNotFiltered() throws Exception {
}

@Test
public void testReadObjectStringNotFiltered() throws Exception {
doTestReadObject(new String(name.getMethodName()), ACCEPTS_NONE_FILTER);
public void testReadObjectJdkTypesNotFiltered() throws Exception {
for (String filter : Set.of(ACCEPTS_ALL_FILTER, ACCEPTS_NONE_FILTER,
ClassLoadingAwareObjectInputStream.DEFAULT_SERIALIZABLE_PACKAGES)) {
doTestReadObject(Boolean.TRUE, filter);
doTestReadObject("test", filter);
doTestReadObject(Byte.valueOf("0"), filter);
doTestReadObject(Character.valueOf('a'), filter);
doTestReadObject(Integer.valueOf(100), filter);
doTestReadObject(Long.valueOf(0), filter);
doTestReadObject(Float.valueOf(0), filter);
doTestReadObject(Double.valueOf(0), filter);
}

// these also require collections classes in java util as well as StackTraceElement
// they also can't be compared for equality as they don't implement equals
doTestReadObject(new Exception(), "java.util", false);
doTestReadObject(new Throwable(), "java.util", false);
}

//----- Test that primitive arrays get past filters ----------------------//
Expand Down Expand Up @@ -429,6 +445,10 @@ public void testReadObjectFailsWithUnstrustedContentInTrustedType() throws Excep
//----- Internal methods -------------------------------------------------//

private void doTestReadObject(Object value, String filter) throws Exception {
doTestReadObject(value, filter, true);
}

private void doTestReadObject(Object value, String filter, boolean equalityCheck) throws Exception {
byte[] serialized = serializeObject(value);

try (ByteArrayInputStream input = new ByteArrayInputStream(serialized);
Expand All @@ -441,10 +461,12 @@ private void doTestReadObject(Object value, String filter) throws Exception {
Object result = reader.readObject();
assertNotNull(result);
assertEquals(value.getClass(), result.getClass());
if (result.getClass().isArray()) {
assertTrue(Arrays.deepEquals((Object[]) value, (Object[]) result));
} else {
assertEquals(value, result);
if (equalityCheck) {
if (result.getClass().isArray()) {
assertTrue(Arrays.deepEquals((Object[]) value, (Object[]) result));
} else {
assertEquals(value, result);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1149,6 +1149,62 @@ public void testTransformationSendJSONObject() throws Exception {
assertEquals("Dejan", object.getName());
}


@Test(timeout = 60000)
public void testTransformationReceiveXMLObjectDouble() throws Exception {
MessageConsumer consumer = session.createConsumer(queue);

String frame = "CONNECT\n" + "login:system\n" + "passcode:manager\n\n" + Stomp.NULL;
stompConnection.sendFrame(frame);

frame = stompConnection.receiveFrame();
assertTrue(frame.startsWith("CONNECTED"));

// Double should be allowed by default
frame = "SEND\n" + "destination:/queue/" + getQueueName() + "\n" +
"transformation:" + Stomp.Transformations.JMS_OBJECT_XML + "\n\n" +
"<java.lang.Double>1.1</java.lang.Double>" + Stomp.NULL;

stompConnection.sendFrame(frame);

Message message = consumer.receive(2500);
assertNotNull(message);

LOG.info("Broker sent: {}", message);

assertTrue(message instanceof ObjectMessage);
ObjectMessage objectMessage = (ObjectMessage)message;
Double object = (Double)objectMessage.getObject();
assertEquals(Double.valueOf(1.1), object);
}

@Test(timeout = 60000)
public void testTransformationSendXMLObjectNotAllowed() throws Exception {
MessageConsumer consumer = session.createConsumer(queue);

String frame = "CONNECT\n" + "login:system\n" + "passcode:manager\n\n" + Stomp.NULL;
stompConnection.sendFrame(frame);

frame = stompConnection.receiveFrame();
assertTrue(frame.startsWith("CONNECTED"));

// ProcessBuilder is not allowed by default so the conversion should fail and
// then fall back to using a TextMessage, as well as setting an error header
frame = "SEND\n" + "destination:/queue/" + getQueueName() + "\n" +
"transformation:" + Stomp.Transformations.JMS_OBJECT_XML + "\n\n" +
"<java.lang.ProcessBuilder><command><string>id</string></command></java.lang.ProcessBuilder>" + Stomp.NULL;

stompConnection.sendFrame(frame);

Message message = consumer.receive(2500);
assertNotNull(message);
LOG.info("Broker sent: {}", message);

// The message should be Text and marked with a transformation error header
assertTrue(message instanceof TextMessage);
assertEquals("java.lang.ProcessBuilder", message.getStringProperty("transformation-error"));
}

@Test(timeout = 60000)
public void testTransformationSubscribeXML() throws Exception {

Expand Down
Loading