This is an automated email from the ASF dual-hosted git repository.
cshannon pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/activemq.git
The following commit(s) were added to refs/heads/main by this push:
new 103962e45c Block the XBeanBrokerFactory by default inside
VMTransportFactory (#2003)
103962e45c is described below
commit 103962e45cdf683f8bb948d385dc1e14fcfbd4c8
Author: Christopher L. Shannon <[email protected]>
AuthorDate: Wed May 13 11:14:44 2026 -0400
Block the XBeanBrokerFactory by default inside VMTransportFactory (#2003)
By default the VMTransportFactory will not be allowed to use
XBeanBrokerFactory to create new brokers. Only the default and
properties factories will be enabled. To enable the XBeanBrokerFactory,
the property
org.apache.activemq.transport.VM_TRANSPORT_FACTORY_SCHEMES_ENABLED
can be configured.
---
.../activemq/transport/vm/VMTransportFactory.java | 35 ++++++
.../config/BrokerXmlConfigFromJNDITest.java | 16 +++
.../spring/ActiveMQConnectionFactoryXBeanTest.java | 11 ++
.../transport/vm/VMTransportFactoryTest.java | 134 +++++++++++++++++++++
.../apache/activemq/util/VmTransportTestUtils.java | 48 ++++++++
5 files changed, 244 insertions(+)
diff --git
a/activemq-broker/src/main/java/org/apache/activemq/transport/vm/VMTransportFactory.java
b/activemq-broker/src/main/java/org/apache/activemq/transport/vm/VMTransportFactory.java
index 56baaeec98..63b94dac5d 100644
---
a/activemq-broker/src/main/java/org/apache/activemq/transport/vm/VMTransportFactory.java
+++
b/activemq-broker/src/main/java/org/apache/activemq/transport/vm/VMTransportFactory.java
@@ -19,11 +19,14 @@ package org.apache.activemq.transport.vm;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Collectors;
import org.apache.activemq.broker.BrokerFactory;
import org.apache.activemq.broker.BrokerFactoryHandler;
import org.apache.activemq.broker.BrokerRegistry;
@@ -44,12 +47,29 @@ import org.slf4j.MDC;
public class VMTransportFactory extends TransportFactory {
+ public static final String VM_TRANSPORT_FACTORY_SCHEMES_ENABLED_PROP =
+
"org.apache.activemq.transport.VM_TRANSPORT_FACTORY_SCHEMES_ENABLED";
+ public static final String DEFAULT_ALLOWED_SCHEMES = "broker,properties";
+
public static final ConcurrentMap<String, BrokerService> BROKERS = new
ConcurrentHashMap<String, BrokerService>();
public static final ConcurrentMap<String, TransportConnector> CONNECTORS =
new ConcurrentHashMap<String, TransportConnector>();
public static final ConcurrentMap<String, VMTransportServer> SERVERS = new
ConcurrentHashMap<String, VMTransportServer>();
private static final Logger LOG =
LoggerFactory.getLogger(VMTransportFactory.class);
BrokerFactoryHandler brokerFactoryHandler;
+ private final Set<String> allowedSchemes;
+
+ public VMTransportFactory() {
+ final String allowedSchemes =
System.getProperty(VM_TRANSPORT_FACTORY_SCHEMES_ENABLED_PROP,
+ DEFAULT_ALLOWED_SCHEMES);
+
+ // Asterisk will map to null which will allow all and skip checking
+ // Empty string will map to an empty set and will deny all
+ this.allowedSchemes = !allowedSchemes.equals("*") ?
+ Arrays.stream(allowedSchemes.split("\\s*,\\s*"))
+ .filter(s -> !s.isBlank())
+ .collect(Collectors.toUnmodifiableSet()) : null;
+ }
@Override
public Transport doConnect(URI location) throws Exception {
@@ -119,6 +139,7 @@ public class VMTransportFactory extends TransportFactory {
throw new IOException("Broker named '" + host + "'
does not exist.");
}
try {
+ validateBrokerCreationSchema(host, brokerURI);
if (brokerFactoryHandler != null) {
broker =
brokerFactoryHandler.createBroker(brokerURI);
} else {
@@ -162,6 +183,20 @@ public class VMTransportFactory extends TransportFactory {
return transport;
}
+ private void validateBrokerCreationSchema(String host, URI brokerURI) {
+ if (allowedSchemes != null) {
+ final String detectedScheme = brokerURI.getScheme();
+ if (detectedScheme == null) {
+ throw new IllegalArgumentException("Could not detect scheme in
given URI [" + brokerURI + "]");
+ }
+ if (!allowedSchemes.contains(detectedScheme)){
+ throw new IllegalArgumentException("Broker named '" + host +
"' does not exist and "
+ + "broker creation using the scheme '" +
detectedScheme + "' is not enabled via the VMTransportFactory. "
+ + "To allow creation, configure the system property "
+ VM_TRANSPORT_FACTORY_SCHEMES_ENABLED_PROP);
+ }
+ }
+ }
+
private static String extractHost(URI location) {
String host = location.getHost();
if (host == null || host.length() == 0) {
diff --git
a/activemq-unit-tests/src/test/java/org/apache/activemq/config/BrokerXmlConfigFromJNDITest.java
b/activemq-unit-tests/src/test/java/org/apache/activemq/config/BrokerXmlConfigFromJNDITest.java
index f141690e51..68139efe5c 100644
---
a/activemq-unit-tests/src/test/java/org/apache/activemq/config/BrokerXmlConfigFromJNDITest.java
+++
b/activemq-unit-tests/src/test/java/org/apache/activemq/config/BrokerXmlConfigFromJNDITest.java
@@ -16,6 +16,8 @@
*/
package org.apache.activemq.config;
+import static
org.apache.activemq.util.VmTransportTestUtils.resetVmTransportFactory;
+
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.test.JmsTopicSendReceiveWithTwoConnectionsTest;
@@ -28,6 +30,20 @@ import java.util.Hashtable;
*
*/
public class BrokerXmlConfigFromJNDITest extends
JmsTopicSendReceiveWithTwoConnectionsTest {
+
+ @Override
+ protected void setUp() throws Exception {
+ // reset before each test
+ resetVmTransportFactory("xbean");
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ resetVmTransportFactory();
+ }
+
@Override
protected ActiveMQConnectionFactory createConnectionFactory() throws
Exception {
assertBaseDirectoryContainsSpaces();
diff --git
a/activemq-unit-tests/src/test/java/org/apache/activemq/spring/ActiveMQConnectionFactoryXBeanTest.java
b/activemq-unit-tests/src/test/java/org/apache/activemq/spring/ActiveMQConnectionFactoryXBeanTest.java
index c2b913161a..95bb692675 100644
---
a/activemq-unit-tests/src/test/java/org/apache/activemq/spring/ActiveMQConnectionFactoryXBeanTest.java
+++
b/activemq-unit-tests/src/test/java/org/apache/activemq/spring/ActiveMQConnectionFactoryXBeanTest.java
@@ -16,6 +16,7 @@
*/
package org.apache.activemq.spring;
+import static
org.apache.activemq.util.VmTransportTestUtils.resetVmTransportFactory;
import static
org.apache.activemq.xbean.XBeanBrokerFactory.DEFAULT_ALLOWED_PROTOCOLS;
import static
org.apache.activemq.xbean.XBeanBrokerFactory.XBEAN_BROKER_FACTORY_PROTOCOLS_PROP;
import static org.junit.Assert.assertNotNull;
@@ -29,9 +30,11 @@ import java.net.UnknownHostException;
import java.nio.file.NoSuchFileException;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.test.annotations.ParallelTest;
+import org.apache.activemq.transport.vm.VMTransportFactory;
import org.apache.activemq.xbean.XBeanBrokerFactory;
import org.junit.AfterClass;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.springframework.beans.FatalBeanException;
@@ -39,6 +42,12 @@ import org.springframework.beans.FatalBeanException;
@Category(ParallelTest.class)
public class ActiveMQConnectionFactoryXBeanTest {
+ @BeforeClass
+ public static void beforeClass() throws Exception {
+ // enable xbean for the vm transport factory
+ resetVmTransportFactory("xbean");
+ }
+
@Before
public void setUp() throws Exception {
// reset before each test
@@ -48,6 +57,8 @@ public class ActiveMQConnectionFactoryXBeanTest {
@AfterClass
public static void tearDown() throws Exception {
System.setProperty(XBEAN_BROKER_FACTORY_PROTOCOLS_PROP,
DEFAULT_ALLOWED_PROTOCOLS);
+ // reset defaults
+ resetVmTransportFactory();
}
// File and classpath are allowed by default
diff --git
a/activemq-unit-tests/src/test/java/org/apache/activemq/transport/vm/VMTransportFactoryTest.java
b/activemq-unit-tests/src/test/java/org/apache/activemq/transport/vm/VMTransportFactoryTest.java
new file mode 100644
index 0000000000..1f05168ed4
--- /dev/null
+++
b/activemq-unit-tests/src/test/java/org/apache/activemq/transport/vm/VMTransportFactoryTest.java
@@ -0,0 +1,134 @@
+/**
+ * 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.transport.vm;
+
+import static
org.apache.activemq.util.VmTransportTestUtils.resetVmTransportFactory;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import jakarta.jms.Connection;
+import jakarta.jms.JMSException;
+import org.apache.activemq.ActiveMQConnectionFactory;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.Test;
+
+public class VMTransportFactoryTest {
+
+ @Before
+ public void setUp() throws Exception {
+ // reset before each test
+ resetVmTransportFactory();
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ resetVmTransportFactory();
+ }
+
+ @Test
+ public void testDefaults() throws Exception {
+ // broker and properties allowed by default
+ assertBrokerCreated("vm:localhost?persistent=false");
+
assertBrokerCreated("vm:(broker:(tcp://localhost:0)?persistent=false)");
+
assertBrokerCreated("vm://localhost?brokerConfig=properties:org/apache/activemq/config/broker.properties");
+
+ // xbean not allowed by default
+
assertBrokerStartError("vm://localhost?brokerConfig=xbean:activemq.xml");
+ }
+
+ @Test
+ public void testAllowAll() throws Exception {
+ resetVmTransportFactory("broker,properties,xbean");
+
+ // broker and properties allowed by default
+ assertBrokerCreated("vm:localhost?persistent=false");
+
assertBrokerCreated("vm:(broker:(tcp://localhost:0)?persistent=false)");
+
assertBrokerCreated("vm://localhost?brokerConfig=properties:org/apache/activemq/config/broker.properties");
+
+ // xbean now allowed
+ assertBrokerCreated("vm://localhost?brokerConfig=xbean:activemq.xml");
+ }
+
+ @Test
+ public void testAllowAllWildcard() throws Exception {
+ resetVmTransportFactory("*");
+
+ // all allowed
+ assertBrokerCreated("vm:localhost?persistent=false");
+
assertBrokerCreated("vm:(broker:(tcp://localhost:0)?persistent=false)");
+
assertBrokerCreated("vm://localhost?brokerConfig=properties:org/apache/activemq/config/broker.properties");
+ assertBrokerCreated("vm://localhost?brokerConfig=xbean:activemq.xml");
+ }
+
+ @Test
+ public void testNullSchemes() throws Exception {
+ // should set to defaults
+ resetVmTransportFactory(null);
+
+ // broker and properties allowed by default
+ assertBrokerCreated("vm:localhost?persistent=false");
+
assertBrokerCreated("vm:(broker:(tcp://localhost:0)?persistent=false)");
+
assertBrokerCreated("vm://localhost?brokerConfig=properties:org/apache/activemq/config/broker.properties");
+
+ // xbean not allowed by default
+
assertBrokerStartError("vm://localhost?brokerConfig=xbean:activemq.xml");
+ }
+
+ @Test
+ public void testNoneAllowed() throws Exception {
+ // deny all
+ resetVmTransportFactory("");
+
+ // nothing allowed
+ assertBrokerStartError("vm:localhost?persistent=false");
+
assertBrokerStartError("vm:(broker:(tcp://localhost:0)?persistent=false)");
+
assertBrokerStartError("vm://localhost?brokerConfig=properties:org/apache/activemq/config/broker.properties");
+
assertBrokerStartError("vm://localhost?brokerConfig=xbean:activemq.xml");
+ }
+
+ @Test
+ public void testOneAllowed() throws Exception {
+ // deny all
+ resetVmTransportFactory("properties");
+
+ // only properties allowed
+ assertBrokerStartError("vm:localhost?persistent=false");
+
assertBrokerStartError("vm:(broker:(tcp://localhost:0)?persistent=false)");
+
assertBrokerCreated("vm://localhost?brokerConfig=properties:org/apache/activemq/config/broker.properties");
+
assertBrokerStartError("vm://localhost?brokerConfig=xbean:activemq.xml");
+ }
+
+ private void assertBrokerCreated(String url) throws JMSException {
+ final ActiveMQConnectionFactory factory = new
ActiveMQConnectionFactory(url);
+ try (Connection connection = factory.createConnection()) {
+ assertNotNull(connection);
+ }
+ }
+
+ private void assertBrokerStartError(String url) {
+ final ActiveMQConnectionFactory factory = new
ActiveMQConnectionFactory(url);
+ try (Connection ignored = factory.createConnection()) {
+ fail("Should have failed with an exception");
+ } catch (JMSException e) {
+ Throwable cause = e.getCause() != null ? e.getCause() : e;
+ assertTrue(cause instanceof IllegalArgumentException);
+ }
+ }
+
+}
diff --git
a/activemq-unit-tests/src/test/java/org/apache/activemq/util/VmTransportTestUtils.java
b/activemq-unit-tests/src/test/java/org/apache/activemq/util/VmTransportTestUtils.java
new file mode 100644
index 0000000000..7a4d38aee8
--- /dev/null
+++
b/activemq-unit-tests/src/test/java/org/apache/activemq/util/VmTransportTestUtils.java
@@ -0,0 +1,48 @@
+/*
+ * 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.util;
+
+import java.lang.reflect.Field;
+import java.util.concurrent.ConcurrentMap;
+import org.apache.activemq.transport.TransportFactory;
+import org.apache.activemq.transport.vm.VMTransportFactory;
+
+public class VmTransportTestUtils {
+
+ public static void resetVmTransportFactory() throws Exception {
+ resetVmTransportFactory(VMTransportFactory.DEFAULT_ALLOWED_SCHEMES);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static void resetVmTransportFactory(String allowedSchemes) throws
Exception {
+ if (allowedSchemes == null) {
+
System.clearProperty(VMTransportFactory.VM_TRANSPORT_FACTORY_SCHEMES_ENABLED_PROP);
+ } else {
+ // set property to allowed schemes
+
System.setProperty(VMTransportFactory.VM_TRANSPORT_FACTORY_SCHEMES_ENABLED_PROP,
+ allowedSchemes);
+ }
+
+ // clear any cached factory so the next call will create a new
transport and use
+ // the correct property setting
+ Field factoriesField =
TransportFactory.class.getDeclaredField("TRANSPORT_FACTORYS");
+ factoriesField.setAccessible(true);
+ ConcurrentMap<String, TransportFactory> factories =
+ (ConcurrentMap<String, TransportFactory>)
factoriesField.get(TransportFactory.class);
+ factories.remove("vm");
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
For further information, visit: https://activemq.apache.org/contact