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


Reply via email to