Add spring.rabbitmq.template.allowed-list-patterns property

Update `RabbitProperties` and `RabbitTemplateConfigurer` to support a
`spring.rabbitmq.template.allowed-list-patterns` property.

The can be used to prevent errors of the form:

	java.lang.SecurityException: Attempt to deserialize unauthorized
	class com.example.domain.Message; add allowed class name patterns
	to the message converter or, if you trust the message orginiator,
	set environment variable 'SPRING_AMQP_DESERIALIZATION_TRUST_ALL'
	or system property 'spring.amqp.deserialization.trust.all' to true

See gh-40421
This commit is contained in:
Yanming Zhou
2024-04-18 16:46:33 +08:00
committed by Phillip Webb
parent 4ce736614d
commit c329c5fe5b
3 changed files with 52 additions and 0 deletions

View File

@@ -48,6 +48,7 @@ import org.springframework.util.unit.DataSize;
* @author Rafael Carvalho
* @author Scott Frederick
* @author Lasse Wulff
* @author Yanming Zhou
* @since 1.0.0
*/
@ConfigurationProperties(prefix = "spring.rabbitmq")
@@ -1015,6 +1016,11 @@ public class RabbitProperties {
*/
private boolean observationEnabled;
/**
* Simple patterns for allowable packages/classes for deserialization.
*/
private List<String> allowedListPatterns;
public Retry getRetry() {
return this.retry;
}
@@ -1075,6 +1081,14 @@ public class RabbitProperties {
this.observationEnabled = observationEnabled;
}
public List<String> getAllowedListPatterns() {
return this.allowedListPatterns;
}
public void setAllowedListPatterns(List<String> allowedListPatterns) {
this.allowedListPatterns = allowedListPatterns;
}
}
public static class Retry {

View File

@@ -21,6 +21,7 @@ import java.util.List;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.AllowedListDeserializingMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.util.Assert;
@@ -29,6 +30,7 @@ import org.springframework.util.Assert;
* Configure {@link RabbitTemplate} with sensible defaults.
*
* @author Stephane Nicoll
* @author Yanming Zhou
* @since 2.3.0
*/
public class RabbitTemplateConfigurer {
@@ -102,6 +104,12 @@ public class RabbitTemplateConfigurer {
map.from(templateProperties::getRoutingKey).to(template::setRoutingKey);
map.from(templateProperties::getDefaultReceiveQueue).whenNonNull().to(template::setDefaultReceiveQueue);
map.from(templateProperties::isObservationEnabled).to(template::setObservationEnabled);
if (templateProperties.getAllowedListPatterns() != null) {
MessageConverter messageConverter = template.getMessageConverter();
if (messageConverter instanceof AllowedListDeserializingMessageConverter mc) {
mc.setAllowedListPatterns(templateProperties.getAllowedListPatterns());
}
}
}
private boolean determineMandatoryFlag() {

View File

@@ -17,6 +17,7 @@
package org.springframework.boot.autoconfigure.amqp;
import java.security.NoSuchAlgorithmException;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
@@ -35,6 +36,8 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledForJreRange;
import org.junit.jupiter.api.condition.JRE;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.InOrder;
import org.springframework.amqp.core.AcknowledgeMode;
@@ -59,7 +62,9 @@ import org.springframework.amqp.rabbit.listener.DirectMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.retry.MessageRecoverer;
import org.springframework.amqp.support.converter.AllowedListDeserializingMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.amqp.support.converter.SerializerMessageConverter;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration;
@@ -107,6 +112,7 @@ import static org.mockito.Mockito.mock;
* @author Andy Wilkinson
* @author Phillip Webb
* @author Scott Frederick
* @author Yanming Zhou
*/
@ExtendWith(OutputCaptureExtension.class)
class RabbitAutoConfigurationTests {
@@ -796,6 +802,20 @@ class RabbitAutoConfigurationTests {
});
}
@SuppressWarnings("unchecked")
@ParameterizedTest
@ValueSource(classes = { TestConfiguration.class, TestConfiguration6.class })
void customizeAllowedListPatterns(Class<?> configuration) {
this.contextRunner.withUserConfiguration(configuration)
.withPropertyValues("spring.rabbitmq.template.allowed-list-patterns:*")
.run((context) -> {
MessageConverter messageConverter = context.getBean(RabbitTemplate.class).getMessageConverter();
assertThat(messageConverter).isInstanceOfSatisfying(AllowedListDeserializingMessageConverter.class,
(mc) -> assertThat(mc).extracting("allowedListPatterns")
.isInstanceOfSatisfying(Collection.class, (set) -> assertThat(set).contains("*")));
});
}
@Test
void noSslByDefault() {
this.contextRunner.withUserConfiguration(TestConfiguration.class).run((context) -> {
@@ -1113,6 +1133,16 @@ class RabbitAutoConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
static class TestConfiguration6 {
@Bean
MessageConverter messageConverter() {
return new SerializerMessageConverter();
}
}
@Configuration(proxyBeanMethods = false)
static class MessageConvertersConfiguration {