Skip to content

Commit 68b5d2b

Browse files
authored
Remove "java.lang" package as a default allowed serializable package (#2026) (#2027)
Many classes in the java.lang package should not ever need to be serialized so this commit removes the default package and instead includes an allow list of classes that are ok to serialize that are part of the package. Users have the option to restore the previous behavior by appending "java.lang" back to the serialized packages property if desired. (cherry picked from commit e9ed448)
1 parent dfeac9f commit 68b5d2b

4 files changed

Lines changed: 97 additions & 10 deletions

File tree

activemq-client/src/main/java/org/apache/activemq/util/ClassLoadingAwareObjectInputStream.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,21 @@ public class ClassLoadingAwareObjectInputStream extends ObjectInputStream {
3232
private static final ClassLoader FALLBACK_CLASS_LOADER =
3333
ClassLoadingAwareObjectInputStream.class.getClassLoader();
3434

35+
public static final Set<Class<?>> ALLOWED_JDK_TYPES = Set.of(
36+
Boolean.class, Short.class, Integer.class, Long.class,
37+
Float.class, Double.class, String.class, Character.class, Byte.class,
38+
Throwable.class, Exception.class, StackTraceElement.class);
39+
40+
public static final String DEFAULT_SERIALIZABLE_PACKAGES = "org.apache.activemq,org.fusesource.hawtbuf,com.thoughtworks.xstream.mapper";
3541
public static final String[] serializablePackages;
3642

37-
private List<String> trustedPackages = new ArrayList<String>();
43+
private List<String> trustedPackages = new ArrayList<>();
3844
private boolean trustAllPackages = false;
3945

4046
private final ClassLoader inLoader;
4147

4248
static {
43-
serializablePackages = System.getProperty("org.apache.activemq.SERIALIZABLE_PACKAGES","java.lang,org.apache.activemq,org.fusesource.hawtbuf,com.thoughtworks.xstream.mapper").split(",");
49+
serializablePackages = System.getProperty("org.apache.activemq.SERIALIZABLE_PACKAGES", DEFAULT_SERIALIZABLE_PACKAGES).split(",");
4450
}
4551

4652
public ClassLoadingAwareObjectInputStream(InputStream in) throws IOException {
@@ -98,7 +104,7 @@ private boolean trustAllPackages() {
98104
}
99105

100106
private void checkSecurity(Class clazz) throws ClassNotFoundException {
101-
if (trustAllPackages() || clazz.isPrimitive()) {
107+
if (trustAllPackages() || clazz.isPrimitive() || ALLOWED_JDK_TYPES.contains(clazz)) {
102108
return;
103109
}
104110

activemq-client/src/main/java/org/apache/activemq/util/XStreamSupport.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,17 @@
2828

2929
public class XStreamSupport {
3030

31+
private static final Class<?>[] ALLOWED_JDK_TYPES =
32+
ClassLoadingAwareObjectInputStream.ALLOWED_JDK_TYPES.toArray(new Class[0]);
33+
3134
public static XStream createXStream() {
3235
XStream stream = new XStream();
3336
stream.addPermission(NoTypePermission.NONE);
3437
stream.addPermission(PrimitiveTypePermission.PRIMITIVES);
3538
stream.addPermission(ArrayTypePermission.ARRAYS);
3639
stream.allowTypeHierarchy(Collection.class);
3740
stream.allowTypeHierarchy(Map.class);
38-
stream.allowTypes(new Class[]{String.class});
41+
stream.allowTypes(ALLOWED_JDK_TYPES);
3942
if (ClassLoadingAwareObjectInputStream.isAllAllowed()) {
4043
stream.addPermission(AnyTypePermission.ANY);
4144
} else {

activemq-client/src/test/java/org/apache/activemq/util/ClassLoadingAwareObjectInputStreamTest.java

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.io.IOException;
2727
import java.io.ObjectOutputStream;
2828
import java.util.Arrays;
29+
import java.util.Set;
2930
import java.util.UUID;
3031
import java.util.Vector;
3132

@@ -206,8 +207,23 @@ public void testPrimitveCharNotFiltered() throws Exception {
206207
}
207208

208209
@Test
209-
public void testReadObjectStringNotFiltered() throws Exception {
210-
doTestReadObject(new String(name.getMethodName()), ACCEPTS_NONE_FILTER);
210+
public void testReadObjectJdkTypesNotFiltered() throws Exception {
211+
for (String filter : Set.of(ACCEPTS_ALL_FILTER, ACCEPTS_NONE_FILTER,
212+
ClassLoadingAwareObjectInputStream.DEFAULT_SERIALIZABLE_PACKAGES)) {
213+
doTestReadObject(Boolean.TRUE, filter);
214+
doTestReadObject("test", filter);
215+
doTestReadObject(Byte.valueOf("0"), filter);
216+
doTestReadObject(Character.valueOf('a'), filter);
217+
doTestReadObject(Integer.valueOf(100), filter);
218+
doTestReadObject(Long.valueOf(0), filter);
219+
doTestReadObject(Float.valueOf(0), filter);
220+
doTestReadObject(Double.valueOf(0), filter);
221+
}
222+
223+
// these also require collections classes in java util as well as StackTraceElement
224+
// they also can't be compared for equality as they don't implement equals
225+
doTestReadObject(new Exception(), "java.util", false);
226+
doTestReadObject(new Throwable(), "java.util", false);
211227
}
212228

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

431447
private void doTestReadObject(Object value, String filter) throws Exception {
448+
doTestReadObject(value, filter, true);
449+
}
450+
451+
private void doTestReadObject(Object value, String filter, boolean equalityCheck) throws Exception {
432452
byte[] serialized = serializeObject(value);
433453

434454
try (ByteArrayInputStream input = new ByteArrayInputStream(serialized);
@@ -441,10 +461,12 @@ private void doTestReadObject(Object value, String filter) throws Exception {
441461
Object result = reader.readObject();
442462
assertNotNull(result);
443463
assertEquals(value.getClass(), result.getClass());
444-
if (result.getClass().isArray()) {
445-
assertTrue(Arrays.deepEquals((Object[]) value, (Object[]) result));
446-
} else {
447-
assertEquals(value, result);
464+
if (equalityCheck) {
465+
if (result.getClass().isArray()) {
466+
assertTrue(Arrays.deepEquals((Object[]) value, (Object[]) result));
467+
} else {
468+
assertEquals(value, result);
469+
}
448470
}
449471
}
450472
}

activemq-stomp/src/test/java/org/apache/activemq/transport/stomp/StompTest.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,6 +1167,62 @@ public void testTransformationSendJSONObject() throws Exception {
11671167
assertEquals("Dejan", object.getName());
11681168
}
11691169

1170+
1171+
@Test(timeout = 60000)
1172+
public void testTransformationReceiveXMLObjectDouble() throws Exception {
1173+
MessageConsumer consumer = session.createConsumer(queue);
1174+
1175+
String frame = "CONNECT\n" + "login:system\n" + "passcode:manager\n\n" + Stomp.NULL;
1176+
stompConnection.sendFrame(frame);
1177+
1178+
frame = stompConnection.receiveFrame();
1179+
assertTrue(frame.startsWith("CONNECTED"));
1180+
1181+
// Double should be allowed by default
1182+
frame = "SEND\n" + "destination:/queue/" + getQueueName() + "\n" +
1183+
"transformation:" + Stomp.Transformations.JMS_OBJECT_XML + "\n\n" +
1184+
"<java.lang.Double>1.1</java.lang.Double>" + Stomp.NULL;
1185+
1186+
stompConnection.sendFrame(frame);
1187+
1188+
Message message = consumer.receive(2500);
1189+
assertNotNull(message);
1190+
1191+
LOG.info("Broker sent: {}", message);
1192+
1193+
assertTrue(message instanceof ObjectMessage);
1194+
ObjectMessage objectMessage = (ObjectMessage)message;
1195+
Double object = (Double)objectMessage.getObject();
1196+
assertEquals(Double.valueOf(1.1), object);
1197+
}
1198+
1199+
@Test(timeout = 60000)
1200+
public void testTransformationSendXMLObjectNotAllowed() throws Exception {
1201+
MessageConsumer consumer = session.createConsumer(queue);
1202+
1203+
String frame = "CONNECT\n" + "login:system\n" + "passcode:manager\n\n" + Stomp.NULL;
1204+
stompConnection.sendFrame(frame);
1205+
1206+
frame = stompConnection.receiveFrame();
1207+
assertTrue(frame.startsWith("CONNECTED"));
1208+
1209+
// ProcessBuilder is not allowed by default so the conversion should fail and
1210+
// then fall back to using a TextMessage, as well as setting an error header
1211+
frame = "SEND\n" + "destination:/queue/" + getQueueName() + "\n" +
1212+
"transformation:" + Stomp.Transformations.JMS_OBJECT_XML + "\n\n" +
1213+
"<java.lang.ProcessBuilder><command><string>id</string></command></java.lang.ProcessBuilder>" + Stomp.NULL;
1214+
1215+
stompConnection.sendFrame(frame);
1216+
1217+
Message message = consumer.receive(2500);
1218+
assertNotNull(message);
1219+
LOG.info("Broker sent: {}", message);
1220+
1221+
// The message should be Text and marked with a transformation error header
1222+
assertTrue(message instanceof TextMessage);
1223+
assertEquals("java.lang.ProcessBuilder", message.getStringProperty("transformation-error"));
1224+
}
1225+
11701226
@Test(timeout = 60000)
11711227
public void testTransformationSubscribeXML() throws Exception {
11721228

0 commit comments

Comments
 (0)