Skip to content

Commit 807fb57

Browse files
committed
feat(security): verify that the tolerant ObjectMapper is used
1 parent a4105d6 commit 807fb57

File tree

5 files changed

+163
-2
lines changed

5 files changed

+163
-2
lines changed

sda-commons-web-autoconfigure/src/main/java/org/sdase/commons/spring/boot/web/jackson/SdaObjectMapperConfiguration.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,19 @@
1616
import java.time.ZonedDateTime;
1717
import org.springframework.boot.autoconfigure.AutoConfiguration;
1818
import org.springframework.context.annotation.Bean;
19+
import org.springframework.context.annotation.Primary;
20+
import org.springframework.core.Ordered;
21+
import org.springframework.core.annotation.Order;
1922
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
2023

2124
@AutoConfiguration
25+
@Order(Ordered.HIGHEST_PRECEDENCE)
2226
public class SdaObjectMapperConfiguration {
23-
@Bean
24-
Jackson2ObjectMapperBuilder objectMapperBuilder() {
27+
public static final String OBJECT_MAPPER_BUILDER_BEAN_NAME = "sdaObjectMapperBuilder";
28+
29+
@Bean(name = OBJECT_MAPPER_BUILDER_BEAN_NAME)
30+
@Primary
31+
public Jackson2ObjectMapperBuilder sdaObjectMapperBuilder() {
2532
return new Jackson2ObjectMapperBuilder()
2633
.featuresToDisable(
2734
SerializationFeature.FAIL_ON_EMPTY_BEANS,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Copyright 2022- SDA SE Open Industry Solutions (https://www.sda.se)
3+
*
4+
* Use of this source code is governed by an MIT-style
5+
* license that can be found in the LICENSE file or at
6+
* https://opensource.org/licenses/MIT.
7+
*/
8+
package org.sdase.commons.spring.boot.web.security.exception;
9+
10+
/** Exception to be thrown if the configuration looks suspicious. */
11+
public class InsecureConfigurationException extends RuntimeException {
12+
13+
public InsecureConfigurationException(String message) {
14+
super(message);
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2022- SDA SE Open Industry Solutions (https://www.sda.se)
3+
*
4+
* Use of this source code is governed by an MIT-style
5+
* license that can be found in the LICENSE file or at
6+
* https://opensource.org/licenses/MIT.
7+
*/
8+
package org.sdase.commons.spring.boot.web.security.validation;
9+
10+
import javax.annotation.PostConstruct;
11+
import org.sdase.commons.spring.boot.web.jackson.SdaObjectMapperConfiguration;
12+
import org.sdase.commons.spring.boot.web.security.exception.InsecureConfigurationException;
13+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
14+
import org.springframework.stereotype.Component;
15+
16+
/**
17+
* Checks that custom error mappers are registered by the SdaObjectMapperConfiguration. The check is
18+
* indirectly performed by checking that the required bean is in the spring context. This class
19+
* checks for the risks identified in the security guide as:
20+
*
21+
* <ul>
22+
* <li>Detection of confidential components
23+
* </ul>
24+
*/
25+
@Component
26+
@ConditionalOnMissingBean(name = SdaObjectMapperConfiguration.OBJECT_MAPPER_BUILDER_BEAN_NAME)
27+
public class CustomObjectMapperAdvice {
28+
29+
private static final String JACKSON_CONFIGURATION_BEAN_CLASS = "Jackson2ObjectMapperBuilder";
30+
31+
@PostConstruct
32+
public void check() {
33+
throw new InsecureConfigurationException(
34+
"Missing "
35+
+ SdaObjectMapperConfiguration.OBJECT_MAPPER_BUILDER_BEAN_NAME
36+
+ " bean from org.sdase.commons.spring.boot.web.jackson. The "
37+
+ JACKSON_CONFIGURATION_BEAN_CLASS
38+
+ " component registers custom mappers.");
39+
}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2022- SDA SE Open Industry Solutions (https://www.sda.se)
3+
*
4+
* Use of this source code is governed by an MIT-style
5+
* license that can be found in the LICENSE file or at
6+
* https://opensource.org/licenses/MIT.
7+
*/
8+
package org.sdase.commons.spring.boot.web.security.test;
9+
10+
import org.springframework.boot.test.context.SpringBootContextLoader;
11+
import org.springframework.boot.test.context.SpringBootTestContextBootstrapper;
12+
import org.springframework.boot.test.context.assertj.ApplicationContextAssert;
13+
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
14+
import org.springframework.context.ConfigurableApplicationContext;
15+
import org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate;
16+
import org.springframework.test.context.support.DefaultBootstrapContext;
17+
18+
public class ContextUtils {
19+
private ContextUtils() {
20+
// avoid instantiation
21+
}
22+
23+
/**
24+
* Creates a spring test context for testing application startup failures. Using {@link
25+
* org.springframework.boot.test.context.SpringBootTest} annotation will prevent the tests from
26+
* starting. Tests that need dependencies from the web environment must add
27+
* `@SpringBootTest(webEnvironment = ...)` to the tested Spring Boot application class because
28+
* that is how it is internally checked. The test class must not be annotated with {@link
29+
* org.springframework.boot.test.context.SpringBootTest}.
30+
*
31+
* <p>Example:
32+
*
33+
* <pre>
34+
* <code>assertThat(createTestContext(AppFailingToInitialize.class)).hasFailed();</code>
35+
* </pre>
36+
*
37+
* @see ApplicationContextAssert for details about the assertions to verify conditions of an
38+
* {@link org.springframework.context.ApplicationContext} that may have {@linkplain
39+
* ApplicationContextAssert#hasFailed() failed to initialize}.
40+
* @param springBootApplicationClass The application class annotated with `SpringBootApplication`
41+
* @return An assertable application context.
42+
*/
43+
public static AssertableApplicationContext createTestContext(
44+
Class<?> springBootApplicationClass) {
45+
var delegate = new DefaultCacheAwareContextLoaderDelegate();
46+
var bootstrapContext = new DefaultBootstrapContext(springBootApplicationClass, delegate);
47+
48+
var bootstrapper = new SpringBootTestContextBootstrapper();
49+
bootstrapper.setBootstrapContext(bootstrapContext);
50+
51+
var configuration = bootstrapper.buildMergedContextConfiguration();
52+
53+
return AssertableApplicationContext.get(
54+
() -> {
55+
SpringBootContextLoader loader = new SpringBootContextLoader();
56+
try {
57+
return (ConfigurableApplicationContext) loader.loadContext(configuration);
58+
} catch (Exception e) {
59+
throw new RuntimeException(e);
60+
}
61+
});
62+
}
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2022- SDA SE Open Industry Solutions (https://www.sda.se)
3+
*
4+
* Use of this source code is governed by an MIT-style
5+
* license that can be found in the LICENSE file or at
6+
* https://opensource.org/licenses/MIT.
7+
*/
8+
package org.sdase.commons.spring.boot.web.security.validation;
9+
10+
import static org.assertj.core.api.Assertions.assertThat;
11+
12+
import org.junit.jupiter.api.Test;
13+
import org.sdase.commons.spring.boot.web.jackson.SdaObjectMapperConfiguration;
14+
import org.sdase.commons.spring.boot.web.security.exception.InsecureConfigurationException;
15+
import org.sdase.commons.spring.boot.web.security.test.ContextUtils;
16+
import org.springframework.boot.autoconfigure.SpringBootApplication;
17+
import org.springframework.boot.test.context.SpringBootTest;
18+
19+
class CustomObjectMapperAdviceTest {
20+
@Test
21+
void shouldPreventStartupIfTracingIsEnabled() {
22+
assertThat(ContextUtils.createTestContext(NoSdaObjectMapperApp.class))
23+
.hasFailed()
24+
.getFailure()
25+
.getRootCause()
26+
.isInstanceOf(InsecureConfigurationException.class)
27+
.hasMessage(
28+
"Missing sdaObjectMapperBuilder bean from org.sdase.commons.spring.boot.web.jackson. "
29+
+ "The Jackson2ObjectMapperBuilder component registers custom mappers.");
30+
}
31+
32+
@SpringBootApplication(exclude = SdaObjectMapperConfiguration.class)
33+
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
34+
public static class NoSdaObjectMapperApp {}
35+
}

0 commit comments

Comments
 (0)