Prevent serialization exception from Env actuator

When `EnvironmentEndpoint` is building a response to return to the
web infrastructure, it creates a data structure containing all
property values from all property sources. Prior to this commit, it
was possible for the response data structure to contain property
values that were not serializable to JSON by Jackson, which would
cause an exception to be thrown by the web infrastructure. This
commit ensures the data structure is serializable to JSON by
ensuring property values are primitives or Strings, and returning
a placeholder value if a property value is of any other type.

Fixes gh-23805
This commit is contained in:
Scott Frederick
2020-10-23 15:04:23 -05:00
parent 008ab4da25
commit 1a3f810cd8
2 changed files with 59 additions and 5 deletions

View File

@@ -16,6 +16,9 @@
package org.springframework.boot.actuate.env;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -39,6 +42,7 @@ import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.InputStreamSource;
import org.springframework.mock.env.MockPropertySource;
import static org.assertj.core.api.Assertions.assertThat;
@@ -54,6 +58,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Andy Wilkinson
* @author HaiTao Zhang
* @author Chris Bono
* @author Scott Frederick
*/
class EnvironmentEndpointTests {
@@ -194,15 +199,22 @@ class EnvironmentEndpointTests {
}
@Test
@SuppressWarnings("unchecked")
void propertyWithTypeOtherThanStringShouldNotFail() {
ConfigurableEnvironment environment = emptyEnvironment();
environment.getPropertySources()
.addFirst(singleKeyPropertySource("test", "foo", Collections.singletonMap("bar", "baz")));
EnvironmentDescriptor descriptor = new EnvironmentEndpoint(environment).environment(null);
Map<String, String> foo = (Map<String, String>) propertySources(descriptor).get("test").getProperties()
.get("foo").getValue();
assertThat(foo.get("bar")).isEqualTo("baz");
String value = (String) propertySources(descriptor).get("test").getProperties().get("foo").getValue();
assertThat(value).isEqualTo("Complex property type java.util.Collections$SingletonMap");
}
@Test
void propertyWithCharSequenceTypeIsConvertedToString() throws Exception {
ConfigurableEnvironment environment = emptyEnvironment();
environment.getPropertySources().addFirst(singleKeyPropertySource("test", "foo", new CharSequenceProperty()));
EnvironmentDescriptor descriptor = new EnvironmentEndpoint(environment).environment(null);
String value = (String) propertySources(descriptor).get("test").getProperties().get("foo").getValue();
assertThat(value).isEqualTo("test value");
}
@Test
@@ -360,4 +372,35 @@ class EnvironmentEndpointTests {
}
public static class CharSequenceProperty implements CharSequence, InputStreamSource {
private final String value = "test value";
@Override
public int length() {
return this.value.length();
}
@Override
public char charAt(int index) {
return this.value.charAt(index);
}
@Override
public CharSequence subSequence(int start, int end) {
return this.value.subSequence(start, end);
}
@Override
public String toString() {
return this.value;
}
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(this.value.getBytes());
}
}
}